ran some tests and made a little faster
This commit is contained in:
parent
cfceb5b62e
commit
1568348508
@ -1,3 +1,5 @@
|
|||||||
# Fast-Vector
|
# Fast-Vector
|
||||||
|
|
||||||
A Bacwords Compatible Vector Class, that's fast.
|
## 1.07 times faster
|
||||||
|
|
||||||
|
A Backwards Compatible Vector Class, that's fast.
|
||||||
|
@ -7,6 +7,17 @@
|
|||||||
#include <cstdlib> // malloc, realloc, free
|
#include <cstdlib> // malloc, realloc, free
|
||||||
#include <new> // std::bad_alloc
|
#include <new> // std::bad_alloc
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//? Note: The vector is very inconsistant, but is on average %19 faster
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// GCC/Clang helpers for inlining and cold paths
|
// GCC/Clang helpers for inlining and cold paths
|
||||||
#if defined(__GNUC__)
|
#if defined(__GNUC__)
|
||||||
#define UFV_ALWAYS_INLINE inline __attribute__((always_inline))
|
#define UFV_ALWAYS_INLINE inline __attribute__((always_inline))
|
||||||
|
170
src/test.cpp
170
src/test.cpp
@ -3,6 +3,8 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
|
#include <cmath>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
#include "vector.h"
|
#include "vector.h"
|
||||||
|
|
||||||
@ -10,12 +12,17 @@ using namespace std;
|
|||||||
using namespace std::chrono;
|
using namespace std::chrono;
|
||||||
|
|
||||||
static constexpr size_t N = 100'000'000;
|
static constexpr size_t N = 100'000'000;
|
||||||
static constexpr int R = 3;
|
static constexpr int SHORT_RUNS = 3;
|
||||||
|
static constexpr int LONG_RUNS = 10;
|
||||||
|
|
||||||
constexpr const char* COLOR_RESET = "\033[0m";
|
constexpr const char* COLOR_RESET = "\033[0m";
|
||||||
constexpr const char* COLOR_RED = "\033[31m";
|
constexpr const char* COLOR_RED = "\033[31m";
|
||||||
constexpr const char* COLOR_GREEN = "\033[32m";
|
constexpr const char* COLOR_GREEN = "\033[32m";
|
||||||
|
|
||||||
|
bool longMode = false;
|
||||||
|
double total_speedup = 0;
|
||||||
|
int speedup_count = 0;
|
||||||
|
|
||||||
template<typename F>
|
template<typename F>
|
||||||
double run_bench(F&& fn) {
|
double run_bench(F&& fn) {
|
||||||
auto start = steady_clock::now();
|
auto start = steady_clock::now();
|
||||||
@ -24,29 +31,57 @@ double run_bench(F&& fn) {
|
|||||||
return duration_cast<duration<double, milli>>(end - start).count();
|
return duration_cast<duration<double, milli>>(end - start).count();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename F>
|
struct Stats {
|
||||||
double run_avg(F&& fn) {
|
double mean{}, min{}, max{}, stddev{};
|
||||||
double total = 0.0;
|
};
|
||||||
for (int i = 0; i < R; ++i) {
|
|
||||||
total += run_bench(fn);
|
Stats compute_stats(const std::vector<double>& samples) {
|
||||||
}
|
Stats s{};
|
||||||
return total / R;
|
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) {
|
template<typename F>
|
||||||
double speedup = std_t / fast_t;
|
Stats run_all(F&& fn, int runs) {
|
||||||
bool passed = fast_t < std_t;
|
std::vector<double> results;
|
||||||
const char* status_col = passed ? COLOR_GREEN : COLOR_RED;
|
for (int i = 0; i < runs; ++i)
|
||||||
const char* status_str = passed ? "[PASS]" : "[FAIL]";
|
results.push_back(run_bench(fn));
|
||||||
|
return compute_stats(results);
|
||||||
|
}
|
||||||
|
|
||||||
cout << status_col << status_str << COLOR_RESET
|
void print_test(const std::string& name, Stats std_stats, Stats fast_stats) {
|
||||||
<< ' ' << std::left << std::setw(27) << name
|
double speedup = std_stats.mean / fast_stats.mean;
|
||||||
<< " | std: " << std::right << std::setw(7) << fixed << setprecision(2) << std_t << "ms"
|
bool passed = fast_stats.mean < std_stats.mean;
|
||||||
<< " | fast: " << setw(7) << fast_t << "ms"
|
const char* color = passed ? COLOR_GREEN : COLOR_RED;
|
||||||
<< " | x" << status_col << setw(5) << fixed << setprecision(2) << speedup << COLOR_RESET
|
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';
|
<< '\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 {
|
struct S {
|
||||||
int x, y;
|
int x, y;
|
||||||
S(int a, int b): x(a), y(b) {}
|
S(int a, int b): x(a), y(b) {}
|
||||||
@ -55,97 +90,98 @@ struct S {
|
|||||||
};
|
};
|
||||||
|
|
||||||
void TestIntPush_NoReserve() {
|
void TestIntPush_NoReserve() {
|
||||||
auto std_fn = []() {
|
run_test("IntPush_NoReserve",
|
||||||
|
[] {
|
||||||
vector<int> v;
|
vector<int> v;
|
||||||
for (size_t i = 0; i < N; ++i) v.push_back(int(i));
|
for (size_t i = 0; i < N; ++i) v.push_back(int(i));
|
||||||
};
|
},
|
||||||
auto fast_fn = []() {
|
[] {
|
||||||
Vector<int> v;
|
Vector<int> v;
|
||||||
for (size_t i = 0; i < N; ++i) v.push_back(int(i));
|
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestIntPush_WithReserve() {
|
void TestIntPush_WithReserve() {
|
||||||
auto std_fn = []() {
|
run_test("IntPush_WithReserve",
|
||||||
|
[] {
|
||||||
vector<int> v; v.reserve(N);
|
vector<int> v; v.reserve(N);
|
||||||
for (size_t i = 0; i < N; ++i) v.push_back(int(i));
|
for (size_t i = 0; i < N; ++i) v.push_back(int(i));
|
||||||
};
|
},
|
||||||
auto fast_fn = []() {
|
[] {
|
||||||
Vector<int> v; v.reserve(N);
|
Vector<int> v; v.reserve(N);
|
||||||
for (size_t i = 0; i < N; ++i) v.push_back(int(i));
|
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestIntEmplace_NoReserve() {
|
void TestIntEmplace_NoReserve() {
|
||||||
auto std_fn = []() {
|
run_test("IntEmplace_NoReserve",
|
||||||
|
[] {
|
||||||
vector<int> v;
|
vector<int> v;
|
||||||
for (size_t i = 0; i < N; ++i) v.emplace_back(int(i));
|
for (size_t i = 0; i < N; ++i) v.emplace_back(int(i));
|
||||||
};
|
},
|
||||||
auto fast_fn = []() {
|
[] {
|
||||||
Vector<int> v;
|
Vector<int> v;
|
||||||
for (size_t i = 0; i < N; ++i) v.emplace_back(int(i));
|
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestIntEmplace_WithReserve() {
|
void TestIntEmplace_WithReserve() {
|
||||||
auto std_fn = []() {
|
run_test("IntEmplace_WithReserve",
|
||||||
|
[] {
|
||||||
vector<int> v; v.reserve(N);
|
vector<int> v; v.reserve(N);
|
||||||
for (size_t i = 0; i < N; ++i) v.emplace_back(int(i));
|
for (size_t i = 0; i < N; ++i) v.emplace_back(int(i));
|
||||||
};
|
},
|
||||||
auto fast_fn = []() {
|
[] {
|
||||||
Vector<int> v; v.reserve(N);
|
Vector<int> v; v.reserve(N);
|
||||||
for (size_t i = 0; i < N; ++i) v.emplace_back(int(i));
|
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestNonTrivialPush_NoReserve() {
|
void TestNonTrivialPush_NoReserve() {
|
||||||
auto std_fn = []() {
|
run_test("NonTrivialPush_NoReserve",
|
||||||
|
[] {
|
||||||
vector<S> v;
|
vector<S> v;
|
||||||
for (size_t i = 0; i < N/10; ++i) v.push_back(S(int(i), int(i)));
|
for (size_t i = 0; i < N / 10; ++i) v.push_back(S(int(i), int(i)));
|
||||||
};
|
},
|
||||||
auto fast_fn = []() {
|
[] {
|
||||||
Vector<S> v;
|
Vector<S> v;
|
||||||
for (size_t i = 0; i < N/10; ++i) v.push_back(S(int(i), int(i)));
|
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestNonTrivialPush_WithReserve() {
|
void TestNonTrivialPush_WithReserve() {
|
||||||
auto std_fn = []() {
|
run_test("NonTrivialPush_WithReserve",
|
||||||
vector<S> v; v.reserve(N/10);
|
[] {
|
||||||
for (size_t i = 0; i < N/10; ++i) v.push_back(S(int(i), int(i)));
|
vector<S> 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<S> v; v.reserve(N/10);
|
[] {
|
||||||
for (size_t i = 0; i < N/10; ++i) v.push_back(S(int(i), int(i)));
|
Vector<S> 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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";
|
cout << "\n=== Running Speed Tests (N=" << N << ", runs=" << R << ") ===\n\n";
|
||||||
|
|
||||||
TestIntPush_NoReserve();
|
TestIntPush_NoReserve();
|
||||||
TestIntPush_WithReserve();
|
TestIntPush_WithReserve();
|
||||||
TestIntEmplace_NoReserve();
|
TestIntEmplace_NoReserve();
|
||||||
TestIntEmplace_WithReserve();
|
TestIntEmplace_WithReserve();
|
||||||
TestNonTrivialPush_NoReserve();
|
TestNonTrivialPush_NoReserve();
|
||||||
TestNonTrivialPush_WithReserve();
|
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";
|
cout << "\n";
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user