GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — Stop `gb.ss` import from turning the SuiteSparse JIT on by eriknw · Pull Request #630 · python-graphblas/python-graphblas

Conversation

@eriknw

@eriknw eriknw commented Aug 26, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member

Stacked on #589.

Importing graphblas.ss raised jit_c_control from SuiteSparse's default 'run' to 'on'. That submodule is reached by attribute access, so print(gb.ss.about["library_version"]), the line a user writes for a bug report, silently changed the numerical machinery of every later call in the process.

'on' is also what makes SuiteSparse load kernels from its on-disk cache, and that cache is keyed on the version triple alone. Two builds sharing a triple but differing in a JIT-visible header therefore share kernels: locally, kernels compiled by a 10.4.0 dated "July 31, 2026" were loaded by a 10.4.0 dated "Aug 7, 2026", whose GB_Matrix_content.h had moved a field. A four-element complement-masked assign returned GrB_OUT_OF_MEMORY, and a full suite run showed 25 unrelated-looking failures. Reported upstream with a standalone C reproducer.

The import now repairs the conda-baked compiler path and nothing else. The bump to 'on' moves to _enable_jit_for_udt, called when a UDT registers a type or arms an op with C source: acts that plainly involve compiling C, so enabling the compiler is not a surprise. An explicit 'off' or 'pause' is honored; only SuiteSparse's own non-compiling defaults are raised. fix_jit_config() is unchanged in intent, since enabling compilation is what its caller asked for; it now also re-arms the cached probe answer, so a user-driven repair gets re-probed at the next UDT op instead of trusting a pre-repair verdict.

Two details the hooks make necessary:

_probe_jit registers a UDT to verify the compiler, and registering a UDT now asks to enable the JIT, which would probe again. The inner probe registered the probe type and the outer one then died redeclaring it through cffi, so a working environment got False from the first fix_jit_config() and True from every call after. _probing_jit makes the re-entrant request answer yes without starting a second probe.

_maybe_warn_no_jit blamed jit_c_control for a UDT that is not expressible as a C struct. Codegen fails before anything arms a kernel, so the control is still 'run' and the old message sent the reader to a setting that could not help. It now only blames the control once something has actually tried to enable the JIT.

A second commit updates the UDT user guide, whose JIT section still said the import raises the control.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@eriknw eriknw mentioned this pull request Aug 26, 2026
Refresh the sparse pool, fix CI on SuiteSparse 8, and fix ss.register_new after #630 #631
Merged
@eriknw
eriknw force-pushed the 40-ss-import-jit-control branch 4 times, most recently from e3f45f4 to 6ed0f67 Compare August 27, 2026 16:46
@eriknw
eriknw force-pushed the 40-ss-import-jit-control branch from 6ed0f67 to f9d2799 Compare September 18, 2026 03:25
@eriknw
eriknw force-pushed the 40-ss-import-jit-control branch from f9d2799 to 854fce4 Compare September 18, 2026 03:46
@eriknw
eriknw changed the base branch from 39-repr-chop-complex-overflow to 09-array-udt-udf-views September 23, 2026 18:18
@eriknw
eriknw force-pushed the 40-ss-import-jit-control branch from 854fce4 to 2082505 Compare September 23, 2026 18:19
@eriknw
eriknw added this pull request to stack #634 September 23, 2026 18:19
@eriknw eriknw mentioned this pull request Sep 23, 2026
Add a test fixture that pins the UDT operator execution path #592
Closed
Base automatically changed from 09-array-udt-udf-views to main September 24, 2026 20:05
@eriknw
eriknw force-pushed the 40-ss-import-jit-control branch from 2082505 to 4c75928 Compare September 24, 2026 20:06
eriknw added 2 commits September 24, 2026 15:06
@eriknw
Stop gb.ss import from turning the SuiteSparse JIT on
89a23d3
Importing ``graphblas.ss`` raised ``jit_c_control`` from SuiteSparse's
default ``'run'`` to ``'on'``. That submodule is reached by attribute
access, so ``print(gb.ss.about["library_version"])``, the line a user writes
for a bug report, silently changed the numerical machinery of every later
call in the process.

