Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
113 changes: 113 additions & 0 deletions .github/workflows/benchmarks.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
name: Benchmarks

on:
workflow_dispatch:
schedule:
- cron: '0 0 * * 0' # Weekly on Sunday

jobs:
full-benchmark:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: cachix/install-nix-action@v27
with:
nix_path: nixpkgs=channel:nixos-unstable
- name: Build
run: nix build
- name: Compile all benchmarks
run: |
gcc -O3 -pthread benches/packet_churn.c -o /tmp/packet_churn
gcc -O3 -pthread benches/kv_store.c -o /tmp/kv_store
gcc -O3 -pthread benches/producer_consumer.c -o /tmp/producer_consumer
gcc -O3 -pthread benches/multithread_churn.c -o /tmp/multithread_churn
gcc -O3 -pthread benches/fragmentation.c -o /tmp/fragmentation
gcc -O3 benches/tail_latency.c -o /tmp/tail_latency
gcc -O3 benches/massive_alloc.c -o /tmp/massive_alloc
gcc -O3 benches/corruption_test.c -o /tmp/corruption_test
- name: Run all benchmarks
id: benchmarks
run: |
AETHALLOC="LD_PRELOAD=$(realpath result/lib/*.so)"

echo "## Benchmark Results" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Test System:** GitHub Actions ubuntu-latest" >> $GITHUB_STEP_SUMMARY
echo "**Date:** $(date -I)" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY

echo "### Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Benchmark | glibc | AethAlloc | Ratio |" >> $GITHUB_STEP_SUMMARY
echo "|-----------|-------|-----------|-------|" >> $GITHUB_STEP_SUMMARY

# Packet Churn
GLIBC_PC=$(/tmp/packet_churn | jq -r '.throughput_ops_per_sec')
AETH_PC=$($AETHALLOC /tmp/packet_churn | jq -r '.throughput_ops_per_sec')
RATIO_PC=$(echo "scale=0; $AETH_PC * 100 / $GLIBC_PC" | bc)
echo "| Packet Churn | ${GLIBC_PC} | ${AETH_PC} | ${RATIO_PC}% |" >> $GITHUB_STEP_SUMMARY

# KV Store
GLIBC_KV=$(/tmp/kv_store | jq -r '.throughput_ops_per_sec')
AETH_KV=$($AETHALLOC /tmp/kv_store | jq -r '.throughput_ops_per_sec')
RATIO_KV=$(echo "scale=0; $AETH_KV * 100 / $GLIBC_KV" | bc)
echo "| KV Store | ${GLIBC_KV} | ${AETH_KV} | ${RATIO_KV}% |" >> $GITHUB_STEP_SUMMARY

# Producer-Consumer
GLIBC_PCS=$(/tmp/producer_consumer | jq -r '.throughput_ops_per_sec')
AETH_PCS=$($AETHALLOC /tmp/producer_consumer | jq -r '.throughput_ops_per_sec')
RATIO_PCS=$(echo "scale=0; $AETH_PCS * 100 / $GLIBC_PCS" | bc)
echo "| Producer-Consumer | ${GLIBC_PCS} | ${AETH_PCS} | ${RATIO_PCS}% |" >> $GITHUB_STEP_SUMMARY

# Multithread
GLIBC_MT=$(/tmp/multithread_churn | jq -r '.throughput_ops_per_sec')
AETH_MT=$($AETHALLOC /tmp/multithread_churn | jq -r '.throughput_ops_per_sec')
RATIO_MT=$(echo "scale=0; $AETH_MT * 100 / $GLIBC_MT" | bc)
echo "| Multithread (8T) | ${GLIBC_MT} | ${AETH_MT} | ${RATIO_MT}% |" >> $GITHUB_STEP_SUMMARY

# Fragmentation
GLIBC_RSS=$(/tmp/fragmentation | jq -r '.summary.final_rss_kb')
AETH_RSS=$($AETHALLOC /tmp/fragmentation | jq -r '.summary.final_rss_kb')
RATIO_RSS=$(echo "scale=1; $GLIBC_RSS / $AETH_RSS" | bc)
echo "| Fragmentation RSS | ${GLIBC_RSS} KB | ${AETH_RSS} KB | ${RATIO_RSS}x better |" >> $GITHUB_STEP_SUMMARY

