diff --git a/benchmarks/bench-all.cpp b/benchmarks/bench-all.cpp index 4862cb9f..11ed5082 100644 --- a/benchmarks/bench-all.cpp +++ b/benchmarks/bench-all.cpp @@ -4,3 +4,4 @@ #include "bench-qselect.hpp" #include "bench-qsort.hpp" #include "bench-keyvalue.hpp" +#include "bench-objsort.hpp" diff --git a/benchmarks/bench-objsort.hpp b/benchmarks/bench-objsort.hpp new file mode 100644 index 00000000..d2f15990 --- /dev/null +++ b/benchmarks/bench-objsort.hpp @@ -0,0 +1,108 @@ +#include + +static constexpr char x[] = "x"; +static constexpr char euclidean[] = "euclidean"; +static constexpr char taxicab[] = "taxicab"; +static constexpr char chebyshev[] = "chebyshev"; + +template +struct Point3D { + double x; + double y; + double z; + static constexpr std::string_view name {val}; + Point3D() + { + x = (double)rand() / RAND_MAX; + y = (double)rand() / RAND_MAX; + z = (double)rand() / RAND_MAX; + } + double distance() + { + if constexpr (name == "x") { + return x; + } + else if constexpr (name == "euclidean") { + return std::sqrt(x * x + y * y + z * z); + } + else if constexpr (name == "taxicab") { + return abs(x) + abs(y) + abs(z); + } + else if constexpr (name == "chebyshev") { + return std::max(std::max(x, y), z); + } + } +}; + +template +std::vector init_data(const int size) +{ + srand(42); + std::vector arr; + for (auto ii = 0; ii < size; ++ii) { + T temp; + arr.push_back(temp); + } + return arr; +} + +template +struct less_than_key { + inline bool operator()(T &p1, T &p2) + { + return (p1.distance() < p2.distance()); + } +}; + +template +static void scalarobjsort(benchmark::State &state) +{ + // set up array + std::vector arr = init_data(state.range(0)); + std::vector arr_bkp = arr; + // benchmark + for (auto _ : state) { + std::sort(arr.begin(), arr.end(), less_than_key()); + state.PauseTiming(); + arr = arr_bkp; + state.ResumeTiming(); + } +} + +template +static void simdobjsort(benchmark::State &state) +{ + // set up array + std::vector arr = init_data(state.range(0)); + std::vector arr_bkp = arr; + // benchmark + for (auto _ : state) { + x86simdsort::object_qsort(arr.data(), arr.size(), [](T p) -> double { + return p.distance(); + }); + state.PauseTiming(); + if (!std::is_sorted(arr.begin(), arr.end(), less_than_key())) { + std::cout << "sorting failed \n"; + } + arr = arr_bkp; + state.ResumeTiming(); + } +} + +#define BENCHMARK_OBJSORT(func, T) \ + BENCHMARK_TEMPLATE(func, T) \ + ->Arg(10e1) \ + ->Arg(10e2) \ + ->Arg(10e3) \ + ->Arg(10e4) \ + ->Arg(10e5) \ + ->Arg(10e6); + +BENCHMARK_OBJSORT(simdobjsort, Point3D) +BENCHMARK_OBJSORT(scalarobjsort, Point3D) +BENCHMARK_OBJSORT(simdobjsort, Point3D) +BENCHMARK_OBJSORT(scalarobjsort, Point3D) +BENCHMARK_OBJSORT(simdobjsort, Point3D) +BENCHMARK_OBJSORT(scalarobjsort, Point3D) +BENCHMARK_OBJSORT(simdobjsort, Point3D) +BENCHMARK_OBJSORT(scalarobjsort, Point3D) diff --git a/lib/x86simdsort.h b/lib/x86simdsort.h index 907ba43d..e5887bb0 100644 --- a/lib/x86simdsort.h +++ b/lib/x86simdsort.h @@ -3,6 +3,8 @@ #include #include #include +#include +#include #define XSS_EXPORT_SYMBOL __attribute__((visibility("default"))) #define XSS_HIDE_SYMBOL __attribute__((visibility("hidden"))) @@ -34,10 +36,49 @@ template XSS_EXPORT_SYMBOL std::vector argselect(T *arr, size_t k, size_t arrsize, bool hasnan = false); -// argselect +// keyvalue sort template XSS_EXPORT_SYMBOL void keyvalue_qsort(T1 *key, T2* val, size_t arrsize, bool hasnan = false); +// sort an object +template +XSS_EXPORT_SYMBOL void object_qsort(T *arr, size_t arrsize, Func key_func) +{ + /* (1) Create a vector a keys */ + using return_type_of = + typename decltype(std::function {key_func})::result_type; + std::vector keys; + keys.reserve(arrsize); + for (size_t ii = 0; ii < arrsize; ++ii) { + keys[ii] = key_func(arr[ii]); + } + + /* (2) Call arg based on keys using the keyvalue sort */ + std::vector arg(arrsize); + std::iota(arg.begin(), arg.end(), 0); + keyvalue_qsort(keys.data(), arg.data(), arrsize); + + /* (3) Permute obj array in-place */ + std::vector done(arrsize); + for (size_t i = 0; i < arrsize; ++i) + { + if (done[i]) + { + continue; + } + done[i] = true; + size_t prev_j = i; + size_t j = arg[i]; + while (i != j) + { + std::swap(arr[prev_j], arr[j]); + done[j] = true; + prev_j = j; + j = arg[j]; + } + } +} + } // namespace x86simdsort #endif diff --git a/run-bench.py b/run-bench.py index 3ea27812..d18dfd64 100644 --- a/run-bench.py +++ b/run-bench.py @@ -37,6 +37,9 @@ elif "keyvalue" in args.benchcompare: baseline = "scalarkvsort.*" + filterb contender = "simdkvsort.*" + filterb + elif "objsort" in args.benchcompare: + baseline = "scalarobjsort.*" + filterb + contender = "simdobjsort.*" + filterb else: parser.print_help(sys.stderr) parser.error("ERROR: Unknown argument '%s'" % args.benchcompare)