Skip to content

Commit

Permalink
Merge pull request #103 from r-devulap/customsort-expt
Browse files Browse the repository at this point in the history
Add API to sort array of custom objects
  • Loading branch information
r-devulap authored Nov 30, 2023
2 parents da8ce10 + fbc033e commit d9c9737
Show file tree
Hide file tree
Showing 4 changed files with 154 additions and 1 deletion.
1 change: 1 addition & 0 deletions benchmarks/bench-all.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
#include "bench-qselect.hpp"
#include "bench-qsort.hpp"
#include "bench-keyvalue.hpp"
#include "bench-objsort.hpp"
108 changes: 108 additions & 0 deletions benchmarks/bench-objsort.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
#include <cmath>

static constexpr char x[] = "x";
static constexpr char euclidean[] = "euclidean";
static constexpr char taxicab[] = "taxicab";
static constexpr char chebyshev[] = "chebyshev";

template <const char* val>
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 <typename T>
std::vector<T> init_data(const int size)
{
srand(42);
std::vector<T> arr;
for (auto ii = 0; ii < size; ++ii) {
T temp;
arr.push_back(temp);
}
return arr;
}

template <typename T>
struct less_than_key {
inline bool operator()(T &p1, T &p2)
{
return (p1.distance() < p2.distance());
}
};

template <typename T>
static void scalarobjsort(benchmark::State &state)
{
// set up array
std::vector<T> arr = init_data<T>(state.range(0));
std::vector<T> arr_bkp = arr;
// benchmark
for (auto _ : state) {
std::sort(arr.begin(), arr.end(), less_than_key<T>());
state.PauseTiming();
arr = arr_bkp;
state.ResumeTiming();
}
}

template <typename T>
static void simdobjsort(benchmark::State &state)
{
// set up array
std::vector<T> arr = init_data<T>(state.range(0));
std::vector<T> 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<T>())) {
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<x>)
BENCHMARK_OBJSORT(scalarobjsort, Point3D<x>)
BENCHMARK_OBJSORT(simdobjsort, Point3D<taxicab>)
BENCHMARK_OBJSORT(scalarobjsort, Point3D<taxicab>)
BENCHMARK_OBJSORT(simdobjsort, Point3D<euclidean>)
BENCHMARK_OBJSORT(scalarobjsort, Point3D<euclidean>)
BENCHMARK_OBJSORT(simdobjsort, Point3D<chebyshev>)
BENCHMARK_OBJSORT(scalarobjsort, Point3D<chebyshev>)
43 changes: 42 additions & 1 deletion lib/x86simdsort.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
#include <stdint.h>
#include <vector>
#include <cstddef>
#include <functional>
#include <numeric>

#define XSS_EXPORT_SYMBOL __attribute__((visibility("default")))
#define XSS_HIDE_SYMBOL __attribute__((visibility("hidden")))
Expand Down Expand Up @@ -34,10 +36,49 @@ template <typename T>
XSS_EXPORT_SYMBOL std::vector<size_t>
argselect(T *arr, size_t k, size_t arrsize, bool hasnan = false);

// argselect
// keyvalue sort
template <typename T1, typename T2>
XSS_EXPORT_SYMBOL void
keyvalue_qsort(T1 *key, T2* val, size_t arrsize, bool hasnan = false);

// sort an object
template <typename T, typename Func>
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<return_type_of> 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<size_t> arg(arrsize);
std::iota(arg.begin(), arg.end(), 0);
keyvalue_qsort(keys.data(), arg.data(), arrsize);

/* (3) Permute obj array in-place */
std::vector<bool> 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
3 changes: 3 additions & 0 deletions run-bench.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down

0 comments on commit d9c9737

Please sign in to comment.