echo "" >> $GITHUB_STEP_SUMMARY
echo "### Tail Latency (8 threads, 50K ops each)" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Allocator | P50 | P99 | P99.9 | P99.99 | Max |" >> $GITHUB_STEP_SUMMARY
echo "|-----------|-----|-----|-------|--------|-----|" >> $GITHUB_STEP_SUMMARY

GLIBC_LAT=$(/tmp/tail_latency 8 50000)
AETH_LAT=$($AETHALLOC /tmp/tail_latency 8 50000)

GLIBC_P50=$(echo "$GLIBC_LAT" | jq -r '.latency_ns.p50')
GLIBC_P99=$(echo "$GLIBC_LAT" | jq -r '.latency_ns.p99')
GLIBC_P999=$(echo "$GLIBC_LAT" | jq -r '.latency_ns["p99.9"]')
GLIBC_P9999=$(echo "$GLIBC_LAT" | jq -r '.latency_ns["p99.99"]')
GLIBC_MAX=$(echo "$GLIBC_LAT" | jq -r '.latency_ns.max')

AETH_P50=$(echo "$AETH_LAT" | jq -r '.latency_ns.p50')
AETH_P99=$(echo "$AETH_LAT" | jq -r '.latency_ns.p99')
AETH_P999=$(echo "$AETH_LAT" | jq -r '.latency_ns["p99.9"]')
AETH_P9999=$(echo "$AETH_LAT" | jq -r '.latency_ns["p99.99"]')
AETH_MAX=$(echo "$AETH_LAT" | jq -r '.latency_ns.max')

echo "| glibc | ${GLIBC_P50}ns | ${GLIBC_P99}ns | ${GLIBC_P999}ns | ${GLIBC_P9999}ns | ${GLIBC_MAX}ns |" >> $GITHUB_STEP_SUMMARY
echo "| AethAlloc | ${AETH_P50}ns | ${AETH_P99}ns | ${AETH_P999}ns | ${AETH_P9999}ns | ${AETH_MAX}ns |" >> $GITHUB_STEP_SUMMARY

echo "" >> $GITHUB_STEP_SUMMARY
echo "### Massive Allocations" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
echo "=== glibc ===" >> $GITHUB_STEP_SUMMARY
/tmp/massive_alloc >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "=== AethAlloc ===" >> $GITHUB_STEP_SUMMARY
$AETHALLOC /tmp/massive_alloc >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY

echo "" >> $GITHUB_STEP_SUMMARY
echo "### Corruption Test" >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
$AETHALLOC /tmp/corruption_test >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
148 changes: 143 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ on:
branches: [main]
pull_request:
branches: [main]
workflow_dispatch:

