diff --git a/README.md b/README.md index d14b8d0..c268a5e 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ # Fast-Vector -A Bacwords Compatible Vector Class, that's fast. \ No newline at end of file +## 1.07 times faster + +A Backwards Compatible Vector Class, that's fast. diff --git a/image.png b/image.png new file mode 100644 index 0000000..7906c5a Binary files /dev/null and b/image.png differ diff --git a/include/vector.h b/include/vector.h index a59d3a9..2359595 100644 --- a/include/vector.h +++ b/include/vector.h @@ -7,6 +7,17 @@ #include // malloc, realloc, free #include // std::bad_alloc + + + + + + + +//? Note: The vector is very inconsistant, but is on average %19 faster + + + // GCC/Clang helpers for inlining and cold paths #if defined(__GNUC__) #define UFV_ALWAYS_INLINE inline __attribute__((always_inline)) diff --git a/src/test.cpp b/src/test.cpp index 34be1d8..d37556c 100644 --- a/src/test.cpp +++ b/src/test.cpp @@ -3,19 +3,26 @@ #include #include #include +#include +#include #include "vector.h" using namespace std; using namespace std::chrono; -static constexpr size_t N = 100'000'000; -static constexpr int R = 3; +static constexpr size_t N = 100'000'000; +static constexpr int SHORT_RUNS = 3; +static constexpr int LONG_RUNS = 10; constexpr const char* COLOR_RESET = "\033[0m"; constexpr const char* COLOR_RED = "\033[31m"; constexpr const char* COLOR_GREEN = "\033[32m"; +bool longMode = false; +double total_speedup = 0; +int speedup_count = 0; + template double run_bench(F&& fn) { auto start = steady_clock::now(); @@ -24,29 +31,57 @@ double run_bench(F&& fn) { return duration_cast>(end - start).count(); } -template -double run_avg(F&& fn) { - double total = 0.0; - for (int i = 0; i < R; ++i) { - total += run_bench(fn); - } - return total / R; +struct Stats { + double mean{}, min{}, max{}, stddev{}; +}; + +Stats compute_stats(const std::vector& samples) { + Stats s{}; + if (samples.empty()) return s; + s.min = *min_element(samples.begin(), samples.end()); + s.max = *max_element(samples.begin(), samples.end()); + double sum = 0; + for (double v : samples) sum += v; + s.mean = sum / samples.size(); + for (double v : samples) s.stddev += (v - s.mean) * (v - s.mean); + s.stddev = sqrt(s.stddev / samples.size()); + return s; } -void print_test(const std::string& name, double std_t, double fast_t) { - double speedup = std_t / fast_t; - bool passed = fast_t < std_t; - const char* status_col = passed ? COLOR_GREEN : COLOR_RED; - const char* status_str = passed ? "[PASS]" : "[FAIL]"; +template +Stats run_all(F&& fn, int runs) { + std::vector results; + for (int i = 0; i < runs; ++i) + results.push_back(run_bench(fn)); + return compute_stats(results); +} - cout << status_col << status_str << COLOR_RESET - << ' ' << std::left << std::setw(27) << name - << " | std: " << std::right << std::setw(7) << fixed << setprecision(2) << std_t << "ms" - << " | fast: " << setw(7) << fast_t << "ms" - << " | x" << status_col << setw(5) << fixed << setprecision(2) << speedup << COLOR_RESET +void print_test(const std::string& name, Stats std_stats, Stats fast_stats) { + double speedup = std_stats.mean / fast_stats.mean; + bool passed = fast_stats.mean < std_stats.mean; + const char* color = passed ? COLOR_GREEN : COLOR_RED; + const char* status = passed ? "[PASS]" : "[FAIL]"; + + total_speedup += speedup; + ++speedup_count; + + cout << color << status << COLOR_RESET + << " " << left << setw(27) << name + << " | std: " << right << setw(7) << fixed << setprecision(2) << std_stats.mean << "ms" + << " ±" << setw(5) << std_stats.stddev + << " | fast: " << setw(7) << fast_stats.mean << "ms" + << " ±" << setw(5) << fast_stats.stddev + << " | x" << color << setw(6) << setprecision(2) << speedup << COLOR_RESET << '\n'; } +void run_test(const string& name, auto std_fn, auto fast_fn) { + int R = longMode ? LONG_RUNS : SHORT_RUNS; + auto std_stats = run_all(std_fn, R); + auto fast_stats = run_all(fast_fn, R); + print_test(name, std_stats, fast_stats); +} + struct S { int x, y; S(int a, int b): x(a), y(b) {} @@ -55,97 +90,98 @@ struct S { }; void TestIntPush_NoReserve() { - auto std_fn = []() { - vector v; - for (size_t i = 0; i < N; ++i) v.push_back(int(i)); - }; - auto fast_fn = []() { - Vector v; - for (size_t i = 0; i < N; ++i) v.push_back(int(i)); - }; - double t1 = run_avg(std_fn); - double t2 = run_avg(fast_fn); - print_test("IntPush_NoReserve", t1, t2); + run_test("IntPush_NoReserve", + [] { + vector v; + for (size_t i = 0; i < N; ++i) v.push_back(int(i)); + }, + [] { + Vector v; + for (size_t i = 0; i < N; ++i) v.push_back(int(i)); + }); } void TestIntPush_WithReserve() { - auto std_fn = []() { - vector v; v.reserve(N); - for (size_t i = 0; i < N; ++i) v.push_back(int(i)); - }; - auto fast_fn = []() { - Vector v; v.reserve(N); - for (size_t i = 0; i < N; ++i) v.push_back(int(i)); - }; - double t1 = run_avg(std_fn); - double t2 = run_avg(fast_fn); - print_test("IntPush_WithReserve", t1, t2); + run_test("IntPush_WithReserve", + [] { + vector v; v.reserve(N); + for (size_t i = 0; i < N; ++i) v.push_back(int(i)); + }, + [] { + Vector v; v.reserve(N); + for (size_t i = 0; i < N; ++i) v.push_back(int(i)); + }); } void TestIntEmplace_NoReserve() { - auto std_fn = []() { - vector v; - for (size_t i = 0; i < N; ++i) v.emplace_back(int(i)); - }; - auto fast_fn = []() { - Vector v; - for (size_t i = 0; i < N; ++i) v.emplace_back(int(i)); - }; - double t1 = run_avg(std_fn); - double t2 = run_avg(fast_fn); - print_test("IntEmplace_NoReserve", t1, t2); + run_test("IntEmplace_NoReserve", + [] { + vector v; + for (size_t i = 0; i < N; ++i) v.emplace_back(int(i)); + }, + [] { + Vector v; + for (size_t i = 0; i < N; ++i) v.emplace_back(int(i)); + }); } void TestIntEmplace_WithReserve() { - auto std_fn = []() { - vector v; v.reserve(N); - for (size_t i = 0; i < N; ++i) v.emplace_back(int(i)); - }; - auto fast_fn = []() { - Vector v; v.reserve(N); - for (size_t i = 0; i < N; ++i) v.emplace_back(int(i)); - }; - double t1 = run_avg(std_fn); - double t2 = run_avg(fast_fn); - print_test("IntEmplace_WithReserve", t1, t2); + run_test("IntEmplace_WithReserve", + [] { + vector v; v.reserve(N); + for (size_t i = 0; i < N; ++i) v.emplace_back(int(i)); + }, + [] { + Vector v; v.reserve(N); + for (size_t i = 0; i < N; ++i) v.emplace_back(int(i)); + }); } void TestNonTrivialPush_NoReserve() { - auto std_fn = []() { - vector v; - for (size_t i = 0; i < N/10; ++i) v.push_back(S(int(i), int(i))); - }; - auto fast_fn = []() { - Vector v; - for (size_t i = 0; i < N/10; ++i) v.push_back(S(int(i), int(i))); - }; - double t1 = run_avg(std_fn); - double t2 = run_avg(fast_fn); - print_test("NonTrivialPush_NoReserve", t1, t2); + run_test("NonTrivialPush_NoReserve", + [] { + vector v; + for (size_t i = 0; i < N / 10; ++i) v.push_back(S(int(i), int(i))); + }, + [] { + Vector v; + for (size_t i = 0; i < N / 10; ++i) v.push_back(S(int(i), int(i))); + }); } void TestNonTrivialPush_WithReserve() { - auto std_fn = []() { - vector v; v.reserve(N/10); - for (size_t i = 0; i < N/10; ++i) v.push_back(S(int(i), int(i))); - }; - auto fast_fn = []() { - Vector v; v.reserve(N/10); - for (size_t i = 0; i < N/10; ++i) v.push_back(S(int(i), int(i))); - }; - double t1 = run_avg(std_fn); - double t2 = run_avg(fast_fn); - print_test("NonTrivialPush_WithReserve", t1, t2); + run_test("NonTrivialPush_WithReserve", + [] { + vector v; v.reserve(N / 10); + for (size_t i = 0; i < N / 10; ++i) v.push_back(S(int(i), int(i))); + }, + [] { + Vector v; v.reserve(N / 10); + for (size_t i = 0; i < N / 10; ++i) v.push_back(S(int(i), int(i))); + }); } -int main() { +int main(int argc, char** argv) { + if (argc > 1 && std::string(argv[1]) == "-l") longMode = true; + + int R = longMode ? LONG_RUNS : SHORT_RUNS; cout << "\n=== Running Speed Tests (N=" << N << ", runs=" << R << ") ===\n\n"; + TestIntPush_NoReserve(); TestIntPush_WithReserve(); TestIntEmplace_NoReserve(); TestIntEmplace_WithReserve(); TestNonTrivialPush_NoReserve(); TestNonTrivialPush_WithReserve(); + + if (longMode) { + double avg_speedup = total_speedup / speedup_count; + cout << "\nOverall average speedup: " + << (avg_speedup > 1.0 ? COLOR_GREEN : COLOR_RED) + << "x" << fixed << setprecision(2) << avg_speedup + << COLOR_RESET << std::endl; + } + cout << "\n"; return 0; }