| 371 | // comparator dominates. Shows whether #4's 2× comparator calls flip the movement-bound win. |
| 372 | template <typename T> |
| 373 | void run_expensive_cmp(const char * type_name) { |
| 374 | const size_t sizes[] = { 10000, 100000 }; |
| 375 | const Pattern patterns[] = { Pattern::Random, Pattern::Dups, Pattern::Sorted, Pattern::Partial, Pattern::Reverse }; |
| 376 | std::printf("\n## EXPENSIVE comparator — %s (sizeof=%zu) — daslang-interp proxy (byte path)\n\n", type_name, sizeof(T)); |
| 377 | std::printf("| N | pattern | std::stable | #3 byte | #1 merge | #4 index | b/std | m/std | i/std |\n"); |
| 378 | std::printf("|--------|------------|------------:|----------:|----------:|----------:|------:|------:|------:|\n"); |
| 379 | for (size_t n : sizes) { |
| 380 | int iters = pick_iters(n) / 4 + 1; // expensive cmp → fewer iters |
| 381 | for (Pattern pat : patterns) { |
| 382 | auto data = make_pattern<T>(pat, n, 0xC0FFEEu); |
| 383 | double s_std = bench_one("std::stable_sort", pat, data, iters, |
| 384 | [](std::vector<T> & a) { std::stable_sort(a.begin(), a.end(), ExpensiveLess<T>()); }); |
| 385 | double d_b = bench_one("#3 byte", pat, data, iters, |
| 386 | [](std::vector<T> & a) { das_stable_sort_r(a.data(), a.size(), sizeof(T), key_less_expensive); }); |
| 387 | double d_m = bench_one("#1 merge", pat, data, iters, |
| 388 | [](std::vector<T> & a) { das_stable_merge_r(a.data(), a.size(), sizeof(T), key_less_expensive); }); |
| 389 | double d_i = bench_one("#4 index", pat, data, iters, |
| 390 | [](std::vector<T> & a) { das_stable_index_r(a.data(), a.size(), sizeof(T), key_less_expensive); }); |
| 391 | std::printf("| %-6zu | %-10s | %11.0f | %9.0f | %9.0f | %9.0f | %5.2f | %5.2f | %5.2f |\n", |
| 392 | n, pattern_name(pat), s_std, d_b, d_m, d_i, d_b/s_std, d_m/s_std, d_i/s_std); |
| 393 | } |
| 394 | } |
| 395 | } |
| 396 | |
| 397 | template <typename T> |
| 398 | void run_cmp_counts(const char * type_name) { |
nothing calls this directly
no test coverage detected