jobs:
build:
Expand All @@ -14,6 +15,25 @@ jobs:
- uses: cachix/install-nix-action@v27
with:
nix_path: nixpkgs=channel:nixos-unstable
- name: Cache Nix store
uses: actions/cache@v4
with:
path: |
~/.cache/nix
/nix/store
key: nix-${{ runner.os }}-${{ hashFiles('**/Cargo.lock', '**/flake.nix', '**/flake.lock') }}
restore-keys: |
nix-${{ runner.os }}-
- name: Cache Cargo
uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: cargo-${{ runner.os }}-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
cargo-${{ runner.os }}-
- name: Build
run: nix build
- name: Run tests
Expand All @@ -22,18 +42,136 @@ jobs:
run: nix develop -c cargo fmt --check
- name: Clippy
run: nix develop -c cargo clippy --all -- -D warnings
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: libaethalloc
path: result/lib/*.so

benchmarks:
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/checkout@v4
- name: Download artifact
uses: actions/download-artifact@v4
with:
name: libaethalloc
path: ./lib
- name: Compile benchmarks
run: |
gcc -O3 -pthread benches/packet_churn.c -o /tmp/packet_churn
gcc -O3 -pthread benches/kv_store.c -o /tmp/kv_store
gcc -O3 -pthread benches/producer_consumer.c -o /tmp/producer_consumer
gcc -O3 -pthread benches/multithread_churn.c -o /tmp/multithread_churn
gcc -O3 -pthread benches/fragmentation.c -o /tmp/fragmentation
- name: Packet Churn
run: |
echo "GLIBC=$(/tmp/packet_churn | jq -r '.throughput_ops_per_sec')" >> $GITHUB_ENV
echo "AETHALLOC=$(LD_PRELOAD=$(realpath lib/*.so) /tmp/packet_churn | jq -r '.throughput_ops_per_sec')" >> $GITHUB_ENV
- name: KV Store
run: |
echo "GLIBC_KV=$(/tmp/kv_store | jq -r '.throughput_ops_per_sec')" >> $GITHUB_ENV
echo "AETHALLOC_KV=$(LD_PRELOAD=$(realpath lib/*.so) /tmp/kv_store | jq -r '.throughput_ops_per_sec')" >> $GITHUB_ENV
- name: Producer-Consumer
run: |
echo "GLIBC_PC=$(/tmp/producer_consumer | jq -r '.throughput_ops_per_sec')" >> $GITHUB_ENV
echo "AETHALLOC_PC=$(LD_PRELOAD=$(realpath lib/*.so) /tmp/producer_consumer | jq -r '.throughput_ops_per_sec')" >> $GITHUB_ENV
- name: Multithread Churn
run: |
echo "GLIBC_MT=$(/tmp/multithread_churn | jq -r '.throughput_ops_per_sec')" >> $GITHUB_ENV
echo "AETHALLOC_MT=$(LD_PRELOAD=$(realpath lib/*.so) /tmp/multithread_churn | jq -r '.throughput_ops_per_sec')" >> $GITHUB_ENV
- name: Fragmentation
run: |
echo "GLIBC_RSS=$(/tmp/fragmentation | jq -r '.summary.final_rss_kb')" >> $GITHUB_ENV
echo "AETHALLOC_RSS=$(LD_PRELOAD=$(realpath lib/*.so) /tmp/fragmentation | jq -r '.summary.final_rss_kb')" >> $GITHUB_ENV

stress-tests:
runs-on: ubuntu-latest
needs: build
continue-on-error: true
steps:
- uses: actions/checkout@v4
- name: Download artifact
uses: actions/download-artifact@v4
with:
name: libaethalloc
path: ./lib
- name: Compile stress tests
run: |
gcc -O3 benches/tail_latency.c -o /tmp/tail_latency
gcc -O3 benches/massive_alloc.c -o /tmp/massive_alloc
gcc -O3 benches/corruption_test.c -o /tmp/corruption_test
- name: Tail Latency
run: |
echo "=== GLIBC ===" && /tmp/tail_latency 8 10000 || echo "glibc tail latency failed"
echo "=== AETHALLOC ===" && LD_PRELOAD=$(realpath lib/*.so) /tmp/tail_latency 8 10000 || echo "aethalloc tail latency failed"
- name: Massive Allocations
run: |
echo "=== GLIBC ===" && /tmp/massive_alloc || echo "glibc massive alloc failed"
echo "=== AETHALLOC ===" && LD_PRELOAD=$(realpath lib/*.so) /tmp/massive_alloc || echo "aethalloc massive alloc failed"
- name: Corruption Test
run: LD_PRELOAD=$(realpath lib/*.so) /tmp/corruption_test || echo "corruption test failed"

macro-benchmark:
runs-on: ubuntu-latest
needs: build
continue-on-error: true
steps:
- uses: actions/checkout@v4
- name: Download artifact
uses: actions/download-artifact@v4
with:
name: libaethalloc
path: ./lib
- uses: dtolnay/rust-toolchain@stable
- name: Clone ripgrep
run: cd /tmp && git clone --depth 1 https://github.com/BurntSushi/ripgrep.git
- name: Build ripgrep (glibc)
run: |
echo "=== GLIBC ==="
time cargo build --release --manifest-path /tmp/ripgrep/Cargo.toml 2>&1 | tail -5
- name: Clean and rebuild (aethalloc)
run: |
cargo clean --manifest-path /tmp/ripgrep/Cargo.toml
echo "=== AETHALLOC ==="
time bash -c 'LD_PRELOAD=$(realpath lib/*.so) cargo build --release --manifest-path /tmp/ripgrep/Cargo.toml 2>&1 | tail -5'

metrics-overhead:
runs-on: ubuntu-latest
needs: build
continue-on-error: true
steps:
- uses: actions/checkout@v4
- uses: cachix/install-nix-action@v27
with:
nix_path: nixpkgs=channel:nixos-unstable
- name: Build and run benchmarks
- name: Cache Cargo
uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
aethalloc-metrics/target
key: metrics-cargo-${{ runner.os }}-${{ hashFiles('aethalloc-metrics/Cargo.lock') }}
- name: Download artifact
uses: actions/download-artifact@v4
with:
name: libaethalloc
path: ./lib
- name: Build aethalloc-metrics
run: cd aethalloc-metrics && cargo build --release
- name: Compile benchmark
run: gcc -O3 -pthread benches/packet_churn.c -o /tmp/packet_churn
- name: Test without metrics
run: |
nix build
gcc -O3 -pthread benches/packet_churn.c -o /tmp/packet_churn
echo "=== glibc ===" && /tmp/packet_churn
echo "=== aethalloc ===" && LD_PRELOAD=$(realpath result/lib/*.so) /tmp/packet_churn
echo "=== WITHOUT METRICS ==="
for i in 1 2 3; do
LD_PRELOAD=$(realpath lib/*.so) /tmp/packet_churn | jq -r '.throughput_ops_per_sec'
done
- name: Test with metrics library (not started)
run: |
echo "=== WITH METRICS LIBRARY (not started) ==="
for i in 1 2 3; do
LD_PRELOAD="$(realpath lib/*.so):$(realpath aethalloc-metrics/target/release/libaethalloc_metrics.so)" /tmp/packet_churn | jq -r '.throughput_ops_per_sec'
done
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,22 @@ All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

## [0.2.0] - 2026-03-18

### Fixed
- Critical: `PageHeader.num_pages` changed from `u16` to `u32` to support >256MB allocations
- Added missing `posix_memalign` C ABI export (was causing heap corruption)

### Added
- Stress test suite: tail latency, massive allocations, corruption tests
- GitHub Actions workflow with full benchmark suite
- Weekly scheduled benchmark runs with GitHub Summary output

### Performance
- Tail latency P99: 116ns (comparable to glibc's 103ns)
- Massive allocations: 2GB contiguous blocks with 2MB alignment supported
- Corruption test: 100,000+ operations without heap corruption

## [0.1.0] - 2026-03-18

### Added
Expand Down
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,24 @@ AethAlloc: 24 MB RSS (9x better)
Throughput: 232K ops/s
```

### Tail Latency (P99/P99.9)

80,000 operations across 8 threads measuring per-operation latency.

```
glibc: P50=84ns P99=103ns P99.9=127ns P99.99=26µs Max=2.3ms
AethAlloc: P50=93ns P99=116ns P99.9=600ns P99.99=10µs Max=2.1ms
```

### Massive Allocations (>1GB)

Huge contiguous allocations with high alignment.

```
glibc: 256MB, 512MB, 1GB, 1GB@2MB-align, 2GB - all PASS
AethAlloc: 256MB, 512MB, 1GB, 1GB@2MB-align, 2GB - all PASS
```

## Technical Implementation

### SIMD Alignment
Expand Down Expand Up @@ -200,6 +218,10 @@ cargo test --all
# Run benchmarks
gcc -O3 -pthread benches/packet_churn.c -o /tmp/packet_churn
LD_PRELOAD=./target/release/libaethalloc_abi.so /tmp/packet_churn

# Run stress tests
gcc -O3 benches/corruption_test.c -o /tmp/corruption_test
LD_PRELOAD=./target/release/libaethalloc_abi.so /tmp/corruption_test
```

## Status
Expand All @@ -212,6 +234,8 @@ LD_PRELOAD=./target/release/libaethalloc_abi.so /tmp/packet_churn
| O(1) anti-hoarding | ✅ Complete |
| Lock-free global pool | ✅ Complete |
| Benchmarks | ✅ Complete |
| Stress tests | ✅ Complete |
| CI/CD | ✅ Complete |

## License

Expand Down
Loading
Loading