diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0d75ad4..12a50dd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,33 +1,54 @@ -name: CI +name: PipeSort CI on: push: branches: ["main"] pull_request: branches: ["main"] + workflow_dispatch: {} jobs: build-and-test: runs-on: ubuntu-latest steps: - - name: Checkout + - name: Checkout repository uses: actions/checkout@v4 - - name: Install deps + - name: Install build dependencies run: | sudo apt-get update - sudo apt-get install -y cmake ninja-build gcc clang + sudo apt-get install -y cmake ninja-build gcc - name: Configure - run: cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release + run: | + cmake -S . -B build \ + -G Ninja \ + -DCMAKE_BUILD_TYPE=Release - name: Build - run: cmake --build build + run: | + cmake --build build + + - name: Verify sorted output (correct list) + run: | + ./build/test_all 200000 123 | tee result.txt + + echo "Checking correctness..." + if grep -q "sorted: NO" result.txt; then + echo "❌ Sorting error detected" + exit 1 + fi + + if ! grep -q "sorted: YES" result.txt; then + echo "❌ No successful sort detected" + exit 1 + fi + + echo "✅ All lists sorted correctly" - - name: Run correctness (test_all) + - name: 5M benchmark vs qsort + if: github.event_name == 'workflow_dispatch' run: | - ./build/test_all 200000 123 | tee out.txt - # Fail if any sorter reports "sorted: NO" - ! grep -q "sorted: NO" out.txt + ./build/bench_u128_qsort 5000000 123 diff --git a/CMakeLists.txt b/CMakeLists.txt index c232298..f36ddbd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,6 +15,8 @@ add_library(pipesort_obj OBJECT internal/pipe_sort_u256_idx_radix8.c internal/pipe_sort_u512_idx_radix8.c ) +add_executable(bench_u128_qsort tests/bench_u128_qsort.c) +target_link_libraries(bench_u128_qsort PRIVATE pipesort) target_include_directories(pipesort_obj PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include @@ -36,3 +38,4 @@ install(TARGETS pipesort ) install(DIRECTORY include/pipesort/ DESTINATION include/pipesort FILES_MATCHING PATTERN "*.h") + diff --git a/tests/bench_u128_qsort.c b/tests/bench_u128_qsort.c new file mode 100644 index 0000000..f2e21c3 --- /dev/null +++ b/tests/bench_u128_qsort.c @@ -0,0 +1,90 @@ +#include +#include +#include +#include +#include + +#include "../include/pipesort/pipesort.h" // psort_u128_t, psort_u128, psort_u128_is_sorted + +static inline uint64_t xorshift64(uint64_t *s) { + uint64_t x = *s; + x ^= x << 13; + x ^= x >> 7; + x ^= x << 17; + *s = x; + return x; +} + +static int cmp_u128(const void *a, const void *b) { + const psort_u128_t *x = (const psort_u128_t *)a; + const psort_u128_t *y = (const psort_u128_t *)b; + if (x->hi < y->hi) return -1; + if (x->hi > y->hi) return 1; + if (x->lo < y->lo) return -1; + if (x->lo > y->lo) return 1; + return 0; +} + +static double now_sec(void) { + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return (double)ts.tv_sec + (double)ts.tv_nsec * 1e-9; +} + +int main(int argc, char **argv) { + int n = (argc > 1) ? atoi(argv[1]) : 5000000; + uint64_t seed = (argc > 2) ? (uint64_t)strtoull(argv[2], NULL, 10) : 123; + + if (n <= 0) { + fprintf(stderr, "n must be > 0\n"); + return 2; + } + + printf("bench_u128_qsort: n=%d seed=%llu\n", n, (unsigned long long)seed); + + psort_u128_t *a = (psort_u128_t *)malloc((size_t)n * sizeof(psort_u128_t)); + psort_u128_t *b = (psort_u128_t *)malloc((size_t)n * sizeof(psort_u128_t)); + if (!a || !b) { + fprintf(stderr, "malloc failed (n=%d)\n", n); + free(a); free(b); + return 2; + } + + // Generate data + uint64_t s = seed ? seed : 1; + for (int i = 0; i < n; i++) { + a[i].hi = xorshift64(&s); + a[i].lo = xorshift64(&s); + } + memcpy(b, a, (size_t)n * sizeof(psort_u128_t)); + + // qsort + double t0 = now_sec(); + qsort(a, (size_t)n, sizeof(psort_u128_t), cmp_u128); + double t1 = now_sec(); + + // psort_u128 + double t2 = now_sec(); + psort_u128(b, n); + double t3 = now_sec(); + + int ok_q = psort_u128_is_sorted(a, n); + int ok_p = psort_u128_is_sorted(b, n); + + printf("qsort time: %.6f s sorted: %s\n", (t1 - t0), ok_q ? "YES" : "NO"); + printf("psort_u128 time: %.6f s sorted: %s\n", (t3 - t2), ok_p ? "YES" : "NO"); + + if (!ok_q || !ok_p) { + fprintf(stderr, "ERROR: sort failed\n"); + free(a); free(b); + return 1; + } + + double speedup = (t1 - t0) / (t3 - t2); + printf("speedup (qsort/psort): %.3fx\n", speedup); + + free(a); + free(b); + return 0; +} +