``'on'`` is also what makes SuiteSparse load kernels from its on-disk cache,
and that cache is keyed on the version triple alone. Two builds sharing a
triple but differing in a JIT-visible header therefore share kernels:
locally, kernels compiled by a 10.4.0 dated "July 31, 2026" were loaded by a
10.4.0 dated "Aug 7, 2026", whose GB_Matrix_content.h had moved a field. A
four-element complement-masked assign returned GrB_OUT_OF_MEMORY, and a full
suite run showed 25 unrelated-looking failures. Reported upstream with a
standalone C reproducer.

The import now repairs the conda-baked compiler path and nothing else. The
bump to ``'on'`` moves to ``_enable_jit_for_udt``, called when a UDT
registers a type or arms an op with C source: acts that plainly involve
compiling C, so enabling the compiler is not a surprise. An explicit
``'off'`` or ``'pause'`` is honored; only SuiteSparse's own non-compiling
defaults are raised. ``fix_jit_config()`` is unchanged, since enabling
compilation is what its caller asked for.

Two details the hooks make necessary:

``_probe_jit`` registers a UDT to verify the compiler, and registering a UDT
now asks to enable the JIT, which would probe again. The inner probe
registered the probe type and the outer one then died redeclaring it through
cffi, so a working environment got ``False`` from the first
``fix_jit_config()`` and ``True`` from every call after. ``_probing_jit``
makes the re-entrant request answer yes without starting a second probe.

``_maybe_warn_no_jit`` blamed ``jit_c_control`` for a UDT that is not
expressible as a C struct, and now names the one cause its call site can
produce. It used to re-derive the cause from the live config, which is not
the state codegen read. Codegen reads the dtype; it never looks at the
control or the compiler path, so neither can be why it declined. On a host
whose JIT probe fails the control reads back ``'run'`` and
``_enable_jit_for_udt`` has cached its ``False``, which is enough for the old
message to blame the control again. That is the ubuntu-latest and
macos-latest conda runners, so it was a failing test as well as a message
that sent the reader to a setting which cannot fix it. Dropping the
environment branches costs no diagnostic: a UDT that does have a C form is
armed and never reaches this warning, so an unusable compiler was already
silent here.

``_enable_jit_for_udt`` caches whether this process can compile, which is
settled once, but it was also returning before re-raising the control, which
is separate state that anything may have moved since. Restoring a saved
``'run'`` after an earlier op had enabled the JIT therefore disabled
compilation for the rest of the process, silently: the next op is armed with
C source SuiteSparse will never build, and nothing warns, because codegen
succeeded. The cached path now raises ``'run'`` and ``'load'`` the same way
the uncached one does, and still honors an explicit ``'off'`` or ``'pause'``.
Before this commit the question could not arise: the import forced ``'on'``,
so a saved value was always ``'on'`` too. Its test drives a stand-in config so
the assertion holds on every host, and pins the live round trip separately: a
library built without the JIT clamps every write down to ``'run'``, and would
never read back the ``'on'`` the test is about.

One prose correction while in the file: ``_probe_jit`` claimed SuiteSparse
flips the control to ``'load'`` after a failed compile, as though that were the
only outcome. ``git grep -n "GB_jit_control = "`` in SuiteSparse finds eight
places that lower it. ``GB_jitifyer_set_control`` is the one that comes first: it
takes the minimum of the requested value and ``'run'`` on a library built without
the JIT, so ``'on'`` cannot be set there however good the compiler is. On the
load path, a compile failure gives ``'load'``, a load failure ``'run'``, and a
failed hash insert ``'pause'``. The docstring says the value is not ours to
predict, so the contract here is the bool.

