The settings an Arrow-free module owns: state, getter and setter.
The rule, in one sentence: a knob lives here when the Arrow-free module that reads it must be
able to re-export it. parquet_settings keeps everything else, keeps the validation vocabulary
it alone uses, and keeps the C++ mirror. A future Arrow-free module applies that sentence to its
own knobs without revisiting anyone else's, which is what makes the arrangement extensible rather
than a list someone has to maintain.
Why the SETTER lives here too, which is the part that is easy to get wrong. A user who
imports one module to get one capability must be able to configure that capability from the same
import -- use parquet_sorting has to offer the sorting knobs, or the only way to change them is
use parquet_settings, which imports parquet_bindings and drags the whole Arrow stack back in.
Re-exporting the getter alone would leave a user able to read a setting and not change it. Four
of these setters used to mirror their value to C++ immediately, which is exactly what pinned them
to parquet_settings; that push now happens at the point of USE instead
(parquet_push_settings_to_cpp, called when a reader or writer is opened, and by the C++ sort
engine's own dispatch), so no setter anywhere reaches C++ and every one was free to move.
This module began for one reason: to keep parquet_strings linkable on its own.
parquet_strings is documented as usable without the Arrow/Parquet C++ stack -- a project that
wants compact string storage and nothing else can depend on it alone. It needs two answers from
the library's settings, though: whether solicited output is suppressed (verbosity), and the
per-column thread cap. Taking those from parquet_settings directly cost it that independence,
because parquet_settings imports parquet_bindings in order to mirror the C++-side knobs -- so
linking a program whose only import was use parquet_strings pulled in the whole of
parquet_wrapper.cpp and, with it, Arrow. Nothing failed at compile time; the link failed with
the entire C++ surface undefined, which reads as a build misconfiguration rather than a
dependency defect.
So the STATE lives here, in a leaf module that imports nothing but iso_fortran_env, and the
public API over it stays in parquet_settings, which re-exports the two readers below.
This is a split, not a mirror, and the difference is the whole point. There is one copy of
each value: parquet_settings' setters write these variables directly. A pushed second copy in
parquet_strings would have been the other way to break the dependency and was rejected -- two
writers for one value is exactly what CLAUDE.md's "Do not add a second way to set the same
thing" forbids, and a missed push site would leave the two disagreeing with nothing to report it.
It also holds the library's one copy of the automatic THREAD-COUNT rule, for the same
reason and by the same argument. parquet_sorting and parquet_random both have to answer "how
many threads should this use when the caller said nothing", CLAUDE.md's auto-threading note names
a further copy of that rule as the mistake to avoid, and parquet_random is a pure-Fortran
counter-based generator that must not acquire a C++ dependency to ask it. parquet_sorting
reaches parquet_bindings, so it cannot be the home either. This module can be, and the
behaviour is unchanged: pf_sort_threads still reads cfg_sort_threads, still in one place, and
now delegates the OpenMP half here.
Rules for anything added here. A knob belongs in this module only if an Arrow-free module
reads it and therefore has to re-export it; everything else stays in parquet_settings, which
remains where a reader looks for the settings API and where parquet_print_settings,
parquet_reset_settings and parquet_settings_from_env live. The same test governs a procedure:
it belongs here only if it is a rule two such modules must share. The scope is narrow on
purpose -- moving a knob no Arrow-free module reads buys nothing and costs a re-export to keep
in step. Whatever is added must keep this module a leaf -- it may import intrinsic modules and
omp_lib (under #ifdef _OPENMP, as
parquet_strings already does) and nothing else, ever.
check_parquet_strings_stays_leaf (tools/check_source_conventions.py) enforces this by walking
the use graph, because the failure it guards is invisible until someone tries the standalone
build.
| Type | Visibility | Attributes | Name | Initial | |||
|---|---|---|---|---|---|---|---|
| integer, | public, | parameter | :: | verb_normal | = | 0 |
Verbosity levels, ordered so that a |
| integer, | public, | parameter | :: | verb_silent | = | 1 |
informational and solicited output goes quiet. |
| integer, | public, | parameter | :: | verb_errors_only | = | 2 |
warnings go quiet too; only errors survive. |
| character(len=11), | public, | parameter | :: | verbosity_tokens(3) | = | [character(len=11)::"normal", "silent", "errors_only"] |
The accepted vocabulary of the two output knobs, in one place each.
|
| character(len=6), | public, | parameter | :: | stream_tokens(2) | = | [character(len=6)::"stdout", "stderr"] | |
| integer, | public, | parameter | :: | stream_stdout | = | 0 |
Where the library's own messages go. |
| integer, | public, | parameter | :: | stream_stderr | = | 1 | |
| integer, | public, | save | :: | cfg_verbosity | = | verb_normal |
How much the library prints. Written by |
| integer, | public, | save | :: | cfg_string_threads | = | 0 |
Cap on the threads one |
| integer, | public, | save | :: | cfg_random_threads | = | 0 |
Cap on the threads one bulk |
| integer(kind=int64), | public, | save | :: | cfg_random_parallel_min_elements | = | 1000_int64 |
Fewest elements a thread must be given before a bulk permutation opens a team at all. A work floor, not a chunk size, and it exists because threading a small permutation is
monotonically harmful rather than merely useless: machine B measured |
| integer, | public, | save | :: | cfg_sort_threads | = | 0 |
Default thread count for every sort that does not name one. |
| integer, | public, | save | :: | cfg_message_stream | = | stream_stdout |
Which stream the library's own messages go to. Read only by the emit channels below. |
| logical, | public, | save | :: | cfg_sort_counting_path | = | .true. |
Whether the sort's integer counting fast path may be taken at all. Mirrored to C++ by
|
| logical, | public, | save | :: | cfg_sort_radix_path | = | .true. |
Whether the sort's single-key radix fast path may be taken at all. Deliberately NOT mirrored to C++, unlike its counting-path neighbour: the radix path exists
only in the Fortran engine, so there is nothing on the other side of the |
| integer(kind=int64), | public, | save | :: | cfg_sort_counting_bucket_limit | = | 0 |
Largest key value RANGE (not cardinality) the counting path will accept. |
Sets the largest key value range the sort's counting fast path will accept. See parquet_set_sort_counting_bucket_limit_int64 for the full description.
int32 form of parquet_set_sort_counting_bucket_limit_int64 -- see it for what the value means.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| integer(kind=int32), | intent(in) | :: | n |
bucket ceiling, or 0 for the built-in default; must be >= 0. |
Sets the largest key value RANGE for which the sort's counting fast path is taken. Pass 0
to restore the built-in 4194304 (2**22).
Range, not cardinality -- the bound is max(key) - min(key), so a thousand values spread
over a billion is far outside a limit that a million densely-packed values sit inside. This
distinction has already misled one test author here (feature_risks.md Risk-39).
The number IS the memory control: n buckets costs 8n bytes of counters, so the built-in
value caps the counting path at 32 MB. Raising it trades memory for speed on wide-ranged
integer keys; it does nothing at all while parquet_set_sort_counting_path is .false..
Available in both integer kinds; an int64 key's range can exceed int32.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| integer(kind=int64), | intent(in) | :: | n |
bucket ceiling, or 0 for the built-in default; must be >= 0. |
Sets the work floor, in elements per thread, below which a bulk permutation stays serial. See parquet_set_random_parallel_min_elements_int64 for the full description.
int32 form of parquet_set_random_parallel_min_elements_int64 -- see it for what it means.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| integer(kind=int32), | intent(in) | :: | n |
elements per thread, or 0 to disable; must be >= 0. |
Sets the fewest elements a thread must be given before a bulk permutation opens a team.
A work floor, not a chunk size. Threading a small permutation is not merely useless but
harmful -- the team costs more than the whole job -- so below threads * this elements the
bulk forms run serially however many threads are available. Machine B measured m = 10 going
from 0.0021 ms on one core to 0.0050 on sixteen, and 1->16 thread efficiency of 99 % at
10**6, 95 % at 10**4, 69 % at 1000 and 18 % at 100; the default of 1000 sits where that
curve turns.
Read per call, so it takes effect immediately. n takes integer(int32) or
integer(int64). 0 disables the floor entirely, which is how a test asks for a team on a
small array; it is not a useful production setting. An explicit threads= does not bypass
the floor -- the floor is about whether the work is worth splitting at all, which is a
property of the array rather than of the caller's intent.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| integer(kind=int64), | intent(in) | :: | n |
elements per thread, or 0 to disable; must be >= 0. |
Whether SOLICITED output -- something the caller explicitly asked to be printed, such as
%print_stat or parquet_string_column%print -- should stay quiet. Distinct from the emit
channels, which govern the library's own unsolicited messages.
The configured cap on threads inside one string-column bulk operation; 0 means automatic.
The configured cap on threads inside one bulk permutation/subset; 0 means automatic.
The configured work floor, in elements per thread, for a bulk permutation/subset.
The library's one copy of the automatic thread rule: how many threads an operation that
was given no explicit threads= should use right now, under a caller-supplied cap.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| integer, | intent(in) | :: | cap |
caller's domain cap; |
||
| character(len=*), | intent(in) | :: | area |
subsystem name, for the affinity-clamp warning |
Lowers n to the number of processors this process's CPU affinity actually allows, and says
so once per process when that clamp bites.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| integer, | intent(in) | :: | n |
threads resolved before the clamp. |
||
| character(len=*), | intent(in) | :: | area |
subsystem this count belongs to, for the message. |
Whether opening a thread team here would build the shape libgomp deadlocks on.
Reports the sort thread cap, or 0 if sorting is left automatic. This is the raw setting, not
the resolved count -- ask pf_sort_threads() for the number a sort would actually use here,
which additionally accounts for the OpenMP environment and for being inside a parallel region.
Reports whether the sort's integer counting fast path is allowed.
Reports whether the sort's single-key radix fast path is allowed.
Reports the counting path's bucket ceiling -- the EFFECTIVE value, so a program that never set
it is told 4194304 rather than the 0 that is stored.
Overrides the processor count parquet_clamp_to_affinity clamps to. Test-only;
<= 0 restores the real omp_get_num_procs().
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| integer, | intent(in) | :: | n |
processors to pretend the affinity mask allows; |
Clears the once-per-process claim on the affinity-clamp warning. Test-only.
Sets the default thread count for every sort that does not pass threads= explicitly --
pf_sort/pf_argsort and friends, a read-time parquet_open_reader(..., sort_by=), and
parquet_table%sort_by, which all share one engine and must share one default.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| integer, | intent(in) | :: | n |
thread cap, or 0 for automatic; must be >= 0. |
Sets the cap on how many threads one parquet_string_column bulk operation may use --
a reindex, gather, compaction or materialization of a single column's packed payload.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| integer, | intent(in) | :: | n |
thread cap, or 0 for automatic; must be >= 0. |
Sets the cap on how many threads one bulk pf_random_permutation/pf_random_subset call
may use internally.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| integer, | intent(in) | :: | n |
thread cap, or 0 for automatic; must be >= 0. |
Sets the row count below which a sort refuses to use threads at all, however many threads=
asks for. Pass 0 to restore the built-in 8192.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| logical, | intent(in) | :: | enabled |
.true. (the default) allows the fast path. |
Enables or disables the sort's single-key radix fast path.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| logical, | intent(in) | :: | enabled |
.true. (the default) allows the fast path. |
Sets how much the library prints. One of "normal" (everything, the factory default), "silent" (the library's own remarks and its explicitly-called print procedures go quiet; warnings and errors still appear) or "errors_only" (warnings go quiet too). Case-insensitive; anything else aborts.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| character(len=*), | intent(in) | :: | level |
"normal" | "silent" | "errors_only". |
Reports the current verbosity as the same token parquet_set_verbosity accepts.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| character(len=:), | intent(out), | allocatable | :: | level |
"normal" | "silent" | "errors_only". |
Sets which stream the library's own messages go to: "stdout" (the factory default) or "stderr". Case-insensitive; anything else aborts.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| character(len=*), | intent(in) | :: | stream |
"stdout" | "stderr". |
Reports the current message stream as the same token parquet_set_message_stream accepts.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| character(len=:), | intent(out), | allocatable | :: | stream |
"stdout" | "stderr". |
Emits one informational remark -- something worth mentioning that is not a warning about the data. Suppressed from "silent" downward.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| character(len=*), | intent(in) | :: | text |
the message, with no prefix. |
Emits one warning about the data or the schema. Suppressed only at "errors_only".
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| character(len=*), | intent(in) | :: | text |
the message, without the "WARNING: " prefix. |
Emits one line of context belonging to an error that is about to abort.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| character(len=*), | intent(in) | :: | text |
the context line, printed verbatim. |
Renders a token vocabulary as "a, b, c", for an error message.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| character(len=*), | intent(in) | :: | tokens(:) |
the accepted vocabulary. |
||
| character(len=:), | intent(out), | allocatable | :: | out |
comma-separated, in array order. |
Lowercases ASCII letters. A local copy rather than parquet_to_lower, because that one lives in parquet_core, which uses THIS module -- importing it back would be a circular dependency.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| character(len=*), | intent(in) | :: | text |
input text. |
||
| character(len=:), | intent(out), | allocatable | :: | out |
text with every ASCII A-Z lowercased. |