Cache constant polygon by raw column value in pointInPolygon#108184
Conversation
Pre-PR validation gate
Note: measured on a local x86 debug build, so absolute numbers are not comparable to the ARM release figures in the issue; the mechanism is architecture-independent. ARM-hardware confirmation is requested from the reporter. Session id: cron:clickhouse-worker-slot-4:20260622-163100 |
|
cc @nikitamikhaylov @scanhex12 — could you review this? It keys the |
|
The proper fix is here for the regression: We can consider this once the above PR is merged. |
|
Workflow [PR], commit [2581f24] Summary: ✅
AI ReviewSummaryThis PR changes Missing context / blind spots
Final VerdictStatus: ✅ Approve |
|
Thanks for the pointer. I read #107247: it reworks the cache into a shared, bounded LRU for the memory-growth issue (#100759 / #106393), which is valuable, but the constant-polygon lookup key there is still That per-block parse is exactly what the reporter's own validated prototype in #106596 targets ("constant-column cache key before parse; parse only on cache miss", before_after -95.355%), which is what this PR does: key the cache on the raw constant column via So the two are orthogonal and both touch the same region. I am happy to hold this behind #107247; once it lands I can fold the raw-column cache key into its bounded-cache lookup as a small follow-up so the parse-cost fix is not lost (or rebase this on top now, whichever you prefer). |
Pre-PR validation gate (follow-up commit: validate flag in cache key)Addressing the
Session id: cron:clickhouse-worker-slot-1:20260622-184700 |
|
📊 Cloud Performance Report ✅ AI verdict: This PR changes only the pointInPolygon constant-polygon cache key (keying on raw arguments instead of the parsed geometry). ClickBench Q15 is a plain GROUP BY/count aggregation that never invokes pointInPolygon, so the flagged -11.49% improvement cannot plausibly come from this change. The underlying measurements were also fairly noisy on the new build, so we read this as run-to-run variance rather than a real PR effect and downgraded it to not-sure. No other queries showed a real shift. clickbenchFlagged queries (1 of 43)
q-value = BH-FDR adjusted p; smaller is stronger evidence. MIRAI flags a query when q < fdr_q (default 0.10) — the value the verdict is based on. tpch_adapted_1_official🟢 No significant changes Debug info
|
a39afd8 to
480c9f8
Compare
Pre-PR validation gate (click to expand)
Session id: cron:clickhouse-worker-slot-0:20260625-220800 |
|
@nihalzp #107247 is merged now, thanks. I rebased this on top of it. #107247's bounded cache still keys the constant-polygon lookup on the parsed geometry and re-parses on every input block, so the per-block parse behind #106596 remained. This PR keys the cache on the raw constant arguments and moves the parse into the cache-miss load function, so a hit does no parse. The key folds in |
|
cc @nikitamikhaylov @scanhex12 - could you review this? It keys the |
CI finish ledger — 480c9f8Every failure below has an owner: a fixing PR (ours or external), or a full-effort fix task whose fixing-PR link will be posted here when it opens. Only
Neither functional failure is in this PR's changed files ( Session id: cron:our-pr-ci-monitor:20260626-023000 |
|
Fixing PR for test_async_backup_restore_with_max_execution_time_zero created: #108567 |
There was a problem hiding this comment.
Why did you replace the xxhash with siphash? It degraded performance of one of the existing performance tests:
point_in_polygon_const_hashing | 1.0651 | 1.7929 | +68.3% | arm | Likely | 0 | SELECT count() FROM numbers(1000000) WHERE NOT ignore(pointInPolygon( (number % 997 / 997. - 0.5, (number * 7 % 991) / 991. - 0.5), arrayMap(x -> (cos(x / 8000. * 2 * pi()), sin(x / 8000. * 2 * pi())), range(8000)))) SETTINGS max_block_size = 64, validate_polygons = 0;Make sure there are no slowdown.
480c9f8 to
7892439
Compare
|
@nihalzp good catch, fixed. The slowdown came from how I hashed the raw columns, not from dropping xxhash in principle. Root cause: the previous key hashed the parsed geometry, so it ran one bulk Fix: I kept the raw-column key (so the per-block parse behind #106596 is still removed) but hash it with XXH3 again, over the columns' contiguous buffers in bulk ( Local debug build, your perf query (
Both are faster than master now: master parses + XXH3 per block, this PR does XXH3-only (no parse). The |
|
Pre-PR validation gate for the perf fix (commit 7892439, addressing the Pre-PR validation gate (click to expand)
Session id: cron:clickhouse-worker-slot-0:20260626-095500 |
7892439 to
36d3118
Compare
|
Fixed the Style check failure on the previous push. The regression test 04411 used |
|
Why not use |
For a constant polygon, executeImpl parsed and hashed the polygon on every input block before the preprocessed-polygon cache lookup, because the cache key was a hash of the parsed geometry. Key the cache on the raw constant arguments instead and move the parse into the cache-miss load function, so repeated invocations skip the parse entirely. This addresses the per-block parse cost behind the ARM regression in ClickHouse#106596, which the shared bounded cache added in ClickHouse#107247 did not remove. The raw-argument key is computed with XXH3 over the constant columns' contiguous buffers (array offsets, tuple elements, numeric leaves), the same hash the parsed-geometry key used. Hashing the constant polygon is on the per-block hot path, so an element-wise SipHash walk over the raw values regressed the point_in_polygon_const_hashing perf test; the bulk XXH3 walk is faster than the previous parse-then-hash (no parse on the hot path) and removes that regression. The key folds in the validate_polygons setting and the argument types: parsing (and its validity check) runs only on a miss, so an entry built under one validate mode or coordinate type must not satisfy a lookup under another. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
36d3118 to
2581f24
Compare
|
|
LLVM Coverage Report
Changed lines: Changed C/C++ lines covered by tests: 56/58 (96.55%) | Lost baseline coverage (was covered on master, now uncovered in this PR): 1 line(s) · Uncovered code |
CI finish ledger — 2581f24Every failure below has an owner; none is in this PR's diff (
Session id: cron:our-pr-ci-monitor:20260626-173000 |
c84fb04
Changelog category (leave one):
Changelog entry (a user-readable short description of the changes that goes into CHANGELOG.md):
Improve performance of the
pointInPolygonfunction with a constant polygon. The preprocessed-polygon cache is now keyed on the raw constant arguments, so a constant polygon is parsed only once (on a cache miss) instead of being re-parsed on every input block.Description
Closes #106596. Rebased on top of #107247 (now merged).
Root cause
For a constant polygon argument,
FunctionPointInPolygon::executeImplpreprocesses the polygon into a grid / R-tree and caches the preprocessed impl in a process-wide cache keyed by a 128-bit hash. The problem is the order of operations: the cache key is computed from the parsedboost::geometryobject, so every invocation ofexecuteImpl(one per input block) first parses the constant polygon just to compute the key:The cache stores the expensive preprocessing but not the parse, so a query that scans many blocks re-parses and re-hashes the (potentially huge) constant polygon on every block. This is the ARM regression in #106596.
Relationship to #107247
#107247 reworked this cache into a shared, bounded LRU (
CacheBase) for the memory-growth issue (#106393). It did not change the order of operations: the constant-polygon lookup key is still a hash of the parsed geometry, andparseConst*still runs on every block beforegetOrSet. So #107247 did not remove the per-block parse behind #106596. This PR is orthogonal and sits on top of it.Fix
Key the cache on the raw constant arguments (
IColumn::updateHashWithValueover the constant columns) and move the parse into the cache-missloadfunction. On a cache hit, no parse happens at all.The key also folds in two things that change how the same raw bytes are interpreted, because parsing (and its validity check) now runs only on a miss:
validate_polygonssetting: otherwise an entry built withvalidate_polygons = 0would satisfy avalidate_polygons = 1lookup and skip the validation that should raise an exception;Float64during parsing, so the same raw bytes under a different declared type would parse to different coordinates.Moving the parse under
getOrSetis exception-safe with the newCacheBase: it inserts the entry only on a successfulload, so a failed parse/validation does not leave a dead entry in the cache.Test
04411_point_in_polygon_const_cache_keycovers the validate-mode ordering (both orders), that a failed validation does not poison the cache, that type-distinct polygons are cached independently, and the multipolygon path. Verified locally that the test fails without the validate flag in the key (avalidate_polygons = 1query that should raiseBAD_ARGUMENTSinstead returns a result) and passes with the fix.Version info
26.7.1.154(included in26.7and later)