Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "agentic/cpp"]
path = agentic/cpp
url = https://github.com/Malkovsky/ai_for_cpp.git
58 changes: 53 additions & 5 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ When a task matches a skill, read:
### Project Layout Conventions

- **`include/`**: Header-only library API (all implementations here, no `.cpp` files)
- **`include/misc/`**: Naive reference implementations for differential testing
- **`include/reference_implementations/`**: Naive reference implementations for differential testing
- **`src/*_tests.cpp`**: Unit tests (Google Test)
- **`src/*_benchmarks.cpp`**: Performance benchmarks (Google Benchmark)
- **`src/docs/`**: Doxygen configuration
Expand All @@ -36,6 +36,11 @@ When a task matches a skill, read:
3. **SIMD conditional compilation**: Uses `#ifdef PIXIE_AVX512_SUPPORT` / `PIXIE_AVX2_SUPPORT` with scalar fallbacks.
4. **Target domain**: Optimized for data sizes up to 2^64 bits.
5. **Platform**: Linux/Unix is the primary target platform.
6. **Interval convention**: Public APIs, tests, and benchmarks use C++-style
half-open ranges `[left, right)`: `left` is included and `right` is excluded.
Empty ranges (`left == right`) are invalid unless an API explicitly documents
otherwise. Low-level primitives may still use their own documented interval
conventions.

### Why Header-Only?

Expand Down Expand Up @@ -113,9 +118,10 @@ cmake --build build/release -j
### Running Tests

```bash
cmake-build/release/unittests # BitVector tests
cmake-build/release/test_rmm # RmM Tree tests
cmake-build/release/louds_tree_tests # LOUDS Tree tests
./build/release/unittests # BitVector tests
./build/release/test_rmm # RmM Tree tests
./build/release/rmq_tests # RMQ tests
./build/release/louds_tree_tests # LOUDS Tree tests
```

### Test Configuration via Environment Variables
Expand All @@ -128,9 +134,51 @@ For `test_rmm.cpp`:

### Testing Patterns

- **Differential testing**: Compare against naive reference implementations in `include/misc/`
- **Differential testing**: Compare against naive reference implementations in `include/reference_implementations/`
- **Randomized testing**: Random bit vectors and balanced parentheses sequences
- **Exhaustive short inputs**: Test all patterns for small sizes
- **Shape-forcing tests**: For RMQ/RmM trees, deterministic tests that force
a specific traversal path are often more valuable than adding more random
cases. Target border correction, same-leaf paths, prefix/suffix leaf
selectors, top sparse-table hit/miss paths, partial last blocks, and
first-min tie cases.

### Coverage and Codecov

Use the repository script for local coverage:

```bash
./scripts/coverage_report.sh
```

This script configures the `coverage` preset, builds coverage targets, deletes
old `.gcda`/`.gcov` files, runs the test binaries, and writes
`build/coverage/coverage.txt`.

Coverage interpretation notes:

- The report is generated with `gcov -pb`, so Codecov receives branch
probabilities in addition to line hits. Codecov "partial" lines are lines
that executed but did not cover all branch paths, for example short-circuit
conditions, ternaries, loop exits, or exception edges.
- Do not compare Codecov's branch-aware percentage directly with a plain
line-only local summary. Codecov effectively penalizes partially covered
branch lines.
- After recompiling an instrumented target, stale `.gcda` files can produce
`overwriting an existing profile data with a different checksum`. Run
`./scripts/coverage_report.sh` or delete the coverage `.gcda` files before
trusting a local coverage run.
- Header-only templates can appear in multiple gcov translation-unit reports.
For headers such as `experimental/rmm_btree.h`, inspect the generated
`.gcov` file or Codecov aggregate rather than trusting the first `File ...`
block in `coverage.txt`.
- For RMQ/RmM coverage work, prioritize public behavior paths over artificial
internal coverage. Useful tests cover HybridBTree leaf selector variants,
internal border correction, top sparse-overlay hit/miss behavior,
CartesianHybridBTree BP-depth query paths, and RmMBTree forward/backward
search/minselect edge cases. Low-value gaps include allocator failure paths,
`length_error` paths that require impossible index sizes, and private
defensive `npos` branches that cannot be reached through a valid public API.

