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
9 changes: 6 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,18 @@ on:
pull_request:
branches: [ main ]

# Release builds: triggered by version tags (v1.0.0, v2.3.1, etc.)
# Same check on main, so the branch keeps a current status. Without this
# the README and profile badges stay frozen on whatever ran last.
push:
branches: [ main ]

# Release builds: triggered by version tags (v1.0.0, v2.3.1, etc.)
tags:
- 'v*'

jobs:
# ---------------------------------------------------------------
# Quick CI gate — runs on every PR to main
# Quick CI gate — runs on every PR to main and every push to main
# Builds the library and runs only fast unit tests (<2 min total).
# Benchmarks and long-running tests are SKIPPED here because
# GitHub's free runners are too slow / unreliable for them.
Expand Down Expand Up @@ -46,7 +50,6 @@ jobs:
- name: Run unit tests (benchmarks excluded)
run: |
cd build
ctest --output-on-failure --timeout 120 -L unit 2>/dev/null || \
ctest --output-on-failure --timeout 120
echo "See TESTING.md for info on skipped/disabled tests"

Expand Down
25 changes: 24 additions & 1 deletion TESTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## What runs in CI

CI runs on **pull requests to `main`** and **release tags** (`v*`).
CI runs on **pull requests to `main`**, **pushes to `main`** and **release tags** (`v*`).
Only fast unit tests execute in CI — the full suite finishes in under 2 minutes.

```
Expand Down Expand Up @@ -33,6 +33,29 @@ cd build
./bin/GenerativeModelsBenchmark
```

### Timing assertions (skipped unless asked for)

Five tests assert wall-clock thresholds:

| Test | Asserts |
|---|---|
| `Phase1SIMDTest.PerformanceTargetsValidation` | per-op time against a target in µs |
| `Phase6ProductionTest.ProductionPerformanceBenchmarks` | audio, time series, vision and text latency |
| `Phase6ProductionTest.ProductionDeploymentScenarios` | speech, IoT, edge and device-text latency |
| `Phase4SimpleTest.StreamingSimulation` | jitter, as max/min per-token time |
| `Phase7AdvancedAttentionTest.PerformanceBenchmarks` | attention latency and throughput |

On a shared runner these measure the runner. The correctness assertions in the
same tests always run; the timing ones are opt-in:

```bash
TINYML_PERF_ASSERTS=1 ctest --output-on-failure
```

The measured numbers print either way. The current targets do not hold on a
GitHub runner, and `Phase7AdvancedAttentionTest` throughput sits near its 25
tok/s line even on a loaded laptop, so treat them as goals rather than facts.

### Disabled tests (registered but skipped)

| Test | Why disabled |
Expand Down
24 changes: 24 additions & 0 deletions tests/perf_assert.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#pragma once

// Wall-clock thresholds on shared CI runners measure the runner, not the code.
// TESTING.md says this about the benchmark binaries; the same holds for the
// timing assertions embedded in these test files. They are skipped by default
// and enforced when you ask for them on hardware you control:
//
// TINYML_PERF_ASSERTS=1 ctest --output-on-failure
//
// The measured numbers still print either way, so a run remains readable.

#include <cstdlib>
#include <cstring>

namespace tinyml_test {

inline bool perf_asserts_enabled() {
const char* v = std::getenv("TINYML_PERF_ASSERTS");
return v != nullptr && *v != '\0' && std::strcmp(v, "0") != 0;
}

} // namespace tinyml_test

#define TINYML_IF_PERF_ASSERTS if (::tinyml_test::perf_asserts_enabled())
7 changes: 5 additions & 2 deletions tests/test_phase1_simd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*****************************************************************************/

#include <gtest/gtest.h>
#include "perf_assert.h"
#include <cmath>
#include <chrono>
#include <random>
Expand Down Expand Up @@ -209,8 +210,10 @@ TEST_F(Phase1SIMDTest, PerformanceTargetsValidation) {
<< std::setw(15) << std::fixed << std::setprecision(2) << avg_time
<< std::setw(12) << target_us << std::setw(10) << status << std::endl;

EXPECT_LE(avg_time, target_us)
<< name << " performance target not met: " << avg_time << "μs > " << target_us << "μs";
TINYML_IF_PERF_ASSERTS {
EXPECT_LE(avg_time, target_us)
<< name << " performance target not met: " << avg_time << "μs > " << target_us << "μs";
}
};

