Profiling and Debugging Tools in zvec: A Complete Guide to Performance Analysis
The zvec vector database provides built-in stage-level profiling via zvec::Profiler, RAII-based timing helpers, pluggable logging macros, multi-threaded benchmarking utilities, and code coverage scripts to diagnose performance bottlenecks throughout the query lifecycle.
The Alibaba zvec repository ships with a comprehensive, self-contained toolkit for profiling and debugging vector search operations. These integrated utilities allow developers to measure query execution latency at granular stages, monitor index-level operations, and validate performance optimizations without relying on external instrumentation.
Stage-Level Profiling with zvec::Profiler
The primary profiling interface in zvec is the zvec::Profiler class, defined in src/db/common/profiler.h. This lightweight collector records named execution stages and aggregates their timings in seconds.
Core Profiler API
The profiler exposes three main methods for manual instrumentation: open_stage(), close_stage(), and add(). You instantiate a profiler and bracket code sections to capture precise timing data.
#include "zvec/db/common/profiler.h"
#include <iostream>
#include <memory>
auto profiler = std::make_shared<zvec::Profiler>(true); // true enables the profiler
profiler->open_stage("parse query");
// ... query parsing logic ...
profiler->close_stage();
profiler->open_stage("vector search");
// ... search execution ...
profiler->close_stage();
std::cout << profiler->display(); // outputs a formatted timing table
The display() method renders a human-readable table showing each stage's duration, making it easy to identify bottlenecks in complex query pipelines.
RAII Timing with ScopedLatency
For exception-safe profiling, zvec provides the ScopedLatency helper class (lines 176-186 in src/db/common/profiler.h). This RAII wrapper automatically records timing when the object goes out of scope, eliminating the risk of missing close_stage() calls during early returns or exceptions.
{
zvec::ScopedLatency timer(profiler.get(), "automatic_stage");
// ... code to time ...
} // timing automatically recorded here
SQL Engine Integration
The SQL engine implementation in src/db/sqlengine/sqlengine_impl.cc (lines 125-188) demonstrates production-grade profiler usage. The engine automatically opens specific stages during query processing: "analyze stage", "message_to_sqlinfo", and "plan stage". This integration provides out-of-the-box visibility into query lifecycle performance without requiring manual instrumentation of the engine internals.
Index-Level Performance Tracking
Beyond the high-level query profiler, zvec exposes a separate profiler struct for low-level index operations via IndexContext.
The IndexContext Profiler
Defined in src/include/zvec/core/framework/index_context.h (lines 29-52), zvec::core::Profiler is a lightweight map-based accumulator used by index implementations to record per-operation metrics. Index developers can inject timing data directly into the search context:
context->profiler().add("search", elapsed_seconds);
This collected data can be printed using display(), as referenced in the recall testing tool at tools/core/recall.cc (line 679), enabling fine-grained analysis of index-specific performance characteristics like graph traversal or quantization overhead.
Logging Framework for Debug Output
zvec implements a flexible, macro-based logging system to aid debugging without impacting release performance.
Macro-Based Logging
The logging interface resides in src/include/zvec/ailego/logger/logger.h (lines 39-66) and provides severity-level macros: LOG_DEBUG, LOG_INFO, LOG_WARN, LOG_ERROR, and LOG_FATAL. The underlying zvec::ailego::Logger class is pluggable; developers can register custom log sinks via FACTORY_REGISTER_LOGGER macros (lines 22-30) to route output to files, consoles, or remote aggregation systems.
Runtime Log Level Control
Debug output can be enabled dynamically without recompilation by setting the global log level through the broker:
zvec::ailego::LoggerBroker::SetLevel(zvec::ailego::Logger::LEVEL_DEBUG);
This activates verbose LOG_DEBUG statements scattered throughout the codebase, such as diagnostic output for use_key_info_map_ variables, providing immediate insight into internal state transitions during query execution.
Benchmarking and Testing Utilities
zvec includes standalone tools for reproducible performance testing and quality assurance.
Multi-Threaded Benchmark Driver
The tools/core/bench.cc executable provides a configurable, multi-threaded benchmark harness for vector search and index construction. It accepts YAML configuration files specifying index types, query datasets, thread counts, and batch sizes. The template class Bench<T> utilizes BenchResult (defined in tools/core/bench_result.h) to record per-batch latency statistics and generate summarized performance reports.
cd tools/core
./bench CONFIG.yaml // executes benchmark and prints latency percentiles
Code Coverage Analysis
For debugging test quality, the repository includes scripts/gcov.sh, which wraps lcov and genhtml to generate HTML coverage reports. The script filters out test and third-party sources to focus on core library coverage.
# After building with -fprofile-arcs -ftest-coverage
./scripts/gcov.sh -p zvec // generates html/coverage.html
Profiling Workflow Integration
To standardize performance investigations, zvec provides a GitHub issue template specifically for profiling reports (.github/ISSUE_TEMPLATE/profiling.yml). This template prompts contributors to document hardware specifications, profiling goals, and attach relevant benchmark outputs or timing logs, ensuring consistent context for performance-related bug reports.
End-to-End Profiling Example
The following example demonstrates wiring the profiler with the SQL engine, enabling debug logging, and retrieving a comprehensive timing report:
#include "zvec/db/common/profiler.h"
#include "zvec/db/sqlengine/sqlengine.h"
#include "zvec/ailego/logger/logger.h"
#include <iostream>
#include <memory>
int main() {
// Enable verbose debug output
zvec::ailego::LoggerBroker::SetLevel(zvec::ailego::Logger::LEVEL_DEBUG);
// Initialize and enable profiler
auto prof = std::make_shared<zvec::Profiler>(true);
prof->start(); // begin root timing stage
// Execute query with automatic stage instrumentation
auto engine = zvec::sqlengine::SQLEngine::create(prof);
engine->run("SELECT * FROM vectors WHERE id > 100");
prof->stop(); // finalize root stage
std::cout << prof->display(); // print timing breakdown
return 0;
}
Running this produces output similar to:
================================================================
analyze stage: 0.012345 s
message_to_sqlinfo: 0.003210 s
plan stage: 0.001234 s
================================================================
Summary
- Stage-Level Profiler: Use
zvec::Profilerinsrc/db/common/profiler.hwithopen_stage()/close_stage()to measure query pipeline segments, or employScopedLatencyfor automatic RAII-based timing. - Index Context Tracking: Leverage
zvec::core::ProfilerviaIndexContextinsrc/include/zvec/core/framework/index_context.hfor granular index operation metrics. - Debug Logging: Control verbose output dynamically using
LOG_DEBUGmacros andLoggerBroker::SetLevel()without recompiling. - Benchmarking: Run standardized performance tests using
tools/core/bench.ccwith YAML configurations to measure latency percentiles under concurrent load. - Coverage Analysis: Generate HTML coverage reports via
scripts/gcov.shto ensure test suite comprehensiveness.
Frequently Asked Questions
How do I enable profiling in a production zvec deployment?
Enable the profiler by passing true to the zvec::Profiler constructor and calling start() before query execution. The profiler adds minimal overhead since it only records high-level stage boundaries. For production systems, consider sampling or conditional compilation to disable profiling in hot paths, though the implementation in src/db/common/profiler.h is designed to be lightweight enough for continuous monitoring.
Can I add custom timing stages to my index implementation?
Yes. Inside your index code, access the profiler through the IndexContext pointer: context->profiler().add("my_custom_stage", elapsed_time). This integrates with the same display mechanism used by the SQL engine profiler, allowing your custom index timings to appear alongside standard query stage metrics.
What is the difference between zvec::Profiler and zvec::core::Profiler?
zvec::Profiler (defined in src/db/common/profiler.h) is the high-level, stage-based profiler used by the SQL engine to track query lifecycle phases. zvec::core::Profiler (defined in src/include/zvec/core/framework/index_context.h) is a simpler map-based struct designed for index developers to accumulate arbitrary timing key-value pairs during low-level vector operations like distance calculations or graph traversals.
How do I generate a code coverage report for my zvec modifications?
First, compile the project with GCC flags -fprofile-arcs -ftest-coverage. Then execute your test suite and run ./scripts/gcov.sh -p zvec. This generates an HTML report in html/coverage.html excluding third-party and test files, allowing you to identify untested code paths in your changes.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →