You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Native wheel: replies are copied three times in Python (string_at, decode, json.loads) #323
Source for the practice: a literature and documentation pass of 2026-11-09 (the Arrow C Data and C Stream interface specifications, the ADBC documentation, the DuckDB, Polars and psycopg documentation, the JPype user guide, and Raasveldt and Muehleisen, VLDB 2017, on result-set transfer); the exact list of 30 sources is in my notes and I will attach it on request. Everything about our code is read at the tag 26.11.1 (commit 2f28a328be); nothing was run or timed, so there is no speed claim. Part of #315.
Read: _native/lib.py:330-365 copies each reply out of the library with ctypes.string_at, then decodes it, then parses it with json.loads. The usual practice is to wrap the pointer in an owner object and free it when the owner is released, so the bytes are not copied before they are parsed (for example cffiffi.gc, or pyarrow.foreign_buffer(base=...) for a buffer).
Proposal: a small pointer-owner class that frees the reply on release and lets the decoder read from the address, keeping every result as it is. Python only. This is independent of #312's columnar frames and also helps the one-row paths of #229 and #317.
Measure: per-reply microseconds and bytes copied for a small and a large reply, warmed, before and after. Done when the copy count is lower and the measurement is written here.
The measurement this issue asks for was not written here, so here it is. I ran it on the published arcadedb==26.11.1.post2 wheel against the reader of 26.11.1, on my laptop: one run, one table shape (1,000,000 rows of id, name, score, flag). It is a directional run and not an ab.py A/B, so I name a difference only when the ranges do not overlap.
Copies: the reply text is now copied once, into the str that json.loads reads (before: a bytes copy and a decode copy; PR #327 explains the change). The Python-side peak of reading one 2,048-row reply, the batch the real read uses, is 0.35 MiB before and 0.17 MiB after. For one 1,000,000-row reply it is 183.74 MiB before and 91.87 MiB after.
Time: reading one 2,048-row reply took 17.2 µs before and 13.1 µs after (median), which my rule calls not separated. Only the extreme case, one 1,000,000-row reply, is separated (84.3 ms to 42.0 ms, 3 repetitions). A whole 1,000,000-row to_json_list() took 2.824 s before and 2.779 s after (median of 7 paired rounds, old over new 1.013 with range 1.005 to 1.021, against 0.983 to 1.009 for the control): not separated. The rows are byte-identical in every run.
So the claim is fewer copies and half the Python-side peak of a read, and no speed claim. Not measured: the column-frame reads of #331 (each frame is still copied once), other table shapes, other platforms. The change is in 26.11.1.post1 and 26.11.1.post2, and this was the last item of the "done when", so I close this issue.
Activity
Native: read each library reply from its address, decode it in one co…
tae898 commentedon Nov 14, 2026
The measurement this issue asks for was not written here, so here it is. I ran it on the published
arcadedb==26.11.1.post2wheel against the reader of26.11.1, on my laptop: one run, one table shape (1,000,000 rows ofid,name,score,flag). It is a directional run and not anab.pyA/B, so I name a difference only when the ranges do not overlap.Copies: the reply text is now copied once, into the
strthatjson.loadsreads (before: abytescopy and a decode copy; PR #327 explains the change). The Python-side peak of reading one 2,048-row reply, the batch the real read uses, is 0.35 MiB before and 0.17 MiB after. For one 1,000,000-row reply it is 183.74 MiB before and 91.87 MiB after.Time: reading one 2,048-row reply took 17.2 µs before and 13.1 µs after (median), which my rule calls not separated. Only the extreme case, one 1,000,000-row reply, is separated (84.3 ms to 42.0 ms, 3 repetitions). A whole 1,000,000-row
to_json_list()took 2.824 s before and 2.779 s after (median of 7 paired rounds, old over new 1.013 with range 1.005 to 1.021, against 0.983 to 1.009 for the control): not separated. The rows are byte-identical in every run.So the claim is fewer copies and half the Python-side peak of a read, and no speed claim. Not measured: the column-frame reads of #331 (each frame is still copied once), other table shapes, other platforms. The change is in
26.11.1.post1and26.11.1.post2, and this was the last item of the "done when", so I close this issue.