Speed up Slice::ToString(bool hex=true) - #15045
Conversation
Replace branching with a single indexed load from constexpr lookup table.
Instead of reserve + push_back, one allocation sets final size immediately, avoiding repeated size updates and bounds checks.
Raw pointer skips repeated operator[] overhead and internal index tracking.
When hex=false, construct the string directly, avoiding assign + return copy.
Noticed when using "ldb --value_hex scan":
Before:
perf stat -e branches,branch-misses tools/ldb --value_hex scan --db=test_db | dd of=/dev/null
Performance counter stats for 'tools/ldb --value_hex scan --db=test_db':
2,983,373,019 branches:u
101,288,380 branch-misses:u # 3.40% of all branches
4.365280485 seconds time elapsed
2.854659000 seconds user
0.741348000 seconds sys
884520+1 records in
884520+1 records out
452874563 bytes (453 MB, 432 MiB) copied, 4.40603 s, 103 MB/s
After:
perf stat -e branches,branch-misses tools/ldb --value_hex scan --db=test_db | dd of=/dev/null
Performance counter stats for 'tools/ldb --value_hex scan --db=test_db':
275,719,553 branches:u
797,887 branch-misses:u # 0.29% of all branches
1.922297831 seconds time elapsed
0.346486000 seconds user
0.785366000 seconds sys
884520+1 records in
884520+1 records out
452874563 bytes (453 MB, 432 MiB) copied, 1.97187 s, 230 MB/s
Built with DEBUG_LEVEL=0. gcc 8.5.0
|
| Check | Count |
|---|---|
concurrency-mt-unsafe |
1 |
performance-unnecessary-value-param |
1 |
| Total | 2 |
Details
db_stress_tool/db_stress_test_base.cc (1 warning(s))
db_stress_tool/db_stress_test_base.cc:475:5: warning: function is not thread safe [concurrency-mt-unsafe]
utilities/backup/backup_engine.cc (1 warning(s))
utilities/backup/backup_engine.cc:2301:28: warning: parameter 'progress_callback' is passed by value and only copied once; consider moving it to avoid unnecessary copies [performance-unnecessary-value-param]
Claude Code Review - OBSOLETESuperseded by a newer AI review. Expand to see the original review.✅ Claude Code ReviewAuto-triggered after CI passed — reviewing commit 41d1c16 SummaryClean, well-motivated performance optimization for High-severity findings (0): No high-severity findings. Full review (click to expand)Findings🔴 HIGHNone. 🟡 MEDIUMM1. Use
|
| Context | Assumptions hold? | Action needed? |
|---|---|---|
All platforms (C++20 required, string::data() mutable since C++17) |
YES | None |
| Thread safety (const method, no shared mutable state) | YES | None |
DecodeHex round-trip (output 0-9A-F, fromHex accepts both cases) |
YES | None |
| Empty slice (size_==0, loop body never entered) | YES | None |
Positive Observations
- Lookup table eliminates per-nibble branching (10x branch reduction per benchmarks).
return {data_, size_}for non-hex path is cleaner thanassign+return.- Pre-allocating exact size avoids repeated capacity updates from
push_back. static constexprlookup table has zero runtime initialization cost.
ℹ️ About this response
Generated by Claude Code.
Review methodology: claude_md/code_review.md
Limitations:
- Claude may miss context from files not in the diff
- Large PRs may be truncated
- Always apply human judgment to AI suggestions
Commands:
/claude-review [context]— Request a code review/claude-query <question>— Ask about the PR or codebase
as per CLAUDE.md: "Avoid static_cast, reinterpret_cast, and C-style casts; static_cast_with_check, up_cast, and lossless_cast from cast_util.h are preferred."
that separates the comment header from the fromHex function it describes.
Claude Code Review - OBSOLETESuperseded by a newer AI review. Expand to see the original review.✅ Claude Code ReviewAuto-triggered after CI passed — reviewing commit a947ff2 SummaryClean, well-motivated performance optimization that replaces branching hex conversion with a lookup table and eliminates per-character High-severity findings (0): No high-severity findings. Full review (click to expand)Findings🔴 HIGHNone. 🟡 MEDIUMM1. Include ordering —
|
| Context | Assumptions hold? | Action needed? |
|---|---|---|
| WritePreparedTxnDB | Yes | Safe |
| ReadOnly DB | Yes | Safe |
| User-defined timestamps | Yes | Safe |
| Concurrent callers | Yes (const, no shared mutable state) | Safe |
| All platforms (MSVC, GCC, Clang) | Yes (C++17 string::data()) |
Safe |
Verified claims:
- Lookup table output identical to
toHex():kHexChars[0..15]matches'0'+v/'A'+v-10for all nibble values. Correct. return {data_, size_}equivalent toassign+return: Both constructstd::string(const char*, size_t). New form leverages C++17 guaranteed copy elision. Correct.toHex()has no other callers in codebase. Verified.lossless_cast<const unsigned char*>has precedent atutil/coding.h:109. Verified.
Positive Observations
- ~2x speedup with minimal, clean code change
- Properly uses
lossless_castper project guidelines fromHexcorrectly preserved forDecodeHex- Non-hex fast path nicely leverages C++17
ℹ️ About this response
Generated by Claude Code.
Review methodology: claude_md/code_review.md
Limitations:
- Claude may miss context from files not in the diff
- Large PRs may be truncated
- Always apply human judgment to AI suggestions
Commands:
/claude-review [context]— Request a code review/claude-query <question>— Ask about the PR or codebase
Validates empty inputs, a single-byte value and a round-trip of all 256 possible byte values using both standard and hex representations.
Claude Code Review - OBSOLETESuperseded by a newer AI review. Expand to see the original review.✅ Claude Code ReviewAuto-triggered after CI passed — reviewing commit 4cb05b8 SummaryClean, well-motivated performance optimization with substantial measured improvement (2.3x throughput, 10x branch reduction). The implementation is correct and follows established codebase patterns. High-severity findings (0): No high-severity findings. Full review (click to expand)Findings🔴 HIGHNone. 🟡 MEDIUMM1.
|
| Context | Does code execute? | Assumptions hold? | Action needed? |
|---|---|---|---|
| WritePreparedTxnDB | Yes (via logging) | Yes — pure data transform | None |
| ReadOnly DB | Yes | Yes | None |
| Concurrent readers | Yes | Yes — static constexpr is safe |
None |
| 32-bit platforms | Yes | 2*size_ overflow theoretically possible |
See L1 |
| MSVC/Windows | Yes | std::string::data() returns char* in C++17 |
None |
| Checksum encoding | Yes (via ChecksumInt32ToHex) |
Yes — uppercase preserved | None |
Positive Observations
- Excellent benchmark methodology: PR includes before/after
perf statwith branch counters, demonstrating the optimization thesis (branch elimination) with hard data. - Correct use of
lossless_cast: Follows the established codebase pattern (same usage inutil/coding.h:109,util/prefix_varint.h:95) rather than using rawreinterpret_cast. - Test covers all 256 byte values: The round-trip test with all byte values is thorough and catches any encoding errors.
- Non-hex path simplification:
return {data_, size_}is cleaner than the oldresult.assign(); return result;and likely slightly faster (avoids constructing an empty string first). - kHexChars is 17 bytes: Fits in a single cache line, excellent for the hot loop.
- Sequential memory access: Both
srcandpadvance linearly, which is optimal for hardware prefetching.
ℹ️ About this response
Generated by Claude Code.
Review methodology: claude_md/code_review.md
Limitations:
- Claude may miss context from files not in the diff
- Large PRs may be truncated
- Always apply human judgment to AI suggestions
Commands:
/claude-review [context]— Request a code review/claude-query <question>— Ask about the PR or codebase
external linkage is not required.
to reduce duplication. Also to verify chars in hex string are valid after encoding. Add test for hex string DEADBEEF and deadbeef.
✅ Claude Code ReviewAuto-triggered after CI passed — reviewing commit 473e88c SummaryClean, well-motivated performance optimization. The lookup-table approach is correct and the benchmarks are compelling (10x reduction in branches, ~2x throughput improvement). No correctness issues found. High-severity findings (0): No high-severity findings. Full review (click to expand)Findings🔴 HIGHNone. 🟡 MEDIUMNone. 🟢 LOW / NITL1. Pre-existing integer overflow in
|
| Check | Result |
|---|---|
Lookup table bounds: c >> 4 ∈ [0,15], c & 0xF ∈ [0,15], kHexChars has 16 entries |
Safe |
Buffer sizing: std::string(2*size_, '\0') matches exactly 2*size_ bytes written |
Correct |
lossless_cast<const unsigned char*>(data_): same-size integral pointer cast |
Valid (matches util/coding.h:109) |
std::string::data() mutability: C++17 guarantees writable char* |
Valid |
| Empty slice: loop never executes, returns empty string | Correct |
hex=false: return {data_, size_} ≡ std::string(data_, size_) |
Correct |
Output format: uppercase A-F, same as old toHex() |
Backward compatible |
fromHex/toHex linkage: both only used within util/slice.cc |
Safe |
Positive Observations
- Significant measurable improvement: 10x branch reduction, ~2x throughput on real workload
- Clean implementation: Canonical lookup-table hex encoding
- Good use of
lossless_cast: Follows CLAUDE.md guidance - Improved
hex=falsepath: Avoids unnecessary default construction - Comprehensive tests: All 256 byte values, round-trip verification
ℹ️ About this response
Generated by Claude Code.
Review methodology: claude_md/code_review.md
Limitations:
- Claude may miss context from files not in the diff
- Large PRs may be truncated
- Always apply human judgment to AI suggestions
Commands:
/claude-review [context]— Request a code review/claude-query <question>— Ask about the PR or codebase
hex=false, construct the string directly, avoiding assign + return copy.Noticed when using
ldb --value_hex scan:Before:
After:
Built with
DEBUG_LEVEL=0.gcc 8.5.0Also see #15070 which incorporates the changes from this PR and also speeds up
Slice::DecodeHex.