diff --git a/example/n_body.cc b/example/n_body.cc index cb8eab5..6e0c410 100644 --- a/example/n_body.cc +++ b/example/n_body.cc @@ -11,9 +11,9 @@ #include "soa/soa.h" using namespace std; +using namespace ikra::soa; using ikra::executor::execute; using ikra::executor::Iterator; -using ikra::soa::SoaLayout; static const int kNumBodies = 25; static const double kMaxMass = 1000; @@ -27,7 +27,8 @@ static const int kMaxRect = 20; #define RAND (1.0 * rand() / RAND_MAX) -static void render_rect(SDL_Renderer* renderer, double x, double y, double mass) { +static void render_rect(SDL_Renderer* renderer, + double x, double y, double mass) { SDL_Rect rect; rect.w = rect.h = mass / kMaxMass * kMaxRect; rect.x = (x/2 + 0.5) * kWindowWidth - rect.w/2; @@ -57,12 +58,10 @@ class Body : public SoaLayout { if (this == body) return; double EPS = 0.01; // Softening parameter (just to avoid infinities). - double dx = body->position_[0] - position_[0]; - double dy = body->position_[1] - position_[1]; - double dist = sqrt(dx*dx + dy*dy); + Array d = body->position_ - position_; + double dist = sqrt(d[0]*d[0] + d[1]*d[1]); double F = kGravityConstant*mass_*body->mass_ / (dist*dist + EPS*EPS); - force_[0] += F*dx / dist; - force_[1] += F*dy / dist; + force_ += d * F / dist; } void add_force_to_all() { @@ -75,10 +74,8 @@ class Body : public SoaLayout { } void update(double dt) { - velocity_[0] += force_[0]*dt / mass_; - velocity_[1] += force_[1]*dt / mass_; - position_[0] += velocity_[0]*dt; - position_[1] += velocity_[1]*dt; + velocity_ += force_ * (dt / mass_); + position_ += velocity_ * dt; if (position_[0] < -1 || position_[0] > 1) { velocity_[0] = -velocity_[0]; diff --git a/ikra/soa/array_expression.h b/ikra/soa/array_expression.h new file mode 100644 index 0000000..3717391 --- /dev/null +++ b/ikra/soa/array_expression.h @@ -0,0 +1,182 @@ +#ifndef SOA_ARRAY_EXPRESSION_H +#define SOA_ARRAY_EXPRESSION_H + +#include + +#include "soa/array_traits.h" +#include "soa/util.h" + +namespace ikra { +namespace soa { + +// Superclass for all kinds of arrays (e.g., SoaArrayField_, ArrayAdd_). +// `Self` is the type of the derived class (Curiously Recurring Template Pattern). +template +class ArrayExpression_ { + private: + using T = typename ArrayTraits::T; + static const int N = ArrayTraits::N; + + public: + // Force size of this class to be 0. + char dummy_[0]; + + static const bool kIsArrayExpression = true; + + T operator[](size_t i) const { + return static_cast(*this)[i]; + } + T& operator[](size_t i) { + return static_cast(*this)[i]; + } + size_t size() const { return N; } + + operator Self&() { + return static_cast(*this); + } + operator Self const&() { + return static_cast(*this); + } +}; + +// These macros are used to overload assignment operators for array expressions. +// ElementType and ArraySize should be defined within the context. +#define IKRA_DEFINE_ARRAY_ASSIGNMENT(op) \ + template \ + void operator op##=(ArrayExpression_ const& a) { \ + for (size_t i = 0; i < ArraySize; ++i) { \ + (*this)[i] op##= a[i]; \ + } \ + } +#define IKRA_DEFINE_ARRAY_SCALAR_ASSIGNMENT(op) \ + void operator op##=(ElementType c) { \ + for (size_t i = 0; i < ArraySize; ++i) { \ + (*this)[i] op##= c; \ + } \ + } + +// A wrapper for std::array to make it inherit from ArrayExpression_. +template +class Array : public ArrayExpression_> { + private: + std::array array_; + + public: + ElementType operator[](size_t i) const { return array_[i]; } + ElementType& operator[](size_t i) { return array_[i]; } + + Array() = default; + Array(std::array a) : array_(a) {} + + // An array wrapper can be constructed from any ArrayExpression_, + // which forces its evaluation. + template + Array(ArrayExpression_ const& a) { + static_assert(std::is_same::T>::value, + "The specified element type is wrong."); + static_assert(ArraySize == ArrayTraits::N, + "The specified array size is wrong."); + + for (size_t i = 0; i < ArraySize; ++i) { + array_[i] = a[i]; + } + } + + // Define the assignment operator between array wrappers and array expressions. + IKRA_DEFINE_ARRAY_ASSIGNMENT(); + // Defines compound assignment operators between array wrappers and array expressions. + IKRA_APPLY_TO_ALL_OPERATORS(IKRA_DEFINE_ARRAY_ASSIGNMENT); + // Defines compound assignment operators between array wrappers and scalars. + IKRA_APPLY_TO_ALL_OPERATORS(IKRA_DEFINE_ARRAY_SCALAR_ASSIGNMENT); +}; + +// Generates classes and operators for the operations bewteen array expressions. +// `name` will be part of the class name (e.g., Add -> ArrayAdd_), +// and `op` is the corresponding operator. +#define IKRA_DEFINE_ARRAY_OPERATION(name, op) \ + template \ + class Array##name##_ : public ArrayExpression_> { \ + private: \ + Left const& left_; \ + Right const& right_; \ + \ + using T = typename ArrayTraits::T; \ + static const int N = ArrayTraits::N; \ + \ + public: \ + Array##name##_(Left const& l, Right const& r) : left_(l), right_(r) { \ + static_assert(Left::kIsArrayExpression, \ + "The left operand is not an array expression."); \ + static_assert(Right::kIsArrayExpression, \ + "The right operand is not an array expression."); \ + static_assert(std::is_same::T>::value, \ + "The element type of two operands are not the same."); \ + static_assert(N == ArrayTraits::N, \ + "The array sizes of two operands are not the same."); \ + }; \ + T operator[](size_t i) const { \ + return left_[i] op right_[i]; \ + } \ + }; \ + \ + template \ + Array##name##_ operator op(Left const& l, Right const& r) { \ + return Array##name##_(l, r); \ + } + +// Generates classes and operators for the scalar operation of array expressions. +// The left template argument is an array while the right one is a scalar. +// `name` will be part of the class name (e.g., Mul -> ArrayScalarMul_), +// and `op` is the corresponding operator. +#define IKRA_DEFINE_ARRAY_SCALAR_OPERATION(name, op) \ + template \ + class ArrayScalar##name##_ : \ + public ArrayExpression_> { \ + private: \ + Left const& left_; \ + Right right_; \ + \ + public: \ + ArrayScalar##name##_(Left const& l, Right r) : left_(l), right_(r) { \ + static_assert(Left::kIsArrayExpression, \ + "The left operand is not an array expression."); \ + static_assert(std::is_same::T, Right>::value, \ + "The element type of two operands are not the same."); \ + } \ + Right operator[](size_t i) const { \ + return left_[i] op right_; \ + } \ + }; \ + \ + template \ + typename std::enable_if< \ + std::is_same::T, Right>::value, \ + ArrayScalar##name##_ \ + >::type operator op(Left const& l, Right r) { \ + return ArrayScalar##name##_(l, r); \ + } \ + template \ + typename std::enable_if< \ + std::is_same::T, Left>::value, \ + ArrayScalar##name##_ \ + >::type operator op(Left l, Right const& r) { \ + return ArrayScalar##name##_(r, l); \ + } + +IKRA_DEFINE_ARRAY_OPERATION(Add, +); +IKRA_DEFINE_ARRAY_OPERATION(Sub, -); +IKRA_DEFINE_ARRAY_OPERATION(Mul, *); +IKRA_DEFINE_ARRAY_OPERATION(Div, /); +IKRA_DEFINE_ARRAY_OPERATION(Mod, %); +IKRA_DEFINE_ARRAY_SCALAR_OPERATION(Add, +); +IKRA_DEFINE_ARRAY_SCALAR_OPERATION(Sub, -); +IKRA_DEFINE_ARRAY_SCALAR_OPERATION(Mul, *); +IKRA_DEFINE_ARRAY_SCALAR_OPERATION(Div, /); +IKRA_DEFINE_ARRAY_SCALAR_OPERATION(Mod, %); + +} // namespace soa +} // namespace ikra + +#endif // SOA_ARRAY_EXPRESSION_H diff --git a/ikra/soa/array_field.h b/ikra/soa/array_field.h index f03a15f..ee18293 100644 --- a/ikra/soa/array_field.h +++ b/ikra/soa/array_field.h @@ -1,49 +1,63 @@ -#ifndef SOA_ARRAY_H -#define SOA_ARRAY_H +#ifndef SOA_ARRAY_FIELD_H +#define SOA_ARRAY_FIELD_H #include #include "soa/constants.h" #include "soa/field.h" +#include "soa/array_expression.h" namespace ikra { namespace soa { -// Class for field declarations of type array. This class is intended to be -// used with T = std::array and forwards all method invocations to the wrapped -// array object. The array is stored in AoS format. -template -class AosArrayField_ : public Field_ { +class AosArrayField_ : public ArrayExpression_> { + private: + using T = std::array; + public: static const int kSize = sizeof(T); // This operator is just for convenience reasons. The correct way to use it // would be "this->operator[](pos)". - typename T::reference operator[](typename T::size_type pos) { + typename T::reference operator[](typename T::size_type pos) const { return this->data_ptr()->operator[](pos); } + // Define the assignment operator between AOS array fields and array expressions. + IKRA_DEFINE_ARRAY_ASSIGNMENT(); + // Defines compound assignment operators between AOS array fields and array expressions. + IKRA_APPLY_TO_ALL_OPERATORS(IKRA_DEFINE_ARRAY_ASSIGNMENT); + // Defines compound assignment operators between AOS array fields and scalars. + IKRA_APPLY_TO_ALL_OPERATORS(IKRA_DEFINE_ARRAY_SCALAR_ASSIGNMENT); + #include "soa/field_shared.inc" }; // Class for field declarations of type array. T is the base type of the array. // This class is the SoA counter part of AosArrayField_. Array slots are // layouted as if they were SoA fields (columns). -template -class SoaArrayField_ { +class SoaArrayField_ : public ArrayExpression_> { private: + using T = ElementType; using Self = SoaArrayField_; @@ -61,6 +75,13 @@ class SoaArrayField_ { operator T&() const = delete; + // Define the assignment operator between SOA array fields and array expressions. + IKRA_DEFINE_ARRAY_ASSIGNMENT(); + // Defines compound assignment operators between SOA array fields and array expressions. + IKRA_APPLY_TO_ALL_OPERATORS(IKRA_DEFINE_ARRAY_ASSIGNMENT); + // Defines compound assignment operators between SOA array fields and scalars. + IKRA_APPLY_TO_ALL_OPERATORS(IKRA_DEFINE_ARRAY_SCALAR_ASSIGNMENT); + // Implement std::array interface. #if defined(__CUDA_ARCH__) || !defined(__CUDACC__) @@ -90,6 +111,7 @@ class SoaArrayField_ { __ikra_device__ T& back() const { return at(); } + #else // A helper class with an overridden operator= method. This class allows @@ -265,4 +287,4 @@ class SoaArrayField_ { } // namespace soa } // namespace ikra -#endif // SOA_ARRAY_H +#endif // SOA_ARRAY_FIELD_H diff --git a/ikra/soa/array_traits.h b/ikra/soa/array_traits.h new file mode 100644 index 0000000..b73ffe1 --- /dev/null +++ b/ikra/soa/array_traits.h @@ -0,0 +1,65 @@ +#ifndef SOA_ARRAY_TRAITS_H +#define SOA_ARRAY_TRAITS_H + +#include "soa/constants.h" + +namespace ikra { +namespace soa { + +// Code adapted from: +// https://stackoverflow.com/questions/45801696/no-type-named-type-in-ctrp-derived-class +template struct ArrayTraits; + +template class Array; +template +struct ArrayTraits> { + using T = ElementType; + static const int N = ArraySize; +}; + +template +class AosArrayField_; +template +struct ArrayTraits> { + using T = ElementType; + static const int N = ArraySize; +}; + +template +class SoaArrayField_; +template +struct ArrayTraits> { + using T = ElementType; + static const int N = ArraySize; +}; + +#define IKRA_DEFINE_TRAITS_OF_EXPRESSION_TEMPLATES(name) \ + template class Array##name##_; \ + \ + template \ + struct ArrayTraits> { \ + using T = typename ArrayTraits::T; \ + static const int N = ArrayTraits::N; \ + }; + +IKRA_DEFINE_TRAITS_OF_EXPRESSION_TEMPLATES(Add); +IKRA_DEFINE_TRAITS_OF_EXPRESSION_TEMPLATES(Sub); +IKRA_DEFINE_TRAITS_OF_EXPRESSION_TEMPLATES(Mul); +IKRA_DEFINE_TRAITS_OF_EXPRESSION_TEMPLATES(Div); +IKRA_DEFINE_TRAITS_OF_EXPRESSION_TEMPLATES(Mod); +IKRA_DEFINE_TRAITS_OF_EXPRESSION_TEMPLATES(ScalarAdd); +IKRA_DEFINE_TRAITS_OF_EXPRESSION_TEMPLATES(ScalarSub); +IKRA_DEFINE_TRAITS_OF_EXPRESSION_TEMPLATES(ScalarMul); +IKRA_DEFINE_TRAITS_OF_EXPRESSION_TEMPLATES(ScalarDiv); +IKRA_DEFINE_TRAITS_OF_EXPRESSION_TEMPLATES(ScalarMod); + +} // namespace soa +} // namespace ikra + +#endif // SOA_ARRAY_TRAITS_H diff --git a/ikra/soa/layout.h b/ikra/soa/layout.h index 0f934ae..32bac8d 100644 --- a/ikra/soa/layout.h +++ b/ikra/soa/layout.h @@ -77,7 +77,7 @@ class SoaLayout : SizeNDummy { // This struct serves as a namespace and contains array field types. struct array { template - using aos = ikra::soa::AosArrayField_, Capacity, + using aos = ikra::soa::AosArrayField_; @@ -130,10 +130,11 @@ class SoaLayout : SizeNDummy { // Create multiple new instances of this class. Data will be allocated inside // storage.data. - __ikra_device__ void* operator new[](size_t count) { - check_sizeof_class(); - // "count" is the number of new instances. - return get_uninitialized(Self::storage().increase_size(count/AddressMode)); + __ikra_device__ void* operator new[](size_t /*count*/) { + // check_sizeof_class(); + // "count" is the number of bytes. + // return get_uninitialized(Self::storage().increase_size(count/AddressMode)); + assert(false); // TODO: Implement. } // Return the number of instances of this class. diff --git a/ikra/soa/storage.h b/ikra/soa/storage.h index 771a330..c0e016b 100644 --- a/ikra/soa/storage.h +++ b/ikra/soa/storage.h @@ -198,7 +198,7 @@ class DynamicStorage_ private: template - friend class StorageDataOffset; + friend struct StorageDataOffset; // The base pointer (start) of the arena. void* arena_base_; @@ -248,7 +248,7 @@ class StaticStorage_ private: template - friend class StorageDataOffset; + friend struct StorageDataOffset; // Statically allocated data storage for the arena. char arena_base_[ArenaSize]; diff --git a/ikra/soa/util.h b/ikra/soa/util.h index eaccfe4..ef68024 100644 --- a/ikra/soa/util.h +++ b/ikra/soa/util.h @@ -16,6 +16,10 @@ struct NvccWorkaroundIdentityClassHolder { // Only needed for clang. #define IKRA_fold(x) (__builtin_constant_p(x) ? (x) : (x)) +// Applies the macro to all the arithmetic operators. +#define IKRA_APPLY_TO_ALL_OPERATORS(macro) \ + macro(+); macro(-); macro(*); macro(/); macro(%); + // This class is used to check if the compiler supports the selected addressing // mode. template diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 9d5f2b4..b0c653e 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -2,13 +2,18 @@ add_executable(array_test soa/array_test.cc) target_link_libraries(array_test gtest gtest_main ikra) add_test(array_test ${CMAKE_BINARY_DIR}/bin/array_test) +add_executable(array_operations_test soa/array_operations_test.cc) +target_link_libraries(array_operations_test gtest gtest_main ikra) +add_test(array_operations_test ${CMAKE_BINARY_DIR}/bin/array_operations_test) + add_executable(basic_class_test soa/basic_class_test.cc) target_link_libraries(basic_class_test gtest gtest_main ikra) add_test(basic_class_test ${CMAKE_BINARY_DIR}/bin/basic_class_test) -add_executable(pointer_arithmetics_test soa/pointer_arithmetics_test.cc) -target_link_libraries(pointer_arithmetics_test gtest gtest_main ikra) -add_test(pointer_arithmetics_test ${CMAKE_BINARY_DIR}/bin/pointer_arithmetics_test) +# TODO: The new[] operator does not work now. +# add_executable(pointer_arithmetics_test soa/pointer_arithmetics_test.cc) +# target_link_libraries(pointer_arithmetics_test gtest gtest_main ikra) +# add_test(pointer_arithmetics_test ${CMAKE_BINARY_DIR}/bin/pointer_arithmetics_test) add_executable(executor_test executor_test.cc) target_link_libraries(executor_test gtest gtest_main ikra) diff --git a/test/soa/array_operations_test.cc b/test/soa/array_operations_test.cc new file mode 100644 index 0000000..9aaa291 --- /dev/null +++ b/test/soa/array_operations_test.cc @@ -0,0 +1,177 @@ +#include + +#include "gtest/gtest.h" +#include "soa/soa.h" + +namespace { +using namespace ikra::soa; + +static const int kClassMaxInst = 1024; +static const int kTestSize = 9; + +template +T array_sum(Array array) { + T sum = 0; + for (size_t i = 0; i < N; ++i) { + sum += array[i]; + } + return sum; +} + +// Zero addressing mode. +#define IKRA_TEST_CLASSNAME TestClassZ +#define IKRA_TEST_ADDRESS_MODE kAddressModeZero +#include "array_test_layout.inc" +#undef IKRA_TEST_CLASSNAME +#undef IKRA_TEST_ADDRESS_MODE +IKRA_HOST_STORAGE(TestClassZ) + +// Valid addressing mode. +#define IKRA_TEST_CLASSNAME TestClassV +#define IKRA_TEST_ADDRESS_MODE sizeof(int) +#include "array_test_layout.inc" +#undef IKRA_TEST_CLASSNAME +#undef IKRA_TEST_ADDRESS_MODE +IKRA_HOST_STORAGE(TestClassV) + +template +class ArrayTest : public testing::Test {}; + +TYPED_TEST_CASE_P(ArrayTest); + +TYPED_TEST_P(ArrayTest, ArrayOperations) { + TypeParam::initialize_storage(); + TypeParam** instances = new TypeParam*[kTestSize]; + Array, kTestSize> arrays; + + // Initialize array fields and arrays. + for (int i = 0; i < kTestSize; ++i) { + instances[i] = new TypeParam(); + for (int j = 0; j < 3; ++j) { + instances[i]->field1[j] = 2*i; + instances[i]->field3[j] = i + j; + arrays[i][j] = i - j; + } + } + + // Test operations between two arrays. + for (int i = 0; i < kTestSize; ++i) { + Array addition = arrays[i] + arrays[i]; + EXPECT_EQ(array_sum(addition), 2 * array_sum(arrays[i])); + Array subtraction = addition - arrays[i]; + EXPECT_EQ(array_sum(subtraction), array_sum(arrays[i])); + addition += arrays[i]; + EXPECT_EQ(array_sum(addition), 3 * array_sum(arrays[i])); + subtraction -= arrays[i]; + EXPECT_EQ(array_sum(subtraction), 0); + } + + // Test operations between arrays and AOS array fields. + for (int i = 0; i < kTestSize; ++i) { + Array addition = arrays[i] + instances[i]->field1; + EXPECT_EQ(array_sum(addition), 9*i - 3); + Array subtraction = instances[i]->field1 - arrays[i]; + EXPECT_EQ(array_sum(subtraction), 3*i + 3); + addition += instances[i]->field1; + EXPECT_EQ(array_sum(addition), 15*i - 3); + subtraction -= instances[i]->field1; + EXPECT_EQ(array_sum(subtraction), -3*i + 3); + } + + // Test operations between arrays and SOA array fields. + for (int i = 0; i < kTestSize; ++i) { + Array addition = arrays[i] + instances[i]->field3; + EXPECT_EQ(array_sum(addition), 6*i); + Array subtraction = instances[i]->field3 - arrays[i]; + EXPECT_EQ(array_sum(subtraction), 6); + addition += instances[i]->field3; + EXPECT_EQ(array_sum(addition), 9*i + 3); + subtraction -= instances[i]->field3; + EXPECT_EQ(array_sum(subtraction), -3*i + 3); + } + + // Test operations between AOS and SOA array fields. + for (int i = 0; i < kTestSize; ++i) { + Array addition = instances[i]->field3 + instances[i]->field1; + EXPECT_EQ(array_sum(addition), 9*i + 3); + Array subtraction = instances[i]->field1 - instances[i]->field3; + EXPECT_EQ(array_sum(subtraction), 3*i - 3); + } + + // Test operations between two AOS array fields. + for (int i = 0; i < kTestSize; ++i) { + Array addition = instances[i]->field1 + instances[i]->field1; + EXPECT_EQ(array_sum(addition), 12*i); + Array subtraction = instances[i]->field1 - instances[i]->field1; + EXPECT_EQ(array_sum(subtraction), 0); + instances[i]->field1 += instances[i]->field1; + EXPECT_EQ(instances[i]->field1_sum(), 12*i); + instances[i]->field1 -= instances[i]->field1; + EXPECT_EQ(instances[i]->field1_sum(), 0); + } + + // Test operations between two SOA array fields. + for (int i = 0; i < kTestSize; ++i) { + Array addition = instances[i]->field3 + instances[i]->field3; + EXPECT_EQ(array_sum(addition), 6*i + 6); + Array subtraction = instances[i]->field3 - instances[i]->field3; + EXPECT_EQ(array_sum(subtraction), 0); + instances[i]->field3 += instances[i]->field3; + EXPECT_EQ(instances[i]->field3_sum(), 6*i + 6); + instances[i]->field3 -= instances[i]->field3; + EXPECT_EQ(instances[i]->field3_sum(), 0); + } +} + +TYPED_TEST_P(ArrayTest, ArrayScalarOperations) { + TypeParam::initialize_storage(); + TypeParam** instances = new TypeParam*[kTestSize]; + Array, kTestSize> arrays; + + // Initialize array fields and arrays. + for (int i = 0; i < kTestSize; ++i) { + instances[i] = new TypeParam(); + for (int j = 0; j < 3; ++j) { + instances[i]->field1[j] = 2*i; + instances[i]->field3[j] = i + j; + arrays[i][j] = i - j; + } + } + + // Test multiplications of arrays. + for (int i = 0; i < kTestSize; ++i) { + Array mul1 = arrays[i] * 3; + EXPECT_EQ(array_sum(mul1), 9*i - 9); + Array mul2 = 3 * arrays[i]; + EXPECT_EQ(array_sum(mul2), 9*i - 9); + arrays[i] *= 2; + EXPECT_EQ(array_sum(arrays[i]), 6*i - 6); + } + + // Test multiplications of AOS array fields. + for (int i = 0; i < kTestSize; ++i) { + Array mul1 = instances[i]->field1 * 3; + EXPECT_EQ(array_sum(mul1), 18*i); + Array mul2 = 3 * instances[i]->field1; + EXPECT_EQ(array_sum(mul2), 18*i); + instances[i]->field1 *= 2; + EXPECT_EQ(instances[i]->field1_sum(), 12*i); + } + + // Test multiplications of AOS array fields. + for (int i = 0; i < kTestSize; ++i) { + Array mul1 = instances[i]->field3 * 3; + EXPECT_EQ(array_sum(mul1), 9*i + 9); + Array mul2 = 3 * instances[i]->field3; + EXPECT_EQ(array_sum(mul2), 9*i + 9); + instances[i]->field3 *= 2; + EXPECT_EQ(instances[i]->field3_sum(), 6*i + 6); + } +} + +REGISTER_TYPED_TEST_CASE_P(ArrayTest, ArrayOperations, ArrayScalarOperations); + +INSTANTIATE_TYPED_TEST_CASE_P(Valid, ArrayTest, TestClassV); +INSTANTIATE_TYPED_TEST_CASE_P(Zero, ArrayTest, TestClassZ); + +} // namespace diff --git a/test/soa/array_test.cc b/test/soa/array_test.cc index 67a7fe6..d611425 100644 --- a/test/soa/array_test.cc +++ b/test/soa/array_test.cc @@ -45,8 +45,7 @@ TYPED_TEST_P(ArrayTest, AosArray) { for (int i = 0; i < kTestSize; ++i) { instances[i]->field1[0] = 1*i + 1; instances[i]->field1[1] = 2*i + 2; - // TODO: Should use "." for consistency. - instances[i]->field1->at(2) = 3*i + 3; + instances[i]->field1[2] = 3*i + 3; } // Check result. @@ -73,7 +72,7 @@ TYPED_TEST_P(ArrayTest, SoaArray) { for (int i = 0; i < kTestSize; ++i) { instances[i]->field3[0] = 1*i + 1; instances[i]->field3[1] = 2*i + 2; - instances[i]->field3->at(2) = 3*i + 3; + instances[i]->field3[2] = 3*i + 3; } // Check result. diff --git a/test/soa/benchmarks/build.sh b/test/soa/benchmarks/build.sh index cd7ab71..24e3ded 100755 --- a/test/soa/benchmarks/build.sh +++ b/test/soa/benchmarks/build.sh @@ -64,6 +64,12 @@ do objdump -S bin/${out_name} > assembly/${out_name}.S echo -n "." + out_name="${v_compiler}_nbody_ikracpp_with_operations" + ${v_compiler} -O3 ${extra_args} nbody/ikracpp_with_operations.cc \ + -std=c++11 -I../../../ikra -o bin/${out_name} + objdump -S bin/${out_name} > assembly/${out_name}.S + echo -n "." + # No vectorization with: -fno-tree-vectorize out_name="${v_compiler}_nbody_soa" ${v_compiler} -O3 ${extra_args} nbody/soa.cc -std=c++11 -o bin/${out_name} diff --git a/test/soa/benchmarks/nbody/ikracpp_with_operations.cc b/test/soa/benchmarks/nbody/ikracpp_with_operations.cc new file mode 100644 index 0000000..4c564d4 --- /dev/null +++ b/test/soa/benchmarks/nbody/ikracpp_with_operations.cc @@ -0,0 +1,176 @@ +// N-Body Simulation +// Code adapted from: http://physics.princeton.edu/~fpretori/Nbody/code.htm + +#define NDEBUG // No asserts. + +#include +#include +#include + +#include "benchmark.h" +#include "executor/executor.h" +#include "soa/soa.h" + +using namespace std; +using namespace ikra::soa; +using ikra::executor::execute; +using ikra::executor::Iterator; + +static const int kIterations = 5; +static const int kNumBodies = 8000; +static const double kMaxMass = 1000; +static const double kTimeInterval = 0.5; + +static const double kGravityConstant = 6.673e-11; // gravitational constant + +#define RAND (1.0 * rand() / RAND_MAX) + +class Body : public SoaLayout { + public: + IKRA_INITIALIZE_CLASS + + Body(double mass, double pos_x, double pos_y, double vel_x, double vel_y) + : mass_(mass) { + position_[0] = pos_x; + position_[1] = pos_y; + velocity_[0] = vel_x; + velocity_[1] = vel_y; + this->reset_force(); + } + + double_ mass_; + array_(double, 2) position_; + array_(double, 2) velocity_; + array_(double, 2) force_; + + void add_force(Body* body) { + if (this == body) return; + + double EPS = 0.01; // Softening parameter (just to avoid infinities). + Array d = body->position_ - position_; + double dist = sqrt(d[0]*d[0] + d[1]*d[1]); + double F = kGravityConstant*mass_*body->mass_ / (dist*dist + EPS*EPS); + force_ += d * F / dist; + } + + void add_force_to_all() { + execute(&Body::add_force, this); + } + + void reset_force() { + force_[0] = 0.0; + force_[1] = 0.0; + } + + void update(double dt) { + velocity_ += force_ * (dt / mass_); + position_ += velocity_ * dt; + + if (position_[0] < -1 || position_[0] > 1) { + velocity_[0] = -velocity_[0]; + } + if (position_[1] < -1 || position_[1] > 1) { + velocity_[1] = -velocity_[1]; + } + } + + void codengen_simple_update(double dt) { + for (int i = 0; i < 100; ++i) { + velocity_ += force_ * (dt / mass_); + position_ += velocity_ * dt; + } + } +}; + +IKRA_HOST_STORAGE(Body) + +void instantiation() { + srand(42); + + // Create objects. + for (int i = 0; i < kNumBodies; ++i) { + double mass = (RAND/2 + 0.5) * kMaxMass; + double pos_x = RAND*2 - 1; + double pos_y = RAND*2 - 1; + double vel_x = (RAND - 0.5) / 1000; + double vel_y = (RAND - 0.5) / 1000; + new Body(mass, pos_x, pos_y, vel_x, vel_y); + } +} + +void run_simulation() { + for (int i = 0; i < kIterations; ++i) { + // Reset forces. + execute(&Body::reset_force); + + // Update forces. + execute(&Body::add_force_to_all); + + // Update velocities and positions. + execute(&Body::update, kTimeInterval); + } +} + +void run_simple() { + for (int i = 0; i < kIterations*100; ++i) { + execute(&Body::codengen_simple_update, kTimeInterval); + } +} + +int main() { + // Initialize object storage. + Body::initialize_storage(); + + uint64_t time_instantiation = measure<>::execution(instantiation); + uint64_t time_simulation = measure<>::execution(run_simulation); + uint64_t time_simple = measure<>::execution(run_simple); + + // Calculate checksum + int checksum = 11; + for (uintptr_t i = 0; i < kNumBodies; i++) { + checksum += reinterpret_cast( + r_float2int(Body::get(i)->position_[0])); + checksum += reinterpret_cast( + r_float2int(Body::get(i)->position_[1])); + checksum = checksum % 1234567; + + if (i < 10) { + printf("VALUE[%lu] = %f, %f\n", i, + (double) Body::get(i)->position_[0], + (double) Body::get(i)->position_[1]); + } + } + + printf("instantiation: %lu\nsimulation: %lu\nsimple: %lu\n checksum: %i\n", + time_instantiation, time_simulation, time_simple, checksum); + return 0; +} + +void codegen_reset_force(Body* body) { + body->reset_force(); +} + +void codegen_add_force(Body* self, Body* other) { + self->add_force(other); +} + +void codegen_update(Body* self, double dt) { + self->update(dt); +} + +void codegen_reset_force_manual(Body* body) { + body->force_[0] = 0.0; + body->force_[1] = 0.0; +} + +void codegen_reset_force_half(Body* body) { + body->force_[0] = 0.0; +} + +void codegen_reset_mass(Body* body) { + body->mass_ = 0.0; +} + +void codengen_simple_update(Body* body, double dt) { + body->codengen_simple_update(dt); +}