// Performance targets based on roadmap goals
Expand Down
5 changes: 4 additions & 1 deletion tests/test_phase4_simple.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*****************************************************************************/

#include <gtest/gtest.h>
#include "perf_assert.h"
#include <cmath>
#include <chrono>
#include <random>
Expand Down Expand Up @@ -322,7 +323,9 @@ TEST_F(Phase4SimpleTest, StreamingSimulation) {
std::cout << " Throughput: " << std::fixed << std::setprecision(1) << (1000000.0 / avg_time) << " tokens/sec\n";

// Streaming should be consistent
EXPECT_LT(max_time / min_time, 3.0) << "Processing times should be relatively consistent";
TINYML_IF_PERF_ASSERTS {
EXPECT_LT(max_time / min_time, 3.0) << "Processing times should be relatively consistent";
}

std::cout << "Streaming simulation: PASS\n";
}
Expand Down
21 changes: 13 additions & 8 deletions tests/test_phase6_production.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*****************************************************************************/

#include <gtest/gtest.h>
#include "perf_assert.h"
#include <cmath>
#include <chrono>
#include <random>
Expand Down Expand Up @@ -627,10 +628,12 @@ TEST_F(Phase6ProductionTest, ProductionPerformanceBenchmarks) {
std::cout << std::string(75, '-') << std::endl;

// Performance targets from roadmap
EXPECT_LT(audio_avg_ms, 10) << "Audio processing should be <10ms";
EXPECT_LT(ts_avg_ms, 10) << "Time series processing should be <10ms";
EXPECT_LT(vision_avg_ms, 100) << "Vision processing should be <100ms";
EXPECT_LT(text_avg_ms, 20) << "Text processing should be <20ms";
TINYML_IF_PERF_ASSERTS {
EXPECT_LT(audio_avg_ms, 10) << "Audio processing should be <10ms";
EXPECT_LT(ts_avg_ms, 10) << "Time series processing should be <10ms";
EXPECT_LT(vision_avg_ms, 100) << "Vision processing should be <100ms";
EXPECT_LT(text_avg_ms, 20) << "Text processing should be <20ms";
}
}

// Test Integration Points Robustness
Expand Down Expand Up @@ -763,8 +766,10 @@ TEST_F(Phase6ProductionTest, ProductionDeploymentScenarios) {
std::cout << std::string(80, '-') << std::endl;

// Verify deployment targets
EXPECT_LT(speech_avg_ms, 10) << "Speech enhancement should be <10ms for real-time";
EXPECT_LT(iot_avg_ms, 5) << "IoT analytics should be <5ms";
EXPECT_LT(edge_avg_ms, 100) << "Edge detection should be <100ms";
EXPECT_LT(text_avg_ms, 10) << "Device text processing should be <10ms";
TINYML_IF_PERF_ASSERTS {
EXPECT_LT(speech_avg_ms, 10) << "Speech enhancement should be <10ms for real-time";
EXPECT_LT(iot_avg_ms, 5) << "IoT analytics should be <5ms";
EXPECT_LT(edge_avg_ms, 100) << "Edge detection should be <100ms";
EXPECT_LT(text_avg_ms, 10) << "Device text processing should be <10ms";
}
}
7 changes: 5 additions & 2 deletions tests/test_phase7_advanced_attention.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*****************************************************************************/

#include <gtest/gtest.h>
#include "perf_assert.h"
#include <cmath>
#include <chrono>
#include <random>
Expand Down Expand Up @@ -377,9 +378,11 @@ TEST_F(Phase7AdvancedAttentionTest, PerformanceBenchmarks) {

// Verify performance targets
for (const auto& result : results) {
EXPECT_LT(result.latency_ms, 50.0f) << "Latency should be under 50ms for all attention types";
EXPECT_GT(result.throughput_tokens_per_sec, 25.0f) << "Throughput should be reasonable";
EXPECT_LT(result.memory_usage_mb, 50.0f) << "Memory usage should be under 50MB";
TINYML_IF_PERF_ASSERTS {
EXPECT_LT(result.latency_ms, 50.0f) << "Latency should be under 50ms for all attention types";
EXPECT_GT(result.throughput_tokens_per_sec, 25.0f) << "Throughput should be reasonable";
}
}

// Save results
Expand Down
Loading