The new subprocess tests read the control through the C API rather than
``gb.ss.config``, because reaching for ``gb.ss`` is the thing under test.
@eriknw
Say where the JIT is enabled now in the UDT user guide
c5a45a8
The "JIT compiler auto-fix" section still said the import bumps
jit_c_control from 'run' to 'on'. After the previous commit the import
repairs the compiler path and nothing else, and the control is raised the
first time a UDT registers a type or arms an operator with C source. Also
note that fix_jit_config() sets 'on' as well as repairing and probing.
@eriknw
eriknw force-pushed the 40-ss-import-jit-control branch from 4c75928 to c5a45a8 Compare September 24, 2026 20:07
@eriknw
Keep an explicit JIT opt-out from switching the JIT off for good
644b332
_enable_jit_for_udt cached False when the first UDT op found
jit_c_control at 'off' or 'pause', though an opt-out says nothing about
whether the process can compile. A saved 'run' restored afterwards was
then never raised, so every later UDT op ran without the JIT, silently.
An opt-out is now honored without being cached, and the function returns
whether a kernel armed now will be compiled.

'load' is now honored the same way instead of being raised. SuiteSparse
sets it after a compile fails so that the failure is not repeated, and
raising it at the next arming ran the failing compiler again at every
later UDT op, raising JitError each time on SuiteSparse 9.4 and later. It
also left 'off' as the only way to stop a host with cached kernels and no
compiler from trying to compile.

Also say in fix_jit_config's docstring that it sets 'on', and which
SuiteSparse versions raise the JitError its probe absorbs. In the user
guide, say that NoJITWarning covers only a UDT with no C form and which
calls raise the control. Share one helper between the two subprocess
tests, and word as hypotheticals the test docstrings for failures that
never reached main.
@eriknw

eriknw commented Sep 26, 2026

Copy link
Copy Markdown
Member Author

Merging.

I don't like some of the language such as "arming" (I wouldn't say this), but I'll do a bulk comments/docs cleanup later.

The JITs are pretty complicated, but I think this helps make them "just work" for users at the time of need.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@eriknw
eriknw merged commit f33f8c6 into main Sep 26, 2026
16 checks passed

Uh oh!

There was an error while loading. Please reload this page.

@eriknw
eriknw deleted the 40-ss-import-jit-control branch September 26, 2026 05:23
eriknw added a commit that referenced this pull request Oct 1, 2026
@eriknw
Refresh the sparse pool, fix CI on SuiteSparse 8, and fix ss.register…
5aa3d4c
…_new after #630 (#631)

* Refresh the sparse pool and lift its stale Python 3.13 exclusion

The pools refresh below this commit left sparse behind: the pool stopped at
0.15 while 0.16 through 0.19.2 exist, and py3.13/3.14 hardcoded "NA" under a
comment saying sparse does not support them. That was true in the 0.15 era,
when numba could not run on those Pythons; it is not true now.

Constraints read off conda-forge metadata rather than assumed: every sparse
release here is noarch with numba >=0.49 and numpy >=1.17 and no numpy
ceiling, so only the Python floors matter. 0.16/0.17 declare python >=3.10
and 0.18/0.19 declare >=3.11. On py3.13/3.14 the pool starts at 0.16 because
0.14/0.15 predate those Pythons; on py3.11/3.12 the old entries stay.

No new pairing rules are needed: the workflow already skips sparse whenever
numba is skipped or NA, which covers sparse's only runtime dependency. The
validate() Python check moves from "any sparse on py3.13+" to the 0.16
floor, and the stress test passes 160000 combos.

Verified beyond metadata on this machine (py3.14, macOS arm64): sparse
0.16.0 and 0.19.2 pass the pydata-sparse io tests, and 0.18.0 passed the
full suite.

**Edit: rebased to include the changes on main, which largely supercedes this**

* Test and allow every sparse release on Python 3.13 and 3.14

The CI pools started sparse at 0.16 on py3.13 and 0.18 on py3.14, and the
io extra in pyproject.toml had the same floors. Each was the first sparse
release to come out after that Python, on the assumption that older ones
might not work there. They do. sparse is noarch, and every release from
0.14 on declares numba >=0.49, numpy >=1.17 and a Python floor of 3.11
or lower (>=3.8 for 0.14/0.15, >=3.10 for 0.16/0.17, >=3.11 for
0.18/0.19), with no ceiling on any of them. Each pin, as conda-forge
resolves it (0.14.0, 0.15.5, 0.16.0, 0.17.0, 0.18.0 and 0.19.2), solves
on both Pythons and passes the pydata-sparse tests with the oldest and
newest numpy/numba pair each Python draws: numpy 2.1 with numba 0.62 and
numpy 2.5 with numba 0.67 on py3.13, numpy 2.3 with numba 0.63 and numpy
2.5 with numba 0.67 on py3.14.