## Code Style Guidelines

Expand Down
36 changes: 36 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,28 @@ if(PIXIE_TESTS)
gtest
gtest_main
${PIXIE_DIAGNOSTICS_LIBS})

add_executable(excess_record_lows_tests
src/tests/excess_record_lows_tests.cpp)
target_include_directories(excess_record_lows_tests
PUBLIC include)
target_link_libraries(excess_record_lows_tests
gtest
gtest_main
${PIXIE_DIAGNOSTICS_LIBS})

add_executable(rmq_tests
src/tests/rmq_tests.cpp)
target_include_directories(rmq_tests
PUBLIC include)
target_link_libraries(rmq_tests
gtest
gtest_main
${PIXIE_DIAGNOSTICS_LIBS})
if(PIXIE_THIRD_PARTY_BACKENDS)
target_include_directories(rmq_tests
PRIVATE ${sdsl_lite_SOURCE_DIR}/include)
endif()
endif()

# ---------------------------------------------------------------------------
Expand Down Expand Up @@ -218,6 +240,20 @@ if(PIXIE_BENCHMARKS)
benchmark
${PIXIE_DIAGNOSTICS_LIBS})

add_executable(bench_rmq
src/benchmarks/bench_rmq.cpp)
target_include_directories(bench_rmq
PUBLIC include)
target_link_libraries(bench_rmq
benchmark
${PIXIE_DIAGNOSTICS_LIBS})
if(PIXIE_THIRD_PARTY_BACKENDS)
target_include_directories(bench_rmq
PRIVATE ${sdsl_lite_SOURCE_DIR}/include)
target_compile_definitions(bench_rmq
PRIVATE PIXIE_THIRD_PARTY_BENCHMARKS)
endif()

