utils/parser-memory-profiler/README.md
A tool for profiling memory allocations in the ClickHouse SQL parser using jemalloc's heap profiling capabilities.
This utility helps analyze memory allocation patterns when parsing SQL queries. It's useful for:
| File | Description |
|---|---|
run_profiler.sh | Bash script to batch-process multiple queries and generate JSON results |
generate_report.py | Python script to generate an interactive HTML report for a single result set |
generate_comparison_report.py | Python script to compare two result sets (before/after optimization) |
rebuild_reports.py | Python script to regenerate reports from existing heap profile files |
test_queries.txt | Sample set of 100 diverse SQL queries for testing |
The C++ profiler binary source is located at src/Parsers/examples/parser_memory_profiler.cpp.
cd /path/to/clickhouse
mkdir -p build && cd build
cmake ..
ninja parser_memory_profiler
jeprof is used to analyze heap profile files and generate detailed reports.
git clone https://github.com/jemalloc/jemalloc ~/github/jemalloc
cd ~/github/jemalloc
./autogen.sh
make
# Binary will be at ~/github/jemalloc/bin/jeprof
git clone https://github.com/brendangregg/FlameGraph ~/FlameGraph
# macOS
brew install graphviz
# Ubuntu/Debian
apt install graphviz
# RHEL/CentOS
yum install graphviz
cd utils/parser-memory-profiler
# Run profiler on default test queries
./run_profiler.sh -b ../../build
# Generate HTML report
python3 generate_report.py -i profiler_output/results.json -o profiler_output/report.html
# Open the report
open profiler_output/report.html # macOS
xdg-open profiler_output/report.html # Linux
Create a file with SQL queries (one per line, ending with semicolons):
-- my_queries.txt
SELECT * FROM users WHERE id = 1;
CREATE TABLE test (a Int64, b String) ENGINE = MergeTree() ORDER BY a;
INSERT INTO test VALUES (1, 'hello'), (2, 'world');
Run the profiler:
./run_profiler.sh -q my_queries.txt -o my_results -b ../../build
python3 generate_report.py -i my_results/results.json -o my_results/report.html
Profile both versions and generate a comparison report:
# Profile original version
git checkout main
cd build && ninja parser_memory_profiler && cd ..
./run_profiler.sh -o results_before -b ../build
# Profile optimized version
git checkout my-optimization-branch
cd build && ninja parser_memory_profiler && cd ..
./run_profiler.sh -o results_after -b ../build
# Generate comparison report
python3 generate_comparison_report.py \
--before results_before/results.json \
--after results_after/results.json \
--output comparison.html \
--label-before "Original" \
--label-after "Optimized"
After running run_profiler.sh, the output directory contains:
profiler_output/
├── profiles/ # jemalloc heap profile files
│ ├── query_1_before.*.heap
│ ├── query_1_after.*.heap
│ ├── query_2_before.*.heap
│ └── ...
├── results.json # JSON with all profiling data
└── report.html # Interactive HTML report (after generate_report.py)
The HTML report shows for each query:
Shows side-by-side comparison:
parser_memory_profiler.cpp:
mallctl()run_profiler.sh:
parser_memory_profiler with MALLOC_CONF=prof:true,prof_active:true,lg_prof_sample:0jeprof to analyze heap profile diffsresults.jsongenerate_report.py:
results.json| Variable | Description |
|---|---|
MALLOC_CONF | jemalloc configuration |
Required settings for profiling:
MALLOC_CONF=prof:true,prof_active:true,lg_prof_sample:0
prof:true - Enable profiling supportprof_active:true - Activate profiling at startuplg_prof_sample:0 - Sample every allocation (log2(1) = 0)The jemalloc library must be built with profiling support. Check:
./parser_memory_profiler <<< "SELECT 1"
If it shows "config.prof: no", jemalloc was built without --enable-prof.
Increase the sampling rate or check that lg_prof_sample:0 is set. Higher values (like 19, the default) will miss small allocations.
jeprof can be slow when analyzing large binaries. Increase the timeout:
python3 rebuild_reports.py profiler_output --timeout 300
Install graphviz for call graph generation:
brew install graphviz # macOS
apt install graphviz # Linux
Typical findings from parser memory profiling:
std::make_shared overhead can be significantThis tool is part of ClickHouse and is licensed under the Apache License 2.0.