So every Python now draws from the same pool, 0.14 through 0.19 plus
latest, and the io extra requires sparse >=0.14 wherever numba installs.
No pairing rules are needed, since the workflow already drops sparse
whenever it skips numba. The stress test passes 160000 combinations, and
both version audits pass.

* Skip the failed-compile JIT test on SuiteSparse 8

test_failed_compile_is_not_retried_at_the_next_arming, from #630, arms
binary.plus on a UDT with C source for SuiteSparse to compile, then makes
that compile fail. An op is armed with C source only when the JIT string
setters exist, and those arrived in SuiteSparse 9, so on 8.x jit_c_source
is None and the test failed at its first assertion. It gets that far only
where the JIT probe passes. In CI that happens only on some macOS runners
that draw psg 8 (numpy 1.x); main's CI failed there after #630 merged.

The test now skips when _has_jit_set is false, like the other tests that
need an armed op. On 8.x the stand-in config tests still check that
_enable_jit_for_udt leaves 'load' alone. Locally on psg 9.3.1.0, the only
release in the CI pool that takes its pre-9.4 branch, it runs and passes.

* Enable the JIT when registering from C source, and say why it failed

gb.unary.ss.register_new and its binary, indexunary, select, and
indexbinary siblings create the op from a C definition and no function
pointer, so SuiteSparse has to compile the definition, or load it from its
cache, before the op exists. Under its default jit_c_control of 'run' it
does neither, and the registration fails with a bare NullPointer.

Before #630, importing graphblas.ss raised the control to 'on', and the
docstrings of these functions send users to gb.ss.config, so touching it
first was enough; a process that never touched gb.ss already failed.
#630 stopped the import from changing the control and moved the raise to
_enable_jit_for_udt, which UDT op arming and gb.dtypes.ss.register_new
call but these registrars did not. Since then nothing raises it for them,
and each fails in a fresh process, on SuiteSparse 8.0.2 and 10.5 alike.

The four op registrars and gb.dtypes.ss.register_new now create through
one context manager, _compiling_c_source, which raises 'run' first and
still honors an explicit 'off', 'pause', or 'load'. When creation fails
anyway, SuiteSparse's error (NullPointer, InvalidValue, or from 9.4
JitError) has no message, so the same exception is raised again with the
reason, read off jit_c_control before and after: the JIT is off or
paused; it was left at 'load' and the kernel is not cached; this compile
failed and SuiteSparse set 'load'; the compiler probe failed in this very
call; or the control is still 'run' because no compiler was found or the
library has no JIT.

gb.dtypes.ss.register_new also accepted a name of GxB_MAX_NAME_LEN
characters. That limit counts the NUL, so SuiteSparse cut the name by one,
it no longer matched the typedef, and the compile failed. It is now
rejected up front.

The user guide's list of what raises the control includes the op
registrars. New tests register one op of each kind in a fresh process,
resetting the control to 'run' before each so none can rely on another,
check each reason's message, and check the name limit.

* Fix small errors in docstrings, messages, and docs

Messages: Matrix, Vector, and Scalar __sizeof__ printed a literal
{backend} (no f prefix); the Matrix diagonal size-mismatch message ran two
sentences together, and the nrows/ncols error two words; the compression
level error named only "lz4hc" where "zstd" also takes a level; the UDT
repr size warning ended its first sentence twice.