if(PIXIE_THIRD_PARTY_BACKENDS)
add_executable(bench_rmm_sdsl
src/benchmarks/bench_rmm_sdsl.cpp)
Expand Down
7 changes: 7 additions & 0 deletions CMakePresets.json
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@
"targets": [
"unittests",
"benchmark_tests",
"rmq_tests",
"test_rmm",
"louds_tree_tests",
"dfuds_tree_tests",
Expand All @@ -125,6 +126,7 @@
"targets": [
"unittests",
"benchmark_tests",
"rmq_tests",
"test_rmm",
"louds_tree_tests",
"dfuds_tree_tests",
Expand All @@ -138,6 +140,7 @@
"targets": [
"benchmarks",
"bench_rmm",
"bench_rmq",
"louds_tree_benchmarks",
"dfuds_tree_benchmarks",
"alignment_comparison",
Expand All @@ -152,6 +155,7 @@
"benchmarks",
"bench_rmm",
"bench_rmm_sdsl",
"bench_rmq",
"louds_tree_benchmarks",
"dfuds_tree_benchmarks",
"alignment_comparison",
Expand All @@ -165,6 +169,7 @@
"targets": [
"benchmarks",
"bench_rmm",
"bench_rmq",
"louds_tree_benchmarks",
"dfuds_tree_benchmarks",
"alignment_comparison",
Expand All @@ -186,6 +191,7 @@
"targets": [
"unittests",
"benchmark_tests",
"rmq_tests",
"test_rmm",
"louds_tree_tests",
"dfuds_tree_tests",
Expand All @@ -199,6 +205,7 @@
"targets": [
"unittests",
"benchmark_tests",
"rmq_tests",
"test_rmm",
"louds_tree_tests",
"dfuds_tree_tests",
Expand Down
116 changes: 116 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,122 @@ python3 scripts/excess_benchmark_table.py excess_positions.json -o src/docs/exce
Generated benchmark documentation can be written to `src/docs/benchmark_results.md`;
the documentation pipeline does not run benchmarks.

### Adding an RMQ Benchmark

Value RMQ implementations are benchmarked through the common CRTP interface in
`pixie::rmq::RmqBase`. To add a comparable backend, implement a non-owning index
that can be constructed from `std::span<const T>` and provides:

* `size_impl()`
* `arg_min_impl(left, right)` for half-open ranges `[left, right)`
* `value_at_impl(position)`

The public `size()`, `empty()`, `arg_min()`, and `range_min()` methods are then
provided by `RmqBase`. Ties should return the smaller original position.

Minimal example:

```cpp
#include <pixie/rmq/rmq_base.h>

#include <cstddef>
#include <functional>
#include <span>

namespace pixie::rmq {

template <class T, class Compare = std::less<T>>
class LinearRmq : public RmqBase<LinearRmq<T, Compare>, T> {
public:
using Self = LinearRmq<T, Compare>;
static constexpr std::size_t npos = RmqBase<Self, T>::npos;

explicit LinearRmq(std::span<const T> values, Compare compare = Compare())
: values_(values), compare_(compare) {}

std::size_t size_impl() const { return values_.size(); }

std::size_t arg_min_impl(std::size_t left, std::size_t right) const {
if (left >= right || right > values_.size()) {
return npos;
}
std::size_t best = left;
for (std::size_t i = left + 1; i < right; ++i) {
if (compare_(values_[i], values_[best])) {
best = i;
}
}
return best;
}

T value_at_impl(std::size_t position) const { return values_[position]; }

private:
std::span<const T> values_;
Compare compare_;
};

} // namespace pixie::rmq
```

Then add rows to `src/benchmarks/bench_rmq.cpp` in `register_benchmarks()`.
Use `run_value_rmq_build` for construction cost and `run_queries` for query
cost:

```cpp
benchmark::RegisterBenchmark(
"rmq_build_linear",
&run_value_rmq_build<pixie::rmq::LinearRmq<
std::int64_t, std::less<std::int64_t>>>)
->Arg(static_cast<std::int64_t>(size))
->Unit(benchmark::kMillisecond);

benchmark::RegisterBenchmark(
"rmq_linear",
&run_queries<pixie::rmq::LinearRmq<
std::int64_t, std::less<std::int64_t>>>)
->Args({static_cast<std::int64_t>(size),
static_cast<std::int64_t>(width)})
->Unit(benchmark::kNanosecond);
```

The RMQ benchmark harness rotates through several value arrays so results are
less dependent on the global-minimum position. The `width` argument is the
maximum query width, not an exact width.

To compare the new backend with `HybridBTree`, run both benchmark families
with a Google Benchmark filter. For example, after registering the new backend
as `rmq_linear` and `rmq_build_linear`:

```sh
./build/release/bench_rmq \
--benchmark_filter='^(rmq_linear|rmq_hybrid_btree)/(4194304|16777216)/(64|4096|262144|4194304|16777216)$'

./build/release/bench_rmq \
--benchmark_filter='^(rmq_build_linear|rmq_build_hybrid_btree)/(262144|4194304|16777216)$'
```

The first command compares query time for `2^22` and `2^24` input sizes across
the common RMQ widths. The second command compares construction time for the
same implementations.

For hardware counters, use the diagnostic preset, which builds Google Benchmark
with libpfm support:

```sh
cmake --preset benchmarks-diagnostic
cmake --build --preset benchmarks-diagnostic -j

./build/release-with-deb/bench_rmq \
--benchmark_filter='rmq_cartesian_hybrid_btree/67108864/4096' \
--benchmark_perf_counters=CYCLES,INSTRUCTIONS,CACHE-MISSES \
--benchmark_counters_tabular=true
```

Counter names are platform/libpfm dependent. Google Benchmark pauses timing and
perf counters during `state.PauseTiming()`, so RMQ dataset-variant rebuilds are
excluded from query counter rows.

### RmM Tree

```sh
Expand Down
1 change: 1 addition & 0 deletions agentic/cpp
Submodule cpp added at 12a959
15 changes: 0 additions & 15 deletions agentic/cpp/README.md

This file was deleted.

6 changes: 0 additions & 6 deletions agentic/cpp/commands/README.md

This file was deleted.

34 changes: 0 additions & 34 deletions agentic/cpp/commands/benchmarks-affected.md

This file was deleted.

Loading