parquet_clamp_to_affinity Function

public function parquet_clamp_to_affinity(n, area) result(m)

Uses

Lowers n to the number of processors this process's CPU affinity actually allows, and says so once per process when that clamp bites.

This is the ONE place the affinity clamp lives. Four resolvers reach it -- the sort's resolve_thread_count, pf_sort_threads and the bulk random draws through parquet_auto_thread_count, prefetch_thread_count (src/parquet_tables_read.f90) and parquet_string_threads (src/parquet_strings.f90). It is here rather than in any of them because three of the four live in tiers that cannot see each other, and a second copy of a rule like this is how two subsystems come to disagree about the same machine.

omp_get_max_threads is what the environment ASKED for; omp_get_num_procs is what the affinity mask allows. They differ whenever the initial thread was bound before main -- OMP_PROC_BIND with OMP_PLACES=cores binds it to one core, after which a team asked for 64 lands on however few processors the mask holds and time-shares them, which is slower than not threading at all. Clamping is therefore a performance decision, never a correctness one: the answer is identical at every thread count.

The warning is the whole point, because nothing else reveals this. No call fails, no result changes, and the only symptom is wall-clock. area names the subsystem that noticed so the reader knows which work was affected; the fix it recommends is the same either way.

Every resolver passes an area; there is no silent variant, deliberately. The obvious refinement -- let a QUERY such as pf_sort_threads() or parquet_string_threads() clamp without printing, and leave the warning to the operation that follows -- was tried and is wrong here, because pf_sort_threads() is exactly how the sort's own AUTOMATIC path resolves its count. A silent query therefore hands resolve_thread_count an already-clamped number, whose own clamp is then a no-op, and the warning becomes unreachable for the one job it exists to catch: OMP_NUM_THREADS=64 under OMP_PLACES=cores, no explicit threads=, and nothing said. Warning from the shared clamp covers both paths and every subsystem at once.

Arguments

Type IntentOptional Attributes Name
integer, intent(in) :: n

threads resolved before the clamp.

character(len=*), intent(in) :: area

subsystem this count belongs to, for the message.

Return Value integer


Source Code

    integer function parquet_clamp_to_affinity(n, area) result(m)
#ifdef _OPENMP
        use omp_lib, only: omp_get_num_procs
#endif
        integer, intent(in) :: n             !! threads resolved before the clamp.
        character(len=*), intent(in) :: area !! subsystem this count belongs to, for the message.
        character(len=32) :: got, asked
        integer(int64) :: seen !! the claim count this call observed; 0 means this call won it.
        integer :: procs       !! processors the mask allows; 0 when this build has no OpenMP.
        !
        m = max(1, n)
        procs = 0
#ifdef _OPENMP
        procs = omp_get_num_procs()
#endif
        if (dbg_affinity_procs > 0) procs = dbg_affinity_procs
        if (procs < 1) return
        if (m <= procs) return
        m = max(1, procs)
        ! Fast path first: in a process whose affinity really is clamped every resolver reaches here
        ! on every operation, so this must cost one load once the line has been said. `atomic read`
        ! is a plain load on every real target.
        !$omp atomic read
        seen = affinity_clamp_claims
        if (seen /= 0_int64) return
        ! Suppression is checked BEFORE the claim, deliberately: a run that silenced its output must
        ! not consume the one warning, so a later call with output enabled still receives it.
        !
        ! **`parquet_output_is_suppressed` rather than `parquet_emit_warning`'s own threshold**, so
        ! this one message goes quiet at verbosity `"silent"` while an ordinary warning survives to
        ! `"errors_only"`. That is deliberate and is what `doc/pages/operating/performance.md`
        ! documents: this is advice about the caller's ENVIRONMENT, not a report of anything the
        ! library found wrong, so it belongs with the output a `"silent"` run is asking to be spared.
        if (parquet_output_is_suppressed()) return
        !$omp atomic capture
        seen = affinity_clamp_claims
        affinity_clamp_claims = affinity_clamp_claims + 1_int64
        !$omp end atomic
        if (seen /= 0_int64) return
        write (got, '(i0)') m
        ! **The count REQUESTED, not `omp_get_max_threads()`.** The clamp fires for an explicit
        ! `threads=` too, and reporting the environment's ICV there names a number the caller never
        ! asked for -- `threads=100` on a machine offering 8 used to print "although 8 were
        ! requested". `n` is what the resolver actually settled on before this clamp touched it, so
        ! it is right on both paths: the caller's own number when they named one, and the
        ! environment's (already narrowed by any cap) when they did not.
        write (asked, '(i0)') n
        call parquet_emit_warning(trim(area) // " is limited to " // trim(got) // &
            " thread(s) because this process's CPU affinity allows no more, although " // &
            trim(asked) // " were requested. This usually means OMP_PROC_BIND is set with " // &
            "OMP_PLACES=cores; OMP_PLACES=sockets avoids it.")
    end function parquet_clamp_to_affinity