Docstrings: isequal documented check_dtype as check_dtypes; from_csc
documented col_indices and said ncols is computed where it takes
row_indices and computes nrows; from_dicts documented d for nested_dicts;
Vector.diag documented a dtype it does not take; Vector.from_scalar
documented nrows and ncols for size; serialize gave the zstd default level
as 19 and a 1-9 range for both methods (zstd defaults to 1 and takes
1-19), and the descriptor docs gave the lz4hc default as 1 (it is 9);
operator registration pointed at a graphblas.ops namespace, which is
graphblas.op; several parameter lines numpydoc cannot parse ("name: type",
"name  :type"); two choice lists missing a closing quote; .mtz.gz for
.mtx.gz in mmread; "`first` any `any`" in a comment.

Docs: "the the" in the operators guide, the README converting the scipy
matrix back instead of the GraphBLAS one, and two misspellings in a CSS
comment.
eriknw added a commit that referenced this pull request Nov 9, 2026
@eriknw
Set the SuiteSparse JIT control from config, and JIT-compile lifted U…
0f5dc9b
…DT ops on mixed operand types (#652)

* ss.fix_jit_config: find a conda compiler on Windows (Library/bin, .exe names)

The conda repair looked for {baked-in name, cc, clang, gcc} in
$CONDA_PREFIX/bin only. Conda on Windows installs compilers in
%CONDA_PREFIX%\Library\bin, and their names end in .exe, so the repair
found nothing there and left SuiteSparse's build-time compiler, which a
user's machine does not have. It now looks in both places, with and
without .exe, and takes a Windows path's basename with either slash.
Tested with a fake prefix (no Windows machine at hand).

* JIT C definitions for lifted ops on a scalar and a UDT, or on two UDT types

A lifted binary op (plus, minus, times, truediv, floordiv, min, max)
whose operands are a scalar and an array or record UDT, in either
order, or two UDTs of one layout with other element types, ran only
through its Numba cfunc: the C definition was set for same-type pairs
alone, with one C type for x, y and z. So a semiring such as
plus_times[FP64, FP64[R]] ran SuiteSparse's generic kernel with a
function call per multiply.

Such an op now gets a C definition with a type for each of z, x and y
(udt_codegen._make_jit_c_mixed_definition), named op_N_x_y with N the
length of x's C name, so that no two ops share a name (SuiteSparse finds
a compiled kernel in memory by its op's name): a scalar operand is read
as (*x) for every element or leaf, and each operand is converted in C to
the type the op computes in, as the cfunc converts it
(udt._udt_op_types). Ops with no such form keep the cfunc: an array
operand that broadcasts, an element not computed in the result's type
(integer truediv), a record with an array leaf, and eq/ne on a mixed
pair.

The whole mxv with plus_times[FP64, FP64[R]] now compiles as one JIT
kernel (burble: dot2, jit). test_udt_scalar_broadcast_skips_jit becomes
test_udt_scalar_broadcast_jit_routing: mixed arithmetic now JITs, mixed
eq/ne still does not.

* Test the mixed semiring's JIT kernel by burble's generic, absent and present

The burble names a JIT kernel only when it compiles or loads it, so a
kernel loaded earlier in the process prints no '(jit: ...)'. A UDT
product has no kernel but the JIT one and the generic one, which burble
names, so the test checks that 'generic' is absent with the JIT on and
present with it off.

* Vary the mixed JIT C name test's inputs, so its kernel runs on every SuiteSparse

test_udt_mixed_jit_c_name_is_its_own built its vectors from constant
values. SuiteSparse before 10.5 makes a vector built from equal values
iso, and an ewise_mult of iso vectors gives an iso result computed
without the JIT kernel the test is about, so on those versions it
checked nothing. On them its to_dense also raised "TypeError:
unsupported operand type(s) for +: 'int' and 'tuple'" (wave 2 CI, 17
jobs, e.g. 122633175811 and 122633175893), the iso array-UDT export
bug fixed in #591. The values now differ by index.

* Set the SuiteSparse JIT control at init, from a new config key

Behavior change. Before: python-graphblas left jit_c_control where the
SuiteSparse:GraphBLAS build started it ("run" in the conda-forge and PyPI
builds, "on" in upstream's own), and UDT work raised "run" to "on" when it
needed C compiled, whoever had set it. Turning the JIT on for everything
meant setting gb.ss.config["jit_c_control"] after init. After: when the
"suitesparse" backend initializes, it sets jit_c_control from the new config
key graphblas.config["jit_c_control"]. The key is unset by default, which sets
"run": nothing changes for the conda-forge and PyPI builds, and a build that
starts at "on" now starts at "run" too, so every build has one documented
default. GRAPHBLAS_JIT_C_CONTROL, gb.config.set before init, or a YAML file in
~/.config/graphblas/ set it; the values are "on", "load", "run", "pause" and
"off" (YAML's unquoted on/off arrive as bools and are taken as such), and any
other value raises ValueError before anything is initialized.

UDT work still raises the default "run" to "on" when it needs C compiled, but
now keeps a "run" the user set, as it already kept "off", "pause" and "load".
A "run" set before init counts: GRAPHBLAS_JIT_C_CONTROL, gb.config.set or a
YAML file, one key that donfig merges, so they could not be told apart and need
not be; the environment variable is also the form that reaches every worker of
a pool or cluster, which is what a "run" is for (GraphBLAS #449, below). A
later gb.ss.config["jit_c_control"] = "run" counts when it changes the
configured control (a "run" over a configured "on", say), and keeps a
configured "run"; with nothing configured it is the default "run" again. The
comparison is with the configured control, not the live one, which UDT work
may have raised to "on": code that saves the default "run", sets something
else and restores it would otherwise turn the default into a chosen "run" by
accident. So, at runtime, "load" or "off" keep UDT work from switching to "on",
as does GRAPHBLAS_JIT_C_CONTROL=run set before starting.
gb.ss.config["jit_c_control"] = None is the default whatever is configured.
Under a "run" that was set, UDT ops run their Numba functions (no compiled
kernel, typically 2-3x slower for elementwise ops), and types and ops
registered from C source (dtypes.ss.register_new and the op modules'
ss.register_new) fail with a message that says the control was set to "run"
and how to opt in: gb.ss.config["jit_c_control"] = "on", or None for the
default.

"on" makes SuiteSparse compile kernels it has no precompiled one for:
Matrix.reduce_rowwise with lor on BOOL or plus on UINT64 measured 2.7-8x faster
than the generic kernel. It stays opt-in because from SuiteSparse 9.4 on a
failed compile returns GxB_JIT_ERROR, and processes that share one cache
directory race (GraphBLAS #449; 10.1.1 dropped the file locks): on 10.5.2, 127
of 280 concurrent processes compiling the same new kernels into one cache got
errors, invalidated or cleared outputs, wrong values or hangs, where 8.2.1,
which locked the cache, had none.

When "on" is asked for, it is set only where it works (SuiteSparse 7 has no
JIT, and nothing is set there); elsewhere init sets "run", with no error or
warning: a library built without the JIT (psg's wheels clamp writes to "run"),
no compiler on disk after the conda repair, a cache directory it cannot write
to (a probe cached there still loads, and the first new kernel would then fail
to compile), or a compiler that fails a probe. Without the probe a broken
compiler would fail the first user operation that needs a new kernel (a slice,
say) with a bare JitError. The probe compiles a trivial type through
GxB_Type_new; the kernel is cached, so after the first time per cache directory
a probe is a load, about 2 ms against 0.3 s. The type is named after a hash of
the compiler name, flags and libraries, so another toolchain sharing the cache
gets a probe of its own. A failed probe sets "run", not the "load" SuiteSparse
leaves, since "load" still reads kernels from the cache. UDT work makes the
same writability check before it raises the default "run" to "on": without it
(as before this change), a read-only cache that already held the probe let the
raise through, and the first new UDT kernel failed with a bare JitError; now
the UDT op runs its Numba function and the control stays "run".

_probe_jit now goes through the C API for fix_jit_config and UDT work too, so
it registers no dtypes.ss._jit_probe and needs no re-entrancy guard. Init can
run in the middle of importing a core module, so it reads and writes these
settings through the C API (_CConfig) instead of gb.ss.config, and jit_config
imports ffi and lib inside the functions that use them: init imports it, so
importing it first would otherwise start init halfway through its own import.
Importing gb.ss still changes nothing (#630).

* Docs: the SuiteSparse JIT control, how to opt in, and when "on" is unsafe

A section in the initialization page of the user guide, about a screen long:
what "on" buys (the 2.7-8x reduce numbers), that the default is "run" and that
UDT work raises it but leaves a control the user set, how to opt in
(GRAPHBLAS_JIT_C_CONTROL, gb.config, gb.ss.config) and the values, that unset
is the default and a "run" set later in gb.ss.config is then that default
again (at runtime "load" or "off", or GRAPHBLAS_JIT_C_CONTROL=run before
starting, keep UDT work from switching to "on"), where "on" falls back to "run"
with no error, and the costs of "on". The first is plain: processes running at
the same time must not share a cache directory on SuiteSparse 10.1.1 and later,
which no longer lock the cache (GraphBLAS #449: JitError, invalid or cleared
outputs, wrong values returned with success, hangs). The simple
remedy for multiprocessing, dask or pytest-xdist is GRAPHBLAS_JIT_C_CONTROL=run
in the workers' environment, which holds for UDT work too; it costs UDT
operators their compiled kernels (they run their Numba functions, typically
2-3x slower elementwise) and makes C-source registration fail. A cache per
process keeps the kernels. The default will change once SuiteSparse publishes
kernels atomically. Then the first-use compile latency, the cache keyed only
on the version, and what to do on trouble: set GRAPHBLAS_JIT_C_CONTROL=run,
delete the cache, report it.

The UDT page says the control starts at the config value, "run" by default,
that UDT work and C-source registration raise that default but keep a control
the user set (a "run" when set before init), and what a "run" that was set
costs there, and links to the section; graphblas.yaml, jit_config's module
docstring and gb.ss.config's jit_c_control entry point there, and the entry
says what None does.

* Exclude the mixed op codegen's array leaf check from coverage

A record with an array leaf has no C typedef, so the mixed op's C definition
returns before it looks at the leaves.

* test_global_config: None sets jit_c_control to the default "run"

The test expected every key outside GlobalConfig._defaults to refuse None,
but "Set the SuiteSparse JIT control at init, from a new config key" gave
jit_c_control a setter in which None is the default "run" whatever
graphblas.config has, and not a "run" the user chose (UDT work may still
raise it to "on"). Check both, then put the control back as the test found
it, and restore the user-set flag, which the round trip earlier in the test
can change too.

* Skip the unusable-compiler probe check where SuiteSparse compiles with cmake

test_set_jit_c_control_at_init points jit_c_compiler_name at a directory
and expects the probe to fail. SuiteSparse built with MSVC always compiles
through cmake, and cmake's Visual Studio generator chooses the compiler
itself, so on Windows the probe succeeds and the control stays 'on'.
Every Windows job failed on it.

* Say that the JIT stays at "run" on Windows, where SuiteSparse compiles with cmake

SuiteSparse built with MSVC, as conda-forge builds it, compiles JIT kernels
only through cmake. python-graphblas checks the configured C compiler path
before it sets "on", and on conda-forge Windows that is the build machine's
cl.exe, which no user has, so a configured "on" becomes "run" there. The
user guide's JIT section and the jit_use_cmake entry of gb.ss.config say so.

* Keep a UDT op's JIT C name within SuiteSparse's 127 characters

SuiteSparse refuses a JIT C name of GxB_MAX_NAME_LEN (128) characters or
more, but the definition was set anyway and taken under the empty name.
A mixed op on two UDTs whose names add up to about 118 characters then
compiled a kernel that calls no function: every output value was left
uninitialized, with no error. A single-type op on a UDT named with 122
or more characters (on main too) failed with an empty JitError.

Shorten a long mixed-op name to a digest (a length of 0 cannot come from
the readable form, so the name stays the pair's own), and set the
definition only when SuiteSparse took the name; otherwise the op keeps
its cfunc. Found by review (r652f).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

No reviews

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

1 participant

@eriknw
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.