Repository navigation
Native: later result batches carry vectors in the float32 tail (arcadedb_n_next_bin) - #76
Merged
Merged
Conversation
Rows with vectors of 0, 3, 8 and 32 floats, float32 extremes (1e-45, 3.4028235e38, +/-inf, -0.0) and rows with no vector, read in one list (256 rows in the first reply, 2048 per later batch) and through the row-by-row cursor, compared as the 8 bytes of the double. They use the public API only and pass on the number-text path of the later batches, which stays behind ARCADEDB_NATIVE_NEXT_BIN=0. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…edb_n_next_bin) arcadedb_n_exec with flag 4 sends float[] values of 8 or more elements in a binary float32 tail, but only for the first reply (256 rows). arcadedb_n_next, which returns every later batch (2048 rows), has no sink, so an embedding column came back as JSON number text from row 257 on, and json.loads with the object_hook was about half of the wall time of a wide SELECT. New entry point arcadedb_n_next_bin(rs, max): the same batch as arcadedb_n_next, encoded with the float sink. The tail code of replyOf is one helper, withFloatTail, used by both. arcadedb_n_next is unchanged, so an older Python on a newer image keeps working. lib.py: NEXT_BIN is on when the library has the entry point; ARCADEDB_NATIVE_NEXT_BIN=0 turns it off (a diagnostic switch, to compare both paths in one image). NativeResultSet._fill reads batches with call_bin and loads(text, floats) when NEXT_BIN and the binary reply are on. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…, not the host memory test_default_max_heap_is_half_of_the_memory compared the isolate's default heap with the smaller of the host memory and /sys/fs/cgroup/memory.max, the root of the mount. Under any cgroup memory limit that is set on the process's own cgroup (a container, a systemd scope such as the job gate of the laptop) the heap is 50% of that limit and the test failed. _cgroup_memory_limit now follows /proc/self/cgroup to <root>/<path>/memory.max and walks up to the first value that is not "max"; the memory of the host stays the fallback. Unit tests of the reader use fake files under tmp_path. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…bulk From humemai/arcadedb-embedded-python#520. out() and the tail block of withFloatTail wrote the reply one byte (and one float) at a time with p.write / fp.write. They now use CTypeConversion.asByteBuffer(...).put(...) (Unsafe.copyMemory): the text, the zero padding up to the 4-byte boundary, then the float32 tail in native order. This is the state that both arms of the A/B of arcadedb_n_next_bin ran. tests/test_native_result_transport.py gets the round-trip tests of #520 (several batches, a small reply with a vector, rows of about a megabyte of multi-byte text, empty and one-row replies) next to the vector tests. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This was referenced Oct 26, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Port of humemai/arcadedb-embedded-python#521 (
arcadedb_n_next_bin) together with #520 (bulk copy of the reply text) to this repository, plus a fix of one test that failed under a cgroup memory limit. Native build only; the JVM build is not touched.Problem
arcadedb_n_execwith flag 4 sendsfloat[]values of 8 or more elements in a binary float32 tail, but only for the first reply (256 rows).arcadedb_n_next, which returns every later batch (2048 rows), has no sink, so an embedding column came back as JSON number text from row 257 on.json.loadswith the object hook was about half of the wall time of a wide SELECT (a 32-float vector column, 10k to 100k rows). Separately, the image wrote every reply byte by byte (p.writeper byte,fp.writeper float).Change
arcadedb_n_next_bin(rs, max): the same batch asarcadedb_n_next, encoded with the float sink. The tail code ofArcadeNative.replyOfbecomes one helper,withFloatTail, used byreplyOfand by the new entry point.arcadedb_n_nextis unchanged, so an older Python on a newer image keeps working. The lazy command path (Prefetched) reads through the same entry point.src/arcadedb/_native/lib.py:NEXT_BINis on when the library has the entry point.ARCADEDB_NATIVE_NEXT_BIN=0turns it off. I kept the switch as a diagnostic off switch, like the other phase 8 switches inlib.py(it is how the A/B below toggles the path inside one image). No document lists the sibling switches, so none needed an update.NativeResultSet._fillreads batches withlib.call_bin("arcadedb_n_next_bin", ...)andloads(text, floats)whenNEXT_BINand the binary reply (ARCADEDB_NATIVE_BIN_REPLY) are on, and witharcadedb_n_nextas before otherwise.out()and the tail block ofwithFloatTailcopy the reply withCTypeConversion.asByteBuffer(...).put(...)(Unsafe.copyMemory): the text, the zero padding up to the 4-byte boundary, then the float32 tail in native order. Before, they wrote one byte or one float per loop iteration.tests/test_native_result_transport.py: round trips of replies of every size (several batches, a small reply with a vector, rows of about a megabyte of multi-byte text, empty and one-row replies), and vectors of 0, 3, 8 and 32 floats, float32 extremes (1e-45, 3.4028235e38, +/-inf, -0.0) and rows with no vector, read in one list and through the row-by-row cursor, compared as the 8 bytes of the double. The tests use the public API only and pass on the number-text path (ARCADEDB_NATIVE_NEXT_BIN=0) as well as on the new one.tests/test_native_heap.py(from Native: start the isolate with -XX:MaximumHeapSizePercent=50 by default #74):test_default_max_heap_is_half_of_the_memorycompared the default heap with the smaller of the host memory and/sys/fs/cgroup/memory.max(the root of the mount), so it failed whenever the process's own cgroup had a limit (a container, or a systemd scope such as the laptop job gate: the heap is 50% of the limit)._cgroup_memory_limitnow follows/proc/self/cgroupto<root>/<path>/memory.maxand walks up to the first value that is notmax, with the host memory as fallback. Unit tests of the reader use fake files undertmp_path.Measured upstream (not re-run on this tree)
A pre-registered laptop A/B on one image built from the upstream branch (which contains #520 and #521); the two arms differ only by
ARCADEDB_NATIVE_NEXT_BIN. It passed its rule:SELECT id, grp, val, cat, txt, vec FROM D LIMIT 10000(32-float vector)SELECT id, grp, val, cat, txt, vec FROM D LIMIT 100000(32-float vector)Guards stayed within [0.99, 1.01]: indexed point lookup in SQL and in Cypher, 2-hop, GROUP BY, and a 200-row read with vectors. The A/A control was clean and the decoded-row digests were equal in every run.
The state shipped here is the code of that B arm (#520 plus #521). Not measured: the effect of #520 on its own against today's main. It is a byte-copy equivalence, covered by the round-trip tests above and by the parity run. The A/B itself ran on the upstream tree, not on this one, so the absolute times on this tree are not measured either.
Hunk-by-hunk check
Every hunk of the upstream #521 diff (which includes #520's, as it is stacked on it) and of the #520 diff, compared line by line with this PR (added and removed lines counted per file):
database.py_fillreads througharcadedb_n_next_binlib.pyNEXT_BINswitch,arcadedb_n_next_binsignature[P, L, I],global ... NEXT_BIN,hasattrfallbackArcadeNative.javanextBinentry point afternextArcadeNative.javareplyOfcallswithFloatTail; helper javadoc and signatureSupplier<String>with an import instead of the fully qualified name, becauseCLAUDE.mdasks for importsArcadeNative.javawithFloatTail(#520)ByteBufferandByteOrderimported instead of written outArcadeNative.javaout()(#520)test_native_result_transport.pyDIM, the 5000-row test), which are reworded because the later batches now carry the tailThe comparison found no other upstream line missing, in either diff. No upstream hunk was left out. The only differences are the three import-for-qualified-name lines and the reworded docstring and comments.
Verification
arcadedb_n_next_binis exported next toarcadedb_n_next), wheel built from it, and the fullnative/wheel/parity_wheel.pysuite run on that wheel, in the memory-limited job scope (7 GB) the laptop uses for such runs.test_native_heap.py(17 tests, 8 of them the new reader tests) passes inside that 7 GB scope.test_python_function_interrupt,test_sigint,test_interrupt_hygiene,test_transaction_context_interrupt) were run again in a foreground shell, because signal tests see SIGINT ignored in background jobs: 28 passed, 0 failed.arcadedb_n_next_bincalls, each with a float tail, and the decoded rows have the same digest withARCADEDB_NATIVE_NEXT_BIN=1and=0.tests/test_native_result_transport.pypasses withARCADEDB_NATIVE_NEXT_BIN=1and=0(6 tests each). The result-reading tests (test_resultset,test_resultset_arrow,test_results_after_close,test_resultset_read_errors,test_select_native_matrix,test_float_vector_bulk,test_columnar_readers) with the new files: 183 passed, 23 skipped (all NA-JVM tests that need the Java bridge).--severity-level low --confidence-level low) clean,isort --profile blackand black clean on the touched Python files,scripts/check_no_embedded_name.pypasses.🤖 Generated with Claude Code