Skip to content

Vector vertex loads: the JVM wheel spends about 60 percent of the wall in unwrapped Python when rows hold Java float[], the native wheel about 30 percent in prep #337

Closed

Description

@tae898

From the untimed ingest profile of the released 26.11.1 wheels (no speed claim; 3 repetitions, 66 cells, none void): a create_vertices load whose rows hold Java float[] vectors (the shape the e2 and l3d lanes use) spends 59.2 percent (e2) and 60.6 percent (l3d) of the JVM wheel's measured wall in Python code that no phase counter covers, and the native wheel spends 31.3 percent (e2) and 29.0 percent (l3d) in its prep phase (the vector split and the binary argument encoding).

Read in the profile: the JVM cells count only createVertices (the property-matrix call of the engine's GraphBatch) and no batcher call, so a load with Java float[] rows takes the property-matrix path, and _create_vertices_json_bulk returns None at the first Java float[] (graph_batch.py), so the scan costs one row, not every row. A Java float[] is not a vector candidate, and the 60 percent is in _to_java_property_matrix (a Python loop over each row's properties, one JArray(JObject) per row, the whole input held at once), read from the code, not timed. A numpy array or a list of floats is documented to take the binary-matrix path instead.

Proposal: recognise a Java float[] (and a numpy float32 array) as a vector candidate on both wheels and send it through the one binary-matrix path, with a test that the stored vectors are bit-identical to the old path; measure per-phase time and memory (B per row) before and after. This is not gated on a paper cell: every glue drawback found gets fixed.

Edited: the first version said the scan reads every row; PR #339 read the code and it stops at the first Java float[].

Activity

  1. added
    jvmThe arcadedb-jvm package (JPype build)
    nativeThe arcadedb package (native library build)
    performanceSpeed or memory of the bindings, with a measurement
    pythonPull requests that update python code
    vectorVector indexes and find_nearest
    on Nov 9, 2026
  2. added this to the 26.12.1 milestone on Nov 9, 2026
  3. tae898 commented on Nov 9, 2026

    @tae898
    CollaboratorAuthor

    I edited the issue body: the scan in _create_vertices_json_bulk stops at the first Java float[] (one row), it does not read every row. The 60 percent is in _to_java_property_matrix. Found while reading the code for #339.

  4. self-assigned this
    on Nov 9, 2026
  5. tae898 commented on Nov 12, 2026

    @tae898
    CollaboratorAuthor

    Here are the before and after figures for this issue, from the timed A/B I ran on 2026-11-10 on my laptop (Intel Core Ultra X9 388H, Linux 7.0.0, Python 3.12, two cores). A is the released 26.11.1 wheel of each family and B is the candidate wheel of each (CI builds of the release/26.11.1 line before 26.11.1.post1, sha256 starting 7cc57106 for JVM and 144091c9 for native), with PR #339 at its final head in them. B is not the published 26.11.1.post2 wheel: PR #339 is on main and in the 26.11.1.post2 tag, and I did not re-run this on the published files.

    The cells are graph_batch(use_wal=True).create_vertices with rows built by to_java_float_array (a Java float[] on the JVM wheel, a float32 array on the native wheel), as the e2 shape (2 calls of 50,000 rows, 64 dimensions) and the l3d shape (10 calls of 10,000 rows, 96 dimensions), 100,000 rows per cell, with use_wal=True passed in both arms so the durability matches. The study registered these cells as controls and not as the gating figures of this issue, but their rows are exactly the shape of this issue. There are 13 or 14 clean pairs per cell, and below 1.00 means B is faster.

    wheel and shape A B time B/A [95% CI] peak RSS B/A [95% CI] RssAnon growth per row, A to B
    JVM, e2 1.398 s 0.749 s 0.544 [0.527, 0.546] 0.936 [0.861, 0.954] 1439.9 to 978.5 bytes
    JVM, l3d 1.152 s 0.664 s 0.570 [0.562, 0.588] 0.943 [0.923, 1.017] 985.1 to 833.3 bytes
    native, e2 0.752 s 0.567 s 0.755 [0.749, 0.757] 1.009 [1.008, 1.009] 692.7 to 686.0 bytes
    native, l3d 0.785 s 0.614 s 0.783 [0.781, 0.784] 0.994 [0.993, 0.995] 866.1 to 844.3 bytes

    Time falls on both wheels and the upper end of every interval is below 0.90, which is a WIN by the rule I wrote before the run. The memory columns are process readings that the rule does not gate on: the JVM wheel's peak is lower, though the interval of the l3d shape reaches 1.017, and the native wheel's peak did not move. An earlier candidate wheel had made the plain scalar Person load 1.307 times slower on the JVM wheel (the validation pass of #330); the fix is in the final head of PR #339, and with it j_g1_cv is 0.888 s to 0.881 s, B/A 1.018 [0.947, 1.026] (GRAY), and n_g1_cv is 0.998 [0.995, 1.001] (NO-CHANGE).

    I did not time the phases again: the 59.2 and 60.6 percent of the first profile were read from the profile and the code, and this A/B times the whole call. The code is on main with the tests that the stored vectors are bit-identical, and the time and memory before and after are above, so I am closing this issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

jvmThe arcadedb-jvm package (JPype build)nativeThe arcadedb package (native library build)performanceSpeed or memory of the bindings, with a measurementpythonPull requests that update python codevectorVector indexes and find_nearest

Type

No type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions