Skip to content

Embedding matrices in the algo.* procedures are sized nodeCount x dimension and sit outside every budget (follow-up to #6216) #6263

Description

@lvca

Follow-up to #6216 (PR #6222). Flagged during review of that PR as "worth reconciling once both land" - #6065/#6214 and #6216 have now both landed, so this is that reconciliation.

The gap

#6065 capped the embedding-dimension knobs at MAX_EMBEDDING_DIMENSION (4096). #6216 added arcadedb.cypher.algoMaxWalkMemory and priced the random-walk buffers against it. Between them they leave the largest allocation in these procedures completely unbounded: the embedding matrices themselves, which are nodeCount x dimension.

// AlgoNode2Vec.java:186-187
final double[][] W    = new double[n][dim];
final double[][] WCtx = new double[n][dim];

// AlgoFastRP.java:124,138
final double[][] embed    = new double[n][dimensions];
final double[][] newEmbed = new double[n][dimensions];

// AlgoHashGNN.java:157
final double[][] embeddings = new double[n][embDim];

// AlgoGraphSAGE.java:123,143
double[][] embed            = new double[n][initDim];
final double[][] nextEmbed  = new double[n][outDim];

dimension is capped, but nodeCount is not - and nothing checks the product. The dimension cap bounds one embedding row at 32 KB; it says nothing about the matrix.

Why this is worth its own issue rather than a note

The numbers land inside the default configuration, not at an abusive one. For algo.node2vec with the default embeddingDimension: 128, W + WCtx cost 2 x n x 128 x 8 plus per-row headers, about 2080 bytes per node:

nodes W + WCtx at default dim 128 at the 4096 cap
100 000 ~208 MB ~6.5 GB
1 000 000 ~2.1 GB ~65 GB
10 000 000 ~21 GB ~655 GB

The comparison that makes the inconsistency concrete: at node2vec defaults the walk matrix costs 10n x (36 + 4x80) = ~3560 bytes per node and is refused up front if it exceeds the budget, while the embedding matrices cost ~2080 bytes per node on the same call and are not checked at all. Two allocations of the same order of magnitude, one budgeted and one not.

algo.fastRP is the cleaner example: it has no walk buffer at all, so arcadedb.cypher.algoMaxWalkMemory never fires for it and its 2 x n x dimensions x 8 allocation has no ceiling of any kind.

The failure mode is an OutOfMemoryError - not a client error naming a parameter, which is what #6065 and #6216 both exist to produce.

Suggested direction

Rather than a second budget key, generalise the one #6216 added. CYPHER_ALGO_MAX_WALK_MEMORY is named for walks because that was all it priced; the concept it implements - "estimate the working-set footprint in saturating long arithmetic, reject as a client error before allocating, auto-scale the default with the JVM heap" - is not walk-specific. Options worth weighing:

  1. Rename to arcadedb.cypher.algoMaxWorkingMemory (keeping the old key as a deprecated alias) and price the embedding matrices through the same checkWalkBudget/saturatingProduct helpers, renamed to match.
  2. Leave the walk budget alone and add a sibling key for embedding matrices - simpler, but two keys for one question ("how much heap may one algo call take?") is the worse interface.

Option 1 looks right: one question, one setting, and the helpers in AbstractAlgoProcedure already have the shape.

Note AlgoGraphSAGE.java:138 also allocates new double[outDim][concatDim], which is dimension-squared-ish rather than n-scaled. Bounded by the 4096 cap on both axes, so it is a smaller concern, but worth pricing in the same pass.

Related

#5924, #6065, #6216, PR #6055, PR #6214, PR #6222.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions