diff --git a/.gitignore b/.gitignore index a3f2e32..cd1edc0 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,8 @@ build* *~ *# +*.swp +*.swo # Eclipse CDT project files .project diff --git a/algorithms_cpp/CMakeLists.txt b/algorithms_cpp/CMakeLists.txt index 5378bd3..e7ea647 100644 --- a/algorithms_cpp/CMakeLists.txt +++ b/algorithms_cpp/CMakeLists.txt @@ -3,14 +3,16 @@ project (project_embb_algorithms) file(GLOB_RECURSE EMBB_ALGORITHMS_CPP_SOURCES "src/*.cc" "src/*.h") file(GLOB_RECURSE EMBB_ALGORITHMS_CPP_HEADERS "include/*.h") file(GLOB_RECURSE EMBB_ALGORITHMS_CPP_TEST_SOURCES "test/*.cc" "test/*.h") +file(GLOB_RECURSE EMBB_ALGORITHMS_CPP_PERF_SOURCES "perf/*.cc" "perf/*.h") # Execute the GroupSources macro include(${CMAKE_SOURCE_DIR}/CMakeCommon/GroupSourcesMSVC.cmake) GroupSourcesMSVC(include) GroupSourcesMSVC(src) GroupSourcesMSVC(test) +GroupSourcesMSVC(perf) -set (EMBB_ALGORITHMS_CPP_INCLUDE_DIRS "include" "src" "test") +set (EMBB_ALGORITHMS_CPP_INCLUDE_DIRS "include" "src" "test" "perf") include_directories(${EMBB_ALGORITHMS_CPP_INCLUDE_DIRS} ${CMAKE_CURRENT_SOURCE_DIR}/../base_c/include ${CMAKE_CURRENT_BINARY_DIR}/../base_c/include @@ -25,12 +27,21 @@ add_library(embb_algorithms_cpp ${EMBB_ALGORITHMS_CPP_SOURCES} target_link_libraries(embb_algorithms_cpp embb_tasks_cpp) if (BUILD_TESTS STREQUAL ON) + # Unit tests include_directories(${CMAKE_CURRENT_BINARY_DIR}/../partest/include) add_executable (embb_algorithms_cpp_test ${EMBB_ALGORITHMS_CPP_TEST_SOURCES}) target_link_libraries(embb_algorithms_cpp_test embb_algorithms_cpp embb_tasks_cpp embb_mtapi_c partest embb_base_cpp embb_base_c ${compiler_libs}) CopyBin(BIN embb_algorithms_cpp_test DEST ${local_install_dir}) + # Performance tests + include_directories(${CMAKE_CURRENT_BINARY_DIR}/../partest/include + ${CMAKE_CURRENT_SOURCE_DIR}/../base_cpp/perf/include) + add_executable (embb_algorithms_cpp_perf ${EMBB_ALGORITHMS_CPP_PERF_SOURCES}) + target_link_libraries(embb_algorithms_cpp_perf embb_algorithms_cpp + embb_tasks_cpp embb_mtapi_c partest embb_base_cpp + embb_base_c embb_base_cpp_perf ${compiler_libs}) + CopyBin(BIN embb_algorithms_cpp_perf DEST ${local_install_dir}) endif() install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/embb diff --git a/algorithms_cpp/perf/count_perf-inl.h b/algorithms_cpp/perf/count_perf-inl.h new file mode 100644 index 0000000..9f96321 --- /dev/null +++ b/algorithms_cpp/perf/count_perf-inl.h @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2014, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_ALGORITHMS_PERF_COUNT_PERF_INL_H_ +#define EMBB_ALGORITHMS_PERF_COUNT_PERF_INL_H_ + +#include +#include +#include +#include +#include +#include + +namespace embb { +namespace algorithms { +namespace perf { + +using embb::base::perf::CallArgs; + +template +SerialCount::SerialCount(const embb::base::perf::CallArgs & args) : +cargs(args), vector_size(args.VectorSize()), result(0) { + if (cargs.StressMode() == CallArgs::RAM_STRESS) { + a = (T *)Allocation::AllocateCacheAligned( + vector_size * sizeof(T)); + for (size_t i = 0; i < vector_size; i++) { + a[i] = static_cast(i); + } + } + else { + a = 0; + } +} + +template +SerialCount::~SerialCount() { + if (a != 0) { + Allocation::FreeAligned(a); + } +} + +template +void SerialCount::Run() { + T element = static_cast(vector_size / 2); + for (size_t i = 0; i != vector_size; ++i) { + if (a[i] == element) { + ++result; + } + } +} + +template +ParallelCount::ParallelCount(const embb::base::perf::CallArgs & args) : +cargs(args), vector_size(args.VectorSize()), result(0) { + if (cargs.StressMode() == CallArgs::RAM_STRESS) { + a = (T *)Allocation::AllocateCacheAligned( + vector_size * sizeof(T)); + for (size_t i = 0; i < vector_size; i++) { + a[i] = static_cast(i); + } + } + else { + a = 0; + } +} + +template +ParallelCount::~ParallelCount() { + if (a != 0) { + Allocation::FreeAligned(a); + } +} + +template +void ParallelCount::Run(unsigned int numThreads) { + T element = static_cast(vector_size / 2); + if (cargs.StressMode() == CallArgs::CPU_STRESS) { + result = static_cast(embb::algorithms::Count( + // Using iterator returning index value to avoid + // memory access + IndexIterator(0), + IndexIterator(static_cast(vector_size)), + element, + embb::tasks::ExecutionPolicy(), + vector_size / numThreads)); + } + else if (cargs.StressMode() == CallArgs::RAM_STRESS) { + result = static_cast(embb::algorithms::Count( + a, + a + vector_size, + element, + embb::tasks::ExecutionPolicy(), + vector_size / numThreads)); + } +} + +} // namespace perf +} // namespace algorithms +} // namespace embb + +#endif /* EMBB_ALGORITHMS_PERF_COUNT_PERF_INL_H_ */ diff --git a/algorithms_cpp/perf/count_perf.h b/algorithms_cpp/perf/count_perf.h new file mode 100644 index 0000000..f42246b --- /dev/null +++ b/algorithms_cpp/perf/count_perf.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2014, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_ALGORITHMS_CPP_PERF_COUNT_PERF_H_ +#define EMBB_ALGORITHMS_CPP_PERF_COUNT_PERF_H_ + +#include +#include + +namespace embb { +namespace algorithms { +namespace perf { + +template +class SerialCount { +public: + explicit SerialCount(const embb::base::perf::CallArgs & args); + ~SerialCount(); + void Pre() { } + void Run(); + void Post() { } + +private: + const embb::base::perf::CallArgs & cargs; + const size_t vector_size; + T * a; + size_t result; + /* prohibit copy and assignment */ + SerialCount(const SerialCount & other); + SerialCount & operator=(const SerialCount & other); +}; + +template +class ParallelCount { +public: + explicit ParallelCount(const embb::base::perf::CallArgs & args); + ~ParallelCount(); + void Pre() { } + void Run(unsigned int numThreads); + void Post() { } + +private: + const embb::base::perf::CallArgs & cargs; + const size_t vector_size; + T * a; + size_t result; + /* prohibit copy and assignment */ + ParallelCount(const ParallelCount & other); + ParallelCount & operator=(const ParallelCount & other); +}; + +} // namespace perf +} // namespace algorithms +} // namespace embb + +#include + +#endif /* EMBB_ALGORITHMS_CPP_PERF_COUNT_PERF_H_ */ diff --git a/algorithms_cpp/perf/for_each_perf-inl.h b/algorithms_cpp/perf/for_each_perf-inl.h new file mode 100644 index 0000000..4170cbe --- /dev/null +++ b/algorithms_cpp/perf/for_each_perf-inl.h @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2014, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_ALGORITHMS_PERF_FOR_EACH_PERF_INL_H_ +#define EMBB_ALGORITHMS_PERF_FOR_EACH_PERF_INL_H_ + +#include +#include +#include +#include +#include +#include + +namespace embb { +namespace algorithms { +namespace perf { + +template +SerialForEach::SerialForEach(const embb::base::perf::CallArgs & args) +: cargs(args), op(args), vector_size(args.VectorSize()) { + if (cargs.StressMode() == CallArgs::RAM_STRESS) { + v = static_cast(embb::base::Allocation::AllocateCacheAligned( + vector_size * sizeof(T))); + for (size_t i = 0; i < vector_size; i++) { + v[i] = static_cast(i); + } + } + else { + v = 0; + } +} + +template +SerialForEach::~SerialForEach() { + if (cargs.StressMode() == CallArgs::RAM_STRESS) { + embb::base::Allocation::FreeAligned(v); + } +} + +template +void SerialForEach::Run() { + if (cargs.StressMode() == CallArgs::CPU_STRESS) { + for (size_t i = 0; i < vector_size; i++) { + T v = static_cast(i); + op(v); + } + } + else if (cargs.StressMode() == CallArgs::RAM_STRESS) { + for (size_t i = 0; i < vector_size; i++) { + op(v[i]); + } + } +} + +template +ParallelForEach::ParallelForEach(const embb::base::perf::CallArgs & args) +: cargs(args), vector_size(args.VectorSize()) { + if (cargs.StressMode() == CallArgs::RAM_STRESS) { + v = static_cast(embb::base::Allocation::AllocateCacheAligned( + vector_size * sizeof(T))); + for (size_t i = 0; i < vector_size; i++) { + v[i] = static_cast(i); + } + } + else { + v = 0; + } +} + +template +ParallelForEach::~ParallelForEach() { + if (v != 0) { + embb::base::Allocation::FreeAligned(v); + } +} + +template +void ParallelForEach::Run(unsigned int numThreads) { + if (cargs.StressMode() == CallArgs::CPU_STRESS) { + // Computing input values, no memory access + ForEachFunctor op(cargs); + embb::algorithms::ForEach( + // Using iterator returning index value to avoid + // memory access + IndexIterator(0), + IndexIterator(static_cast(vector_size)), + op, + embb::tasks::ExecutionPolicy(), + vector_size / numThreads); + } else if (cargs.StressMode() == CallArgs::RAM_STRESS) { + // Reading input values from memory + ForEachFunctor op(cargs); + embb::algorithms::ForEach( + v, v + vector_size, + op, + embb::tasks::ExecutionPolicy(), + vector_size / numThreads); + } +} + +} // namespace perf +} // namespace algorithms +} // namespace embb + +#endif /* EMBB_ALGORITHMS_PERF_FOR_EACH_PERF_INL_H_ */ diff --git a/algorithms_cpp/perf/for_each_perf.h b/algorithms_cpp/perf/for_each_perf.h new file mode 100644 index 0000000..86d6ca4 --- /dev/null +++ b/algorithms_cpp/perf/for_each_perf.h @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2014, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_ALGORITHMS_PERF_FOR_EACH_PERF_H_ +#define EMBB_ALGORITHMS_PERF_FOR_EACH_PERF_H_ + +#include +#include + +namespace embb { +namespace algorithms { +namespace perf { + +using embb::base::perf::CallArgs; + +/** + * Operation performed in each loop iteration. + */ +template +class ForEachOp { +public: + explicit ForEachOp(const CallArgs & args) : + load_factor(args.LoadFactor()) { } + void operator()(T & val) const { + T x = val; + for (unsigned int i = 0; i < load_factor; i++) { + x = 2 * x * x; + x = sqrt(x); + x = x / sqrt(static_cast(2)); + } + val = x; + } +private: + unsigned int load_factor; +}; + +template +class ForEachFunctor { +public: + ForEachFunctor(const CallArgs & args) : + cargs(args), op(args) { } + void operator()(T & value) const { + op(value); + } + ForEachFunctor(const ForEachFunctor & other) : + cargs(other.cargs), op(other.op) { } + ForEachFunctor & operator=(const ForEachFunctor & other) { + if (&other != *this) { + cargs = other.cargs; + op = other.op; + } + return *this; + } +private: + const CallArgs & cargs; + ForEachOp op; +}; + +template +class SerialForEach { +public: + explicit SerialForEach(const embb::base::perf::CallArgs & args); + ~SerialForEach(); + void Pre() { } + void Run(); + void Post() { } + +private: + const embb::base::perf::CallArgs & cargs; + ForEachOp op; + const size_t vector_size; + T * v; + /* prohibit copy and assignment */ + SerialForEach(const SerialForEach & other); + SerialForEach & operator=(const SerialForEach & other); +}; + +template +class ParallelForEach { +public: + explicit ParallelForEach(const embb::base::perf::CallArgs & args); + ~ParallelForEach(); + void Pre() { } + void Run(unsigned int numThreads); + void Post() { } + +private: + const embb::base::perf::CallArgs & cargs; + const size_t vector_size; + T * v; + /* prohibit copy and assignment */ + ParallelForEach(const ParallelForEach & other); + ParallelForEach & operator=(const ParallelForEach & other); +}; + +} // namespace perf +} // namespace algorithms +} // namespace embb + +#include + +#endif /* EMBB_ALGORITHMS_PERF_FOR_PERF_H_ */ + diff --git a/algorithms_cpp/perf/index_iterator.h b/algorithms_cpp/perf/index_iterator.h new file mode 100644 index 0000000..aabcd84 --- /dev/null +++ b/algorithms_cpp/perf/index_iterator.h @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2014, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_ALGORITHMS_PERF_INTERNAL_INDEX_ITERATOR_H_ +#define EMBB_ALGORITHMS_PERF_INTERNAL_INDEX_ITERATOR_H_ + +#include + +namespace embb { +namespace algorithms { +namespace perf { + +/** + * Auxiliary helper for performance tests. Iterator returns index value + * on dereferencing instead of resolving an underlying value. Prevents + * memory access in CPU-only test cases as parallel algorithms in embb + * currently do not support indices as RAI. + */ +template +class IndexIterator : +public ::std::iterator< ::std::random_access_iterator_tag, T > +{ +private: + typedef IndexIterator self_type; + +public: + typedef ::std::random_access_iterator_tag iterator_category; + typedef T value_type; + typedef int difference_type; + typedef int distance_type; + typedef T * pointer; + typedef T & reference; + + IndexIterator() : _ptr(0), _value(static_cast(0)) { } + IndexIterator(int * rhs) : _ptr(*rhs), _value(static_cast(0)) { } + IndexIterator(int rhs) : _ptr(rhs), _value(static_cast(0)) { } + IndexIterator(const self_type &rhs) : _ptr(rhs._ptr), _value(rhs._value) { } + + inline self_type& operator+=(const difference_type& rhs) { + _ptr += rhs; return *this; + } + inline self_type& operator-=(const difference_type& rhs) { + _ptr -= rhs; return *this; + } + inline T & operator*() { + _value = static_cast(_ptr); + return _value; + } + inline T * operator->() { + _value = static_cast(_ptr); + return _value; + } + inline T& operator[](const difference_type& rhs) { + return rhs; + } + inline self_type& operator++() { + ++_ptr; return *this; + } + inline self_type& operator--() { + --_ptr; return *this; + } + inline difference_type operator+(const self_type& rhs) { + return difference_type(_ptr + rhs._ptr); + } + inline self_type operator+(const difference_type& rhs) const { + return self_type(_ptr + rhs); + } + inline self_type operator+(difference_type& rhs) const { + return self_type(_ptr + rhs); + } + inline self_type operator+=(const difference_type& rhs) const { + return self_type(_ptr + rhs); + } + inline difference_type operator-(const self_type& rhs) { + return difference_type(_ptr - rhs._ptr); + } + inline self_type operator-(const difference_type& rhs) const { + return self_type(_ptr - rhs); + } + inline self_type operator-(difference_type& rhs) const { + return self_type(_ptr - rhs); + } + inline self_type operator-=(const difference_type& rhs) const { + return self_type(_ptr - rhs); + } + friend inline self_type operator+(const difference_type& lhs, const self_type& rhs) { + return self_type(lhs) + rhs; + } + friend inline self_type operator+=(const difference_type& lhs, const self_type& rhs) { + return self_type(lhs) + rhs; + } + friend inline self_type operator-(const difference_type& lhs, const self_type& rhs) { + return self_type(lhs) - rhs; + } + friend inline self_type operator-=(const difference_type& lhs, const self_type& rhs) { + return self_type(lhs) - rhs; + } + inline bool operator==(const self_type& rhs) { + return _ptr == rhs._ptr; + } + inline bool operator!=(const self_type& rhs) { + return _ptr != rhs._ptr; + } + inline bool operator>(const self_type& rhs) { + return _ptr > rhs._ptr; + } + inline bool operator<(const self_type& rhs) { + return _ptr < rhs._ptr; + } + inline bool operator>=(const self_type& rhs) { + return _ptr >= rhs._ptr; + } + inline bool operator<=(const self_type& rhs) { + return _ptr <= rhs._ptr; + } +protected: + int _ptr; + T _value; +}; + +} // namespace perf +} // namespace algorithms +} // namespace embb + +#endif /* EMBB_ALGORITHMS_PERF_INTERNAL_INDEX_ITERATOR_H_ */ diff --git a/algorithms_cpp/perf/main.cc b/algorithms_cpp/perf/main.cc new file mode 100644 index 0000000..c582188 --- /dev/null +++ b/algorithms_cpp/perf/main.cc @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2014, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace embb::algorithms::perf; +using embb::base::perf::Timer; +using embb::base::perf::CallArgs; + +void ReportResult( + const std::string & name, + unsigned int threads, + const CallArgs & args, + double elapsed, + double speedup) { + std::cout + << args.VectorSize() << "," + << args.ElementTypeName() << "," + << args.LoadFactor() << "," + << args.StressModeName() << "," + << threads << "," + << std::fixed << elapsed << "," + << std::setprecision(3) << speedup + << std::endl; + std::ofstream file; + std::string filename = "performance_tests_result.csv"; + file.open(filename.c_str(), ::std::ofstream::out | ::std::ofstream::app); + file + << name << "," + << args.VectorSize() << "," + << args.ElementTypeName() << "," + << args.LoadFactor() << "," + << args.StressModeName() << "," + << threads << "," + << std::fixed << elapsed << "," + << std::setprecision(3) << speedup + << std::endl; +} + +template< typename TestSerial, typename TestParallel > +void RunPerformanceTest( + const embb::base::perf::CallArgs & args, + const std::string & name) { + std::cout << "--- Running " << name << std::endl; + // Initialize new test instances: + TestParallel testParallel(args); + // Parallel runs: + unsigned int threads = 1; + // Base value to compute speedup; parallel execution + // with 1 thread or serial execution. + double baseDuration = 0; + + // Whether to use serial or parallel exeuction using 1 + // thread for speedup reference: + if (args.ParallelBaseReference() == 0) { + TestSerial testSerial(args); + // Serial run: + Timer t; + testSerial.Run(); + double elapsed = t.Elapsed(); + ReportResult( + name, + 0, + args, + elapsed, + 1.0); + baseDuration = elapsed; + } + threads += args.ParallelBaseReference(); + // Run executions with incrementing number of threads: + embb_internal_thread_index_set_max(args.MaxThreads()); + while(threads <= args.MaxThreads()) { + // Set number of available threads to given limit: + // embb::base::Thread::SetThreadsMaxCount(threads); + embb_internal_thread_index_reset(); + // Configure cores to be used by EMBB: + embb::base::CoreSet cores(false); + for (unsigned int coreId = 0; coreId < threads; ++coreId) { + cores.Add(coreId); + } + embb::tasks::Node::Initialize( + 1, 1, cores, + MTAPI_NODE_MAX_TASKS_DEFAULT * 8, + MTAPI_NODE_MAX_GROUPS_DEFAULT * 8, + MTAPI_NODE_MAX_QUEUES_DEFAULT * 8, + MTAPI_NODE_QUEUE_LIMIT_DEFAULT * 8, + MTAPI_NODE_MAX_PRIORITIES_DEFAULT); + // Test setup: + testParallel.Pre(); + // Initialize timer: + Timer t; + // Run the test body: + testParallel.Run(threads); + // Report duration: + double elapsed = t.Elapsed(); + if (threads == 1) { + baseDuration = elapsed; + } + ReportResult( + name, + threads, + args, + elapsed, + static_cast(baseDuration) / static_cast(elapsed)); + // Test teardown: + testParallel.Post(); + if (threads < 4) { + ++threads; + } else { + threads += 4; + } + embb::tasks::Node::Finalize(); + } +} + +template +void RunPerformanceTests( + const CallArgs & args) { + RunPerformanceTest, ParallelForEach >(args, "ForEach"); + RunPerformanceTest, ParallelReduce >(args, "Reduce"); + RunPerformanceTest, ParallelScan >(args, "Scan"); + RunPerformanceTest, ParallelCount >(args, "Count"); + RunPerformanceTest, ParallelQuickSort >(args, "Quicksort"); + RunPerformanceTest, ParallelMergeSort >(args, "Mergesort"); +} + +int main(int argc, char * argv[]) { + // Parse command line arguments: + embb::base::perf::CallArgs args; + try { + args.Parse(argc, argv); + } catch (::std::runtime_error & re) { + ::std::cerr << re.what() << ::std::endl; + } + if (args.ParallelBaseReference() == 1) { + embb_log_set_log_level(EMBB_LOG_LEVEL_TRACE); + } + // Print test settings: + args.Print(::std::cout); + // Run tests: + switch (args.ElementType()) { + case CallArgs::FLOAT: + RunPerformanceTests(args); + break; + case CallArgs::DOUBLE: + RunPerformanceTests(args); + break; + case CallArgs::UNDEFINED_SCALAR_TYPE: + break; + } + return 0; +} diff --git a/algorithms_cpp/perf/merge_sort_perf-inl.h b/algorithms_cpp/perf/merge_sort_perf-inl.h new file mode 100644 index 0000000..04e62f8 --- /dev/null +++ b/algorithms_cpp/perf/merge_sort_perf-inl.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2014, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_ALGORITHMS_PERF_MERGE_SORT_PERF_INL_H_ +#define EMBB_ALGORITHMS_PERF_MERGE_SORT_PERF_INL_H_ + +#include +#include +#include +#include +#include +#include + +namespace embb { +namespace algorithms { +namespace perf { + +template +SerialMergeSort::SerialMergeSort(const embb::base::perf::CallArgs & args) +: cargs(args), vector_size(args.VectorSize()) { + v = static_cast(embb::base::Allocation::AllocateCacheAligned( + vector_size * sizeof(T))); + for (size_t i = 0; i < vector_size; i++) { + v[i] = static_cast(i); + } +} + +template +SerialMergeSort::~SerialMergeSort() { + embb::base::Allocation::FreeAligned(v); +} + +template +void SerialMergeSort::Run() { + std::sort(v, v + vector_size, std::greater()); +} + +template +ParallelMergeSort::ParallelMergeSort(const embb::base::perf::CallArgs & args) +: cargs(args), vector_size(args.VectorSize()) { + v = static_cast(embb::base::Allocation::AllocateCacheAligned( + vector_size * sizeof(T))); + for (size_t i = 0; i < vector_size; i++) { + v[i] = static_cast(i); + } +} + +template +ParallelMergeSort::~ParallelMergeSort() { + embb::base::Allocation::FreeAligned(v); +} + +template +void ParallelMergeSort::Run(unsigned int numThreads) { + // Always reading input values from memory, no CPU-only test possible + // as mergesort sorts in-place. + embb::algorithms::MergeSortAllocate( + v, v + vector_size, + std::greater(), + embb::tasks::ExecutionPolicy(), + vector_size / numThreads); +} + +} // namespace perf +} // namespace algorithms +} // namespace embb + +#endif // EMBB_ALGORITHMS_PERF_MERGE_SORT_PERF_INL_H_ diff --git a/algorithms_cpp/perf/merge_sort_perf.h b/algorithms_cpp/perf/merge_sort_perf.h new file mode 100644 index 0000000..f887097 --- /dev/null +++ b/algorithms_cpp/perf/merge_sort_perf.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2014, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_ALGORITHMS_PERF_MERGE_SORT_PERF_H_ +#define EMBB_ALGORITHMS_PERF_MERGE_SORT_PERF_H_ + +#include +#include + +namespace embb { +namespace algorithms { +namespace perf { + +using embb::base::perf::CallArgs; + +template +class SerialMergeSort { +public: + explicit SerialMergeSort(const embb::base::perf::CallArgs & args); + ~SerialMergeSort(); + void Pre() { } + void Run(); + void Post() { } +private: + static int Greater(const void *a, const void *b); + const embb::base::perf::CallArgs & cargs; + const size_t vector_size; + T * v; + /* prohibit copy and assignment */ + SerialMergeSort(const SerialMergeSort & other); + SerialMergeSort & operator=(const SerialMergeSort & other); +}; + +template +class ParallelMergeSort { +public: + explicit ParallelMergeSort(const embb::base::perf::CallArgs & args); + ~ParallelMergeSort(); + void Pre() { } + void Run(unsigned int numThreads); + void Post() { } + +private: + const embb::base::perf::CallArgs & cargs; + const size_t vector_size; + T * v; + /* prohibit copy and assignment */ + ParallelMergeSort(const ParallelMergeSort & other); + ParallelMergeSort & operator=(const ParallelMergeSort & other); +}; + +} // namespace perf +} // namespace algorithms +} // namespace embb + +#include + +#endif // EMBB_ALGORITHMS_PERF_MERGE_SORT_PERF_H_ + diff --git a/algorithms_cpp/perf/quick_sort_perf-inl.h b/algorithms_cpp/perf/quick_sort_perf-inl.h new file mode 100644 index 0000000..c47fcb5 --- /dev/null +++ b/algorithms_cpp/perf/quick_sort_perf-inl.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2014, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_ALGORITHMS_PERF_QUICK_SORT_PERF_INL_H_ +#define EMBB_ALGORITHMS_PERF_QUICK_SORT_PERF_INL_H_ + +#include +#include +#include +#include +#include +#include + +namespace embb { +namespace algorithms { +namespace perf { + +template +SerialQuickSort::SerialQuickSort(const embb::base::perf::CallArgs & args) +: cargs(args), vector_size(args.VectorSize()) { + v = static_cast(embb::base::Allocation::AllocateCacheAligned( + vector_size * sizeof(T))); + for (size_t i = 0; i < vector_size; i++) { + v[i] = static_cast(i); + } +} + +template +SerialQuickSort::~SerialQuickSort() { + embb::base::Allocation::FreeAligned(v); +} + +template +int SerialQuickSort::Greater(const void *lhs, const void *rhs) { + T l = *reinterpret_cast(lhs); + T r = *reinterpret_cast(rhs); + if (l < r) { return 1; } + if (l > r) { return -1; } + return 0; +} + +template +void SerialQuickSort::Run() { + std::qsort(v, vector_size, sizeof(T), Greater); +} + +template +ParallelQuickSort::ParallelQuickSort(const embb::base::perf::CallArgs & args) +: cargs(args), vector_size(args.VectorSize()) { + v = static_cast(embb::base::Allocation::AllocateCacheAligned( + vector_size * sizeof(T))); + for (size_t i = 0; i < vector_size; i++) { + v[i] = static_cast(i); + } +} + +template +ParallelQuickSort::~ParallelQuickSort() { + embb::base::Allocation::FreeAligned(v); +} + +template +void ParallelQuickSort::Run(unsigned int numThreads) { + // Always reading input values from memory, no CPU-only test possible + // as quicksort sorts in-place. + embb::algorithms::QuickSort( + v, v + vector_size, + std::greater(), + embb::tasks::ExecutionPolicy(), + vector_size / numThreads); +} + +} // namespace perf +} // namespace algorithms +} // namespace embb + +#endif // EMBB_ALGORITHMS_PERF_QUICK_SORT_PERF_INL_H_ diff --git a/algorithms_cpp/perf/quick_sort_perf.h b/algorithms_cpp/perf/quick_sort_perf.h new file mode 100644 index 0000000..9ab8284 --- /dev/null +++ b/algorithms_cpp/perf/quick_sort_perf.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2014, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_ALGORITHMS_PERF_QUICK_SORT_PERF_H_ +#define EMBB_ALGORITHMS_PERF_QUICK_SORT_PERF_H_ + +#include +#include + +namespace embb { +namespace algorithms { +namespace perf { + +using embb::base::perf::CallArgs; + +template +class SerialQuickSort { +public: + explicit SerialQuickSort(const embb::base::perf::CallArgs & args); + ~SerialQuickSort(); + void Pre() { } + void Run(); + void Post() { } +private: + static int Greater(const void *a, const void *b); + const embb::base::perf::CallArgs & cargs; + const size_t vector_size; + T * v; + /* prohibit copy and assignment */ + SerialQuickSort(const SerialQuickSort & other); + SerialQuickSort & operator=(const SerialQuickSort & other); +}; + +template +class ParallelQuickSort { +public: + explicit ParallelQuickSort(const embb::base::perf::CallArgs & args); + ~ParallelQuickSort(); + void Pre() { } + void Run(unsigned int numThreads); + void Post() { } + +private: + const embb::base::perf::CallArgs & cargs; + const size_t vector_size; + T * v; + /* prohibit copy and assignment */ + ParallelQuickSort(const ParallelQuickSort & other); + ParallelQuickSort & operator=(const ParallelQuickSort & other); +}; + +} // namespace perf +} // namespace algorithms +} // namespace embb + +#include + +#endif // EMBB_ALGORITHMS_PERF_QUICK_SORT_PERF_H_ + diff --git a/algorithms_cpp/perf/reduce_perf-inl.h b/algorithms_cpp/perf/reduce_perf-inl.h new file mode 100644 index 0000000..2a7778c --- /dev/null +++ b/algorithms_cpp/perf/reduce_perf-inl.h @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2014, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_ALGORITHMS_PERF_REDUCE_PERF_TBB_INL_H_ +#define EMBB_ALGORITHMS_PERF_REDUCE_PERF_TBB_INL_H_ + +#include +#include +#include +#include +#include +#include + +namespace embb { +namespace algorithms { +namespace perf { + +using embb::base::perf::CallArgs; + +template +SerialReduce::SerialReduce( + const embb::base::perf::CallArgs & args) : + cargs(args), + vector_size(args.VectorSize()) { + if (cargs.StressMode() == CallArgs::RAM_STRESS) { + v = static_cast( + embb::base::Allocation::AllocateCacheAligned( + vector_size * sizeof(T))); + for (size_t i = 0; i < vector_size; i++) { + v[i] = (T)i; + } + } + else { + v = 0; + } +} + +template +SerialReduce::~SerialReduce() { + if (v != 0) { + embb::base::Allocation::FreeAligned(v); + } +} + +template +void SerialReduce::Run() { + result = 0; + if (cargs.StressMode() == CallArgs::CPU_STRESS) { + TransformOp op(static_cast(1.0) / vector_size, cargs); + for (size_t i = 0; i < vector_size; i++) { + result += op((T)i); + } + result /= static_cast(vector_size); + } + else if (cargs.StressMode() == CallArgs::RAM_STRESS) { + TransformOp op(static_cast(1.0) / vector_size, cargs); + for (size_t i = 0; i < vector_size; i++) { + result += op(v[i]); + } + result /= static_cast(vector_size); + } +} + +template +ParallelReduce::ParallelReduce( + const embb::base::perf::CallArgs & args) : + cargs(args), + vector_size(args.VectorSize()) { + if (cargs.StressMode() == CallArgs::RAM_STRESS) { + v = static_cast( + embb::base::Allocation::AllocateCacheAligned( + vector_size * sizeof(T))); + for (size_t i = 0; i < vector_size; i++) { + v[i] = (T)i; + } + } + else { + v = 0; + } +} + +template +ParallelReduce::~ParallelReduce() { + if (v != 0) { + embb::base::Allocation::FreeAligned(v); + } +} + +template +void ParallelReduce::Run(unsigned int numThreads) { + TransformOp op(static_cast(1) / vector_size, cargs); + if (cargs.StressMode() == CallArgs::CPU_STRESS) { + result = embb::algorithms::Reduce( + // Using iterator returning index value to avoid + // memory access + IndexIterator(0), + IndexIterator(static_cast(vector_size)), + static_cast(0), // neutral element + ::std::plus(), // reduce op + op, // transform op + embb::tasks::ExecutionPolicy(), + vector_size / numThreads + ); + } + else if (cargs.StressMode() == CallArgs::RAM_STRESS) { + result = embb::algorithms::Reduce( + v, v + vector_size, // input + static_cast(0), // neutral element + ::std::plus(), // reduce op + op, // transform op + embb::tasks::ExecutionPolicy(), + vector_size / numThreads + ); + } + result /= static_cast(vector_size); +} + +} // namespace perf +} // namespace algorithms +} // namespace embb + +#endif diff --git a/algorithms_cpp/perf/reduce_perf.h b/algorithms_cpp/perf/reduce_perf.h new file mode 100644 index 0000000..b6086d9 --- /dev/null +++ b/algorithms_cpp/perf/reduce_perf.h @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2014, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_ALGORITHMS_PERF_REDUCE_PERF_H_ +#define EMBB_ALGORITHMS_PERF_REDUCE_PERF_H_ + +#include + +namespace embb { +namespace algorithms { +namespace perf { + +template +class TransformOp { + T step_size; + size_t load_factor; +public: + explicit TransformOp(T stepSize, const embb::base::perf::CallArgs & args) : + step_size(stepSize), + load_factor(args.LoadFactor()) { } + TransformOp(const TransformOp & other) : + step_size(other.step_size), + load_factor(other.load_factor) { } + TransformOp & operator=(const TransformOp & other) { + step_size = other.step_size; + load_factor = other.load_factor; + } + T operator()(T val) const { + T x = 0; + // Simulate more complex operation depending on + // load factor. Default load factor is 1. + for (size_t i = 0; i < load_factor * 10000; ++i) { + x = (val + static_cast(0.5)) * step_size * i; + x = static_cast(4.0 / (1.0 + x * x / load_factor)); + } + return x; + } +}; + +template +class SerialReduce { +public: + explicit SerialReduce( + const embb::base::perf::CallArgs & args); + ~SerialReduce(); + void Pre() { } + void Run(); + void Post() { } + +private: + const embb::base::perf::CallArgs & cargs; + const size_t vector_size; + T *v; + T result; + + /* prohibit copy and assignment */ + SerialReduce(const SerialReduce &other); + SerialReduce& operator=(const SerialReduce &other); +}; + +template +class ParallelReduce { +public: + explicit ParallelReduce( + const embb::base::perf::CallArgs & args); + ~ParallelReduce(); + void Pre() { } + void Run(unsigned int numThreads); + void Post() { } + +private: + const embb::base::perf::CallArgs & cargs; + const size_t vector_size; + T *v; + T result; + + /* prohibit copy and assignment */ + ParallelReduce(const ParallelReduce &other); + ParallelReduce& operator=(const ParallelReduce &other); +}; + +} +} +} + +#include + +#endif /* EMBB_ALGORITHMS_PERF_REDUCE_PERF_H_ */ diff --git a/algorithms_cpp/perf/scan_perf-inl.h b/algorithms_cpp/perf/scan_perf-inl.h new file mode 100644 index 0000000..0cd3aa1 --- /dev/null +++ b/algorithms_cpp/perf/scan_perf-inl.h @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2014, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMB_ALGORITHMS_PERF_SCAN_PERF_INL_H_ +#define EMB_ALGORITHMS_PERF_SCAN_PERF_INL_H_ + +#include +#include +#include +#include +#include +#include + +namespace embb { +namespace algorithms { +namespace perf { + +using namespace embb::base; + +template +SerialScan::SerialScan(const embb::base::perf::CallArgs & args) : + cargs(args), + vector_size(args.VectorSize()), + load_factor(args.LoadFactor()) { + if (cargs.StressMode() == CallArgs::RAM_STRESS) { + out = (T *) Allocation::AllocateCacheAligned( + vector_size * sizeof(T)); + in = (T *) Allocation::AllocateCacheAligned( + vector_size * sizeof(T)); + for (size_t i = 0; i < vector_size; i++) { + in[i] = static_cast(1); + } + } + else { + out = 0; + in = 0; + } +} + +template +SerialScan::~SerialScan() { + if (in != 0) { + Allocation::FreeAligned(in); + } + if (out != 0) { + Allocation::FreeAligned(out); + } +} + +template +void SerialScan::Run() { + T total = 0; + if (cargs.StressMode() == CallArgs::RAM_STRESS) { + for (unsigned int i = 0; i < vector_size; i++) { + // artificial complexity + for (unsigned int k = 0; k < load_factor; k++) { + total += static_cast(in[i]); + } + out[i] = total; + } + result = out[vector_size - 1]; + } + else { + for (unsigned int i = 0; i < vector_size; i++) { + // artificial complexity + for (unsigned int k = 0; k < load_factor; k++) { + total += 1; + } + out[i] = total; + } + result = out[vector_size - 1]; + } +} + +template +ParallelScan::ParallelScan(const embb::base::perf::CallArgs & args) : + cargs(args), vector_size(args.VectorSize()) { + if (cargs.StressMode() == CallArgs::RAM_STRESS) { + in = (T *) Allocation::AllocateCacheAligned( + vector_size * sizeof(T)); + for (size_t i = 0; i < vector_size; i++) { + in[i] = static_cast(1); + } + } + else { + in = 0; + } + out = (T *) Allocation::AllocateCacheAligned( + vector_size * sizeof(T)); +} + +template +ParallelScan::~ParallelScan() { + if (in != 0) { + Allocation::FreeAligned(in); + } +} + +template +void ParallelScan::Run(unsigned int numThreads) { + if (cargs.StressMode() == CallArgs::CPU_STRESS) { + CpuStressScanOp op(cargs); + embb::algorithms::Scan( + // Using iterator returning index value to avoid + // memory access + IndexIterator(0), + IndexIterator(static_cast(vector_size)), + out, + static_cast(0), // neutral element + op, // scan aggregation functor + embb::algorithms::Identity(), // no transformation + embb::tasks::ExecutionPolicy(), + vector_size / numThreads + ); + result = out[vector_size - 1]; + } + else if (cargs.StressMode() == CallArgs::RAM_STRESS) { + RamStressScanOp op(cargs, in); + embb::algorithms::Scan( + in, in + vector_size, + out, + static_cast(0), // neutral element + op, // scan aggregation functor + embb::algorithms::Identity(), // no transformation + embb::tasks::ExecutionPolicy(), + vector_size / numThreads + ); + result = out[vector_size - 1]; + } +} + +} // namespace perf +} // namespace algorithms +} // namespace embb + +#endif /* EMB_ALGORITHMS_PERF_SCAN_PERF_INL_H_ */ diff --git a/algorithms_cpp/perf/scan_perf.h b/algorithms_cpp/perf/scan_perf.h new file mode 100644 index 0000000..5f7b4c9 --- /dev/null +++ b/algorithms_cpp/perf/scan_perf.h @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2014, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_ALGORITHMS_PERF_SCAN_PERF_H_ +#define EMBB_ALGORITHMS_PERF_SCAN_PERF_H_ + +#include +#include + +namespace embb { +namespace algorithms { +namespace perf { + + +template +class RamStressScanOp { + size_t load_factor; + const T * const in; +public: + explicit RamStressScanOp(const CallArgs & args, const T inVector[]) : + load_factor(args.LoadFactor()), in(inVector) { } +public: + T operator()(const T & lhs, const T & rhs) { + T value = lhs; + // rhs is index. First iteration in range has + // lhs = 0 (neutral element). + // artificial complexity + for (unsigned int k = 0; k < load_factor; k++) { + // artificial complexity + value += rhs; + } + return value; + } + RamStressScanOp(const RamStressScanOp & other) + : load_factor(other.load_factor), in(other.in) { } + RamStressScanOp & operator=(const RamStressScanOp & other) { + if (*this != &other) { + load_factor = other.load_factor; + in = other.in; + } + return *this; + } +}; + +template +class CpuStressScanOp { + size_t load_factor; +public: + explicit CpuStressScanOp(const CallArgs & args) : + load_factor(args.LoadFactor()) { } +public: + T operator()(const T & lhs, const T &) { + T value = lhs; + // Ignore rhs value, always add 1, corresponding + // to input value in RAM stress operation + for (unsigned int k = 0; k < load_factor; k++) { + // artificial complexity + value += static_cast(1); + } + return value; + } +}; + +template +class SerialScan { +public: + explicit SerialScan(const embb::base::perf::CallArgs & args); + ~SerialScan(); + void Pre() { } + void Run(); + void Post() { } + +private: + const embb::base::perf::CallArgs & cargs; + const size_t vector_size; + unsigned int load_factor; + T * in; + T * out; + T result; + /* prohibit copy and assignment */ + SerialScan(const SerialScan & other); + SerialScan & operator=(const SerialScan & other); +}; + +template +class ParallelScan { +public: + explicit ParallelScan(const embb::base::perf::CallArgs & args); + ~ParallelScan(); + void Pre() { } + void Run(unsigned int numThreads); + void Post() { } + +private: + const embb::base::perf::CallArgs & cargs; + const size_t vector_size; + T * in; + T * out; + T result; + /* prohibit copy and assignment */ + ParallelScan(const ParallelScan & other); + ParallelScan & operator=(const ParallelScan & other); +}; + + +} // namespace perf +} // namespace algorithms +} // namespace embb + +#include + +#endif /* EMBB_ALGORITHMS_PERF_SCAN_PERF_H_ */ diff --git a/base_cpp/CMakeLists.txt b/base_cpp/CMakeLists.txt index e7cfd27..b8f7d4b 100644 --- a/base_cpp/CMakeLists.txt +++ b/base_cpp/CMakeLists.txt @@ -7,6 +7,8 @@ file(GLOB_RECURSE EMBB_BASE_CPP_SOURCES "src/*.cc" "src/*.h") file(GLOB_RECURSE EMBB_BASE_CPP_HEADERS "include/embb/*.h") if (BUILD_TESTS STREQUAL ON) file(GLOB_RECURSE EMBB_BASE_TEST_SOURCES "test/*.cc" "test/*.h") + file(GLOB_RECURSE EMBB_BASE_PERF_SOURCES "perf/src/*.cc" "perf/src/*.h") + file(GLOB_RECURSE EMBB_BASE_PERF_HEADERS "perf/include/*.h") endif() # Create header file from input file @@ -32,13 +34,26 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include add_library (embb_base_cpp ${EMBB_BASE_CPP_SOURCES} ${EMBB_BASE_CPP_HEADERS}) target_link_libraries(embb_base_cpp embb_base_c) if (BUILD_TESTS STREQUAL ON) + # Unit tests include_directories(test/ ${CMAKE_CURRENT_BINARY_DIR}/../partest/include ) - add_executable (embb_base_cpp_test ${EMBB_BASE_TEST_SOURCES}) + add_executable (embb_base_cpp_test + ${EMBB_BASE_TEST_SOURCES}) target_link_libraries(embb_base_cpp_test partest embb_base_cpp embb_base_c ${compiler_libs}) CopyBin(BIN embb_base_cpp_test DEST ${local_install_dir}) + + # Performance tests + include_directories(perf/include + ${CMAKE_CURRENT_BINARY_DIR}/../partest/include + ) + add_library (embb_base_cpp_perf ${EMBB_BASE_PERF_SOURCES}) + target_link_libraries(embb_base_cpp_perf partest embb_base_cpp embb_base_c + ${compiler_libs}) + install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/perf/include/embb + DESTINATION include FILES_MATCHING PATTERN "*.h") + install(TARGETS embb_base_cpp_perf DESTINATION lib) endif() install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/embb diff --git a/base_cpp/perf/include/embb/base/perf/call_args.h b/base_cpp/perf/include/embb/base/perf/call_args.h new file mode 100644 index 0000000..0dfa9a6 --- /dev/null +++ b/base_cpp/perf/include/embb/base/perf/call_args.h @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2014, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_PERF_CALL_ARGS_H_ +#define EMBB_BASE_PERF_CALL_ARGS_H_ + +#include +#include +#include + +namespace embb { +namespace base { +namespace perf { + +class CallArgs { + +public: + + typedef enum { + UNDEFINED_SCALAR_TYPE = 0, + FLOAT, + DOUBLE + } ScalarType; + + typedef enum { + UNDEFINED_STRESS_TYPE = 0, + RAM_STRESS, + CPU_STRESS + } StressType; + +public: + + inline CallArgs() : + element_type(CallArgs::FLOAT), + stress_type(CallArgs::RAM_STRESS), + max_threads(embb::base::CoreSet::CountAvailable()), + vector_size(10000000), + load_factor(100), + parallel_base_ref(0), + counter_scale(0) { + } + + inline CallArgs(int argc, char * argv[]) { + Parse(argc, argv); + } + + void Print(::std::ostream & os); + + void Parse(int argc, char * argv[]); + + inline size_t MaxThreads() const { + return max_threads; + } + + inline unsigned int CounterScale() const { + return counter_scale; + } + + inline ScalarType ElementType() const { + return element_type; + } + + inline ::std::string ElementTypeName() const { + return ((ElementType() == UNDEFINED_SCALAR_TYPE) + ? "undefined" + : ((ElementType() == FLOAT) + ? "float" + : "double")); + } + + inline StressType StressMode() const { + return stress_type; + } + + inline ::std::string StressModeName() const { + return ((StressMode() == CPU_STRESS) + ? "cpu" + : "ram"); + } + + inline size_t VectorSize() const { + return vector_size; + } + + inline size_t LoadFactor() const { + return load_factor; + } + + inline unsigned int ParallelBaseReference() const { + return parallel_base_ref;; + } + +private: + + ScalarType element_type; + StressType stress_type; + size_t max_threads; + size_t vector_size; + size_t load_factor; + unsigned int parallel_base_ref; + unsigned int counter_scale; + +}; + +} // namespace perf +} // namespace base +} // namespace embb + +#endif diff --git a/base_cpp/perf/include/embb/base/perf/duration.h b/base_cpp/perf/include/embb/base/perf/duration.h new file mode 100644 index 0000000..42bfe65 --- /dev/null +++ b/base_cpp/perf/include/embb/base/perf/duration.h @@ -0,0 +1,19 @@ +#ifndef _EMBB_BASE_CPP_PERF_DURATION_H +#define _EMBB_BASE_CPP_PERF_DURATION_H + +#include + +namespace embb { +namespace base { +namespace perf { + +struct Duration { + Timer::timestamp_t Start; + Timer::timestamp_t End; +}; + +} // namespace perf +} // namespace base +} // namespace embb + +#endif // _EMBB_BASE_CPP_PERF_DURATION_H diff --git a/base_cpp/perf/include/embb/base/perf/internal/env.h b/base_cpp/perf/include/embb/base/perf/internal/env.h new file mode 100644 index 0000000..7b2c031 --- /dev/null +++ b/base_cpp/perf/include/embb/base/perf/internal/env.h @@ -0,0 +1,97 @@ +#ifndef EMBB_BASE_CPP_PERF_INTERNAL_ENV_H_ +#define EMBB_BASE_CPP_PERF_INTERNAL_ENV_H_ + +#include + +// Windows +#ifdef EMBB_PLATFORM_THREADING_WINTHREADS +#define EMBB_BASE_CPP_PERF_TIMER_WIN32 +#endif +// OS X +#if defined(__MACH__) && defined(__APPLE__) +#define EMBB_BASE_CPP_PERF_TIMER_OSX +#endif +// HPUX / Sun +#if (defined(__hpux) || defined(hpux)) || \ +((defined(__sun__) || defined(__sun) || defined(sun)) && \ +(defined(__SVR4) || defined(__svr4__))) +#define EMBB_BASE_CPP_PERF_TIMER_UX +#endif +// POSIX +#if defined(EMBB_PLATFORM_THREADING_POSIXTHREADS) +#define EMBB_BASE_CPP_PERF_TIMER_POSIX +#endif +// Linux +#if defined(__linux__) +#define EMBB_BASE_CPP_PERF_TIMER_LINUX +#endif +// FreeBSD +#if defined(__FreeBSD__) +#define EMBB_BASE_CPP_PERF_TIMER_FREEBSD +#endif +// C++11 (std::chrono) +#if (__cplusplus > 199711L) +#define EMBB_BASE_CPP_PERF_TIMER_CXX11__DISABLED__ +#endif + +// Architecture specific defines + +// Intel 386 +#if defined(EMBB_PLATFORM_ARCH_X86_32) +#define EMBB_BASE_CPP_PERF__ARCH_I386 +#define EMBB_BASE_CPP_PERF__ARCH_X86 + +// AMD64, Intel x64 +#elif defined(EMBB_PLATFORM_ARCH_X86_64) +#define EMBB_BASE_CPP_PERF__ARCH_X64 +#define EMBB_BASE_CPP_PERF__ARCH_X86 + +// ARM +#elif defined(EMBB_PLATFORM_ARCH_ARM) +// ARM versions consolidated to major architecture version. +// See: https://wiki.edubuntu.org/ARM/Thumb2PortingHowto +#if defined(__ARM_ARCH_7__) || \ + defined(__ARM_ARCH_7R__) || \ + defined(__ARM_ARCH_7A__) +#define EMBB_BASE_CPP_PERF__ARCH_ARMV7 1 +#endif +#if defined(EMBB_BASE_CPP_PERF__ARCH_ARMV7) || \ + defined(__ARM_ARCH_6__) || \ + defined(__ARM_ARCH_6J__) || \ + defined(__ARM_ARCH_6K__) || \ + defined(__ARM_ARCH_6Z__) || \ + defined(__ARM_ARCH_6T2__) || \ + defined(__ARM_ARCH_6ZK__) +#define EMBB_BASE_CPP_PERF__ARCH_ARMV6 1 +#endif +#if defined(EMBB_BASE_CPP_PERF__ARCH_ARMV6) || \ + defined(__ARM_ARCH_5T__) || \ + defined(__ARM_ARCH_5E__) || \ + defined(__ARM_ARCH_5TE__) || \ + defined(__ARM_ARCH_5TEJ__) +#define EMBB_BASE_CPP_PERF__ARCH_ARMV5 1 +#endif +#if defined(EMBB_BASE_CPP_PERF__ARCH_ARMV5) || \ + defined(__ARM_ARCH_4__) || \ + defined(__ARM_ARCH_4T__) +#define EMBB_BASE_CPP_PERF__ARCH_ARMV4 1 +#endif +#if defined(EMBB_BASE_CPP_PERF__ARCH_ARMV4) || \ + defined(__ARM_ARCH_3__) || \ + defined(__ARM_ARCH_3M__) +#define EMBB_BASE_CPP_PERF__ARCH_ARMV3 1 +#endif +#if defined(EMBB_BASE_CPP_PERF__ARCH_ARMV3) || \ + defined(__ARM_ARCH_2__) +#define EMBB_BASE_CPP_PERF__ARCH_ARMV2 1 +#define EMBB_BASE_CPP_PERF__ARCH_ARM 1 +#endif + +#endif // Architecture + +#if defined(__PAPI__) +#define EMBB_BASE_CPP_PERF_TIMER_PAPI +#endif + +#endif // EMBB_BASE_CPP_PERF_INTERNAL_ENV_H_ + diff --git a/base_cpp/perf/include/embb/base/perf/internal/timestamp.h b/base_cpp/perf/include/embb/base/perf/internal/timestamp.h new file mode 100644 index 0000000..6468f83 --- /dev/null +++ b/base_cpp/perf/include/embb/base/perf/internal/timestamp.h @@ -0,0 +1,38 @@ +#ifndef EMBB_BASE_CPP_PERF_INTERNAL_TIMESTAMP_H_ +#define EMBB_BASE_CPP_PERF_INTERNAL_TIMESTAMP_H_ + +#include +#include + +namespace embb { +namespace base { +namespace perf { + +class Timestamp { + public: +#if defined(EMBB_BASE_CPP_PERF__ARCH_X64) + typedef unsigned long long counter_t; +#else + typedef unsigned long long counter_t; +#endif + + public: + virtual const counter_t & Value() const = 0; + + static double FrequencyScaling(); + static double FrequencyPrescale(); + static const char * VariantName(); + inline static counter_t TimestampInfinity() { + return LLONG_MAX; + } + inline static counter_t TimestampNegInfinity() { + return 0; + } +}; + +} // namespace perf +} // namespace base +} // namespace embb + +#endif // EMBB_BASE_CPP_PERF_INTERNAL_TIMESTAMP_H_ + diff --git a/base_cpp/perf/include/embb/base/perf/internal/timestamp_clock_posix.h b/base_cpp/perf/include/embb/base/perf/internal/timestamp_clock_posix.h new file mode 100644 index 0000000..0466736 --- /dev/null +++ b/base_cpp/perf/include/embb/base/perf/internal/timestamp_clock_posix.h @@ -0,0 +1,124 @@ +#ifndef EMBB_BASE_CPP_PERF_INTERNAL_TIMER_TIMESTAMP_CLOCK_POSIX_H_ +#define EMBB_BASE_CPP_PERF_INTERNAL_TIMER_TIMESTAMP_CLOCK_POSIX_H_ + +#include +#include + +#include + +#include /* POSIX flags */ +#include /* clock_gettime(), time() */ +#include /* gethrtime(), gettimeofday() */ + +#include +#include +#include + +namespace embb { +namespace base { +namespace perf { +namespace internal { +namespace timer { + +/** + * Timestamp counter (TSC) for POSIX platforms. + */ +class TimestampClockPosix : public Timestamp +{ + private: + Timestamp::counter_t value; + + public: + /** + * Clock modes, ordered by preference. + */ + typedef enum { + UNDEFINED_CLOCK = 0, ///< Undefined (unresolved) mode + GENERIC_CLOCK, ///< Unknown (unresolvable) mode + + UX, ///< HP/UX mode (gethrtime) + MACH, ///< MACH/OS X mode + + THREAD_CPUTIME, ///< POSIX clock mode CLOCK_THREAD_CPUTIME_ID + PROC_CPUTIME, ///< POSIX clock mode CLOCK_PROCESS_CPUTIME_ID + HIGHRES, ///< POSIX clock mode CLOCK_HIGHRES + MONOTONIC_PRECISE, ///< POSIX clock mode CLOCK_MONOTONIC_PRECISE + MONOTONIC_RAW, ///< POSIX clock mode CLOCK_MONOTONIC_RAW + MONOTONIC, ///< POSIX clock mode CLOCK_MONOTONIC + REALTIME, ///< POSIX clock mode CLOCK_REALTIME + + NUM_CLOCK_MODES ///< Number of clock modes in total + } ClockMode; + + private: + typedef std::pair ClockDef; + + private: + /** + * List of clock modes as readable strings. + */ + static const char * clockModeNames[]; + /** + * Mapping of clock modes to clockid types. + * Only contains mappings for clock types available + * on the platform. + */ + static TimestampClockPosix::ClockDef availableModes[]; + /** + * Currently selected clock mode. + */ + static ClockMode clockMode; + /** + * Currently selected clockid_t corresponding to clockMode. + */ + static clockid_t clockId; + + public: + static Timestamp::counter_t frequencyScaling; + + public: + static void Calibrate(unsigned int mode = 0); + + inline TimestampClockPosix(const Timestamp::counter_t & counterValue) + : value(counterValue) + { } + + TimestampClockPosix(); + + inline TimestampClockPosix(const TimestampClockPosix & other) : + value(other.value) + { } + + inline TimestampClockPosix & operator=(const TimestampClockPosix & rhs) { + if (this != &rhs) { + value = rhs.value; + } + return *this; + } + + inline const Timestamp::counter_t & Value() const { + return value; + } + + inline static double FrequencyScaling() { + return 1.0f; + } + + inline static double FrequencyPrescale() { + return 1.0f; + } + + inline static const char * TimerName() { + return clockModeNames[clockMode]; + } + +}; + +} // namespace timer +} // namespace internal +} // namespace perf +} // namespace base +} // namespace embb + +#endif // EMBB_BASE_CPP_PERF_INTERNAL_TIMER_TIMESTAMP_CLOCK_POSIX_H_ + diff --git a/base_cpp/perf/include/embb/base/perf/internal/timestamp_clock_win32.h b/base_cpp/perf/include/embb/base/perf/internal/timestamp_clock_win32.h new file mode 100644 index 0000000..a81a06e --- /dev/null +++ b/base_cpp/perf/include/embb/base/perf/internal/timestamp_clock_win32.h @@ -0,0 +1,103 @@ +#ifndef EMBB_BASE_CPP_PERF_INTERNAL_TIMER_TIMESTAMP_CLOCK_WIN32_H_ +#define EMBB_BASE_CPP_PERF_INTERNAL_TIMER_TIMESTAMP_CLOCK_WIN32_H_ + +#include + +#if defined(EMBB_BASE_CPP_PERF_TIMER_WIN32) + +#if (defined(NTDDI_WIN8) && NTDDI_VERSION >= NTDDI_WIN8) +#define EMBB_BASE_CPP_PERF_TIMER_WIN32_SYSTEM_TIME_PRECISE +#endif + +// Prevent definition of Windows-specific macros 'min' and 'max': +#define NOMINMAX +#include + +namespace embb { +namespace base { +namespace perf { +namespace internal { +namespace timer { + +/** +* @brief Timestamp counter (TSC) for Windows platforms. +*/ +class TimestampClockWin32 : public Timestamp +{ + +private: + + typedef TimestampClockWin32 self_t; + +private: + + counter_t value; + +public: + + inline static void Calibrate(unsigned int = 0) { } + +public: + + inline TimestampClockWin32() { + FILETIME tm; +#if defined(EMBB_BASE_CPP_PERF_TIMER_WIN32_SYSTEM_TIME_PRECISE) + // Windows 8, Windows Server 2012 and later + GetSystemTimeAsFileTime(&tm); + // Should work according to docs, but doesn't: + // GetSystemTimePreciseAsFileTime(&tm); +#else + // Windows 2000 and later + GetSystemTimeAsFileTime(&tm); +#endif + value = (static_cast(tm.dwHighDateTime) << 32) | + static_cast(tm.dwLowDateTime); + } + + inline TimestampClockWin32(const self_t & other) + : value(other.value) + { } + + inline TimestampClockWin32(const counter_t & counterValue) + : value(counterValue) + { } + + inline self_t & operator=(const self_t & rhs) { + if (this != &rhs) { + value = rhs.value; + } + return *this; + } + + inline const counter_t & Value() const { + return value; + } + + inline static double FrequencyScaling() { + return 1.0f; + } + + inline static double FrequencyPrescale() { + // Adjust from millisecond to microsecond scale: + return 0.1; + } + + inline static const char * TimerName() { +#if defined(EMBB_BASE_CPP_PERF_TIMER_WIN32_SYSTEM_TIME_PRECISE) + return "Win32:GetSystemTimePrecise"; +#else + return "Win32:GetSystemTime"; +#endif + } + +}; + +} // namespace timer +} // namespace internal +} // namespace perf +} // namespace base +} // namespace embb + +#endif // defined(EMBB_BASE_CPP_PERF_TIMER_WIN32) + +#endif // EMBB_BASE_CPP_PERF_INTERNAL_TIMER_TIMESTAMP_CLOCK_WIN32_H_ diff --git a/base_cpp/perf/include/embb/base/perf/internal/timestamp_counter_posix.h b/base_cpp/perf/include/embb/base/perf/internal/timestamp_counter_posix.h new file mode 100644 index 0000000..54e0b1e --- /dev/null +++ b/base_cpp/perf/include/embb/base/perf/internal/timestamp_counter_posix.h @@ -0,0 +1,150 @@ +#ifndef EMBB_BASE_CPP_PERF_INTERNAL_TIMER_TIMESTAMP_COUNTER_POSIX_H_ +#define EMBB_BASE_CPP_PERF_INTERNAL_TIMER_TIMESTAMP_COUNTER_POSIX_H_ + +#include +#include + +#include +#include + +#include +#if defined(EMBB_BASE_CPP_PERF__ARCH_MIPS) || \ + defined(EMBB_BASE_CPP_PERF__ARCH_ARM) +#include +#endif // ARM <= V3 or MIPS + +namespace embb { +namespace base { +namespace perf { +namespace internal { +namespace timer { + +/** + * @brief Timestamp counter (RDTSC or PMC) for POSIX platforms. + */ +class TimestampCounterPosix : public Timestamp +{ + +private: + + Timestamp::counter_t value; + +public: + + static Timestamp::counter_t frequencyScaling; + + /** + * @brief Serialized RDTSCP (x86, x64) or PMC,PMU (arm6+) + * + * Prevents out-of-order execution to affect timestamps. + * + */ + static inline uint64_t ArchCycleCount() { +#if defined(EMBB_BASE_CPP_PERF__ARCH_X64) + uint64_t rax, rdx; + uint32_t tsc_aux; + __asm__ volatile ("rdtscp\n" : "=a" (rax), "=d" (rdx), "=c" (tsc_aux) : : ); + return (rdx << 32) + rax; +#elif defined(EMBB_BASE_CPP_PERF__ARCH_I386) + int64 ret; + __asm__ volatile ("rdtsc" : "=A" (ret) ); + return ret; +#elif defined(EMBB_BASE_CPP_PERF__ARCH_ARMV6) + uint32_t pmccntr; + uint32_t pmuseren = 1; + uint32_t pmcntenset; + + // Read the user mode perf monitor counter access permissions. + __asm__ volatile ("mrc p15, 0, %0, c9, c14, 0" : "=r" (pmuseren)); + // Set permission flag: + pmuseren &= 0x01; // Set E bit + __asm__ volatile ("mcr p15, 0, %0, c9, c14, 0" : "=r" (pmuseren)); + if (pmuseren & 1) { // Allows reading perfmon counters for user mode code. + __asm__ volatile ("mrc p15, 0, %0, c9, c12, 1" : "=r" (pmcntenset)); + if (pmcntenset & 0x80000000ul) { // Is it counting? + __asm__ volatile ("mrc p15, 0, %0, c9, c13, 0" : "=r" (pmccntr)); + // The counter is set up to count every 64th cycle + return static_cast(pmccntr) * 64; // Should optimize to << 6 + } + else { + return 1; + } + } + else { + return 2; + } +#else // Fallback for architectures that do not provide any low-level counter: +#pragma message "Fallback to generic performance counter implementation" + struct timeval tv; + gettimeofday(&tv, NULL); + return static_cast((tv.tv_sec + tv.tv_usec * 0.000001) * + FrequencyScaling()); +#endif + // Undefined value if perfmon is unavailable: + return 0; + } + +public: + + /** + * @brief Calibrates counts per microscecond. + * + * Used for frequency scaling of RDSTD. + */ + static void Calibrate(unsigned int = 0); + +public: + + inline TimestampCounterPosix() { + value = static_cast(ArchCycleCount()); + } + + inline TimestampCounterPosix(const TimestampCounterPosix & other) + : value(other.value) + { } + + inline TimestampCounterPosix(const counter_t & counterValue) + : value(counterValue) + { } + + inline TimestampCounterPosix & operator=(const TimestampCounterPosix rhs) { + if (this != &rhs) { + value = rhs.value; + } + return *this; + } + + inline const counter_t & Value() const { + return value; + } + + inline static double FrequencyScaling() { + return static_cast(TimestampCounterPosix::frequencyScaling); + } + + inline static double FrequencyPrescale() { + return 1.0f; + } + + inline static const char * TimerName() { +#if defined(EMBB_BASE_CPP_PERF__ARCH_X64) + return "POSIX:X64:RDTSC"; +#elif defined(EMBB_BASE_CPP_PERF__ARCH_I386) + return "POSIX:X86:RDTSC"; +#elif defined(EMBB_BASE_CPP_PERF__ARCH_ARMV6) + return "POSIX:ARM:PMCNT"; +#else + return "POSIX:GENERIC"; +#endif + } + +}; + +} // namespace timer +} // namespace internal +} // namespace perf +} // namespace base +} // namespace embb + +#endif // EMBB_BASE_CPP_PERF_INTERNAL_TIMER_TIMESTAMP_COUNTER_POSIX_H_ + diff --git a/base_cpp/perf/include/embb/base/perf/internal/timestamp_counter_win32.h b/base_cpp/perf/include/embb/base/perf/internal/timestamp_counter_win32.h new file mode 100644 index 0000000..d09d07f --- /dev/null +++ b/base_cpp/perf/include/embb/base/perf/internal/timestamp_counter_win32.h @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2014, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_CPP_PERF_INTERNAL_TIMER_TIMESTAMP_COUNTER_WIN32_H_ +#define EMBB_BASE_CPP_PERF_INTERNAL_TIMER_TIMESTAMP_COUNTER_WIN32_H_ + +#if defined(EMBB_BASE_CPP_PERF_TIMER_WIN32) + +#include + +// Prevent definition of Windows-specific macros 'min' and 'max': +#define NOMINMAX +#include + +namespace embb { +namespace base { +namespace perf { +namespace internal { +namespace timer { + +/** + * @brief Timestamp counter (TSC) for Windows platforms. + */ +class TimestampCounterWin32 : public Timestamp +{ + +private: + + typedef TimestampCounterWin32 self_t; + + static counter_t frequencyScaling; + +private: + + Timestamp::counter_t value; + +public: + + inline static void Calibrate(unsigned int = 0) { + // Resolves ticks per second: + LARGE_INTEGER freq_li; + LONGLONG freq_value; + + QueryPerformanceFrequency(&freq_li); + freq_value = freq_li.QuadPart; + + self_t::frequencyScaling = static_cast(freq_value); + } + +public: + + inline TimestampCounterWin32() { + LARGE_INTEGER count_now; + LONGLONG count_now_value; + QueryPerformanceCounter(&count_now); + count_now_value = count_now.QuadPart; + value = static_cast(count_now_value); + } + + inline TimestampCounterWin32(const self_t & other) + : value(other.value) + { } + + inline TimestampCounterWin32(const Timestamp::counter_t & counterValue) + : value(counterValue) + { } + + inline self_t & operator=(const self_t & rhs) { + if (this != &rhs) { + value = rhs.value; + } + return *this; + } + + inline const Timestamp::counter_t & Value() const { + return value; + } + + inline static double FrequencyScaling() { + if (self_t::frequencyScaling == 0) { + Calibrate(); + } + return static_cast(self_t::frequencyScaling); + } + + inline static double FrequencyPrescale() { + // Adjusting frequency from seconds to microseconds scale: + return 1000000.0f; + } + + inline static const char * TimerName() { + return "Win32:QueryPerformanceCounter"; + } + +}; + +} // namespace timer +} // namespace internal +} // namespace perf +} // namespace base +} // namespace embb + +#endif // EMBB_BASE_CPP_BENCHMARK_INTERNAL_TIMESTAMP_WIN32_H_ + +#endif // _WIN32 diff --git a/base_cpp/perf/include/embb/base/perf/internal/timestamp_papi-inl.h b/base_cpp/perf/include/embb/base/perf/internal/timestamp_papi-inl.h new file mode 100644 index 0000000..a619ad0 --- /dev/null +++ b/base_cpp/perf/include/embb/base/perf/internal/timestamp_papi-inl.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2014, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_CPP_PERF_INTERNAL_TIMER_TIMESTAMP_PAPI_INL_H_ +#define EMBB_BASE_CPP_PERF_INTERNAL_TIMER_TIMESTAMP_PAPI_INL_H_ + +#if defined(EMBB_BASE_CPP_PERF_TIMER_PAPI) + +#include + +namespace embb { +namespace base { +namespace perf { +namespace internal { +namespace timer { + +template +int TimestampPAPI::timer_mode = 0; + +} // namespace timer +} // namespace internal +} // namespace perf +} // namespace base +} // namespace embb + +#endif // EMBB_BASE_CPP_PERF_TIMER_PAPI + +#endif // EMBB_BASE_CPP_PERF_INTERNAL_TIMER_TIMESTAMP_PAPI_INL_H_ + diff --git a/base_cpp/perf/include/embb/base/perf/internal/timestamp_papi.h b/base_cpp/perf/include/embb/base/perf/internal/timestamp_papi.h new file mode 100644 index 0000000..be5cd3a --- /dev/null +++ b/base_cpp/perf/include/embb/base/perf/internal/timestamp_papi.h @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2014, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_CPP_PERF_INTERNAL_TIMER_TIMESTAMP_PAPI_H_ +#define EMBB_BASE_CPP_PERF_INTERNAL_TIMER_TIMESTAMP_PAPI_H_ + +#include +#include +#include +#include + +#include + +#include +#include +#include + +namespace embb { +namespace base { +namespace perf { +namespace internal { +namespace timer { + +/** + * @brief Timestamp counter (RDTSC or PMC) for POSIX platforms. + */ +template +class TimestampPAPI : public Timestamp +{ + +private: + + Timestamp::counter_t value; + + static int timer_mode; + +public: + + static Timestamp::counter_t frequencyScaling; + +public: + + /** + * @brief Initializes the PAPI library. + */ + static void Calibrate(unsigned int arg = 0) { + timer_mode = arg; + int retval = PAPI_library_init(PAPI_VER_CURRENT); + if (retval != PAPI_VER_CURRENT && retval > 0) { + throw ::std::runtime_error("PAPI version mismatch"); + } + else if (retval < 0) { + throw ::std::runtime_error("PAPI init failed"); + } + } + +public: + + TimestampPAPI() { + if (TTimer == TimeMeasure::Clock) { + if (timer_mode == 0) { + value = static_cast(PAPI_get_real_usec()); + } + else { + value = static_cast(PAPI_get_virt_usec()); + } + } + else if (TTimer == TimeMeasure::Counter) { + if (timer_mode == 0) { + value = static_cast(PAPI_get_real_cyc()); + } + else { + value = static_cast(PAPI_get_virt_cyc()); + } + } + } + + inline TimestampPAPI(const TimestampPAPI & other) + : value(other.value) + { } + + inline TimestampPAPI(const counter_t & counterValue) + : value(counterValue) + { } + + inline TimestampPAPI & operator=(const TimestampPAPI rhs) { + if (this != &rhs) { + value = rhs.value; + } + return *this; + } + + inline const counter_t & Value() const { + return value; + } + + inline static double FrequencyScaling() { + if (TTimer == TimeMeasure::Counter) { + return 996.0f; // clock speed on Wandboard + } + return 1.0f; + } + + inline static double FrequencyPrescale() { + return 1.0f; + } + + inline static const char * TimerName() { + return "PAPI"; + } + +}; + +} // namespace timer +} // namespace internal +} // namespace perf +} // namespace base +} // namespace embb + +#include + +#endif // EMBB_BASE_CPP_PERF_INTERNAL_TIMER_TIMESTAMP_PAPI_H_ + diff --git a/base_cpp/perf/include/embb/base/perf/measure.h b/base_cpp/perf/include/embb/base/perf/measure.h new file mode 100644 index 0000000..e4fa621 --- /dev/null +++ b/base_cpp/perf/include/embb/base/perf/measure.h @@ -0,0 +1,20 @@ +#ifndef _EMBB_BASE_CPP_PERF_MEASURE_H +#define _EMBB_BASE_CPP_PERF_MEASURE_H + +#include +#include + +namespace embb { +namespace base { +namespace perf { + +struct Measure { + Duration duration; + PerformanceMetrics metrics; +}; + +} // namespace perf +} // namespace base +} // namespace embb + +#endif // _EMBB_BASE_CPP_PERF_DURATION_H diff --git a/base_cpp/perf/include/embb/base/perf/perf_test_unit.h b/base_cpp/perf/include/embb/base/perf/perf_test_unit.h new file mode 100644 index 0000000..e7dfaf9 --- /dev/null +++ b/base_cpp/perf/include/embb/base/perf/perf_test_unit.h @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2014, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_PERF_PERF_TEST_UNIT_H_ +#define EMBB_BASE_PERF_PERF_TEST_UNIT_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define THIS_DOMAIN_ID 1 +#define THIS_NODE_ID 1 + +namespace embb { +namespace base { +namespace perf { + +/** + * \defgroup CPP_BASE_PERF Performance Tests + * + * Performance Test Framework + * + * \ingroup CPP_BASE + */ +/** + * Performance Test Unit + * + * Base unit of any test (Speedup Test, Performance Test, ...). Takes a + * non-copyable Functor as template argument and executes it \c iteration_count + * times on \c thread_count worker threads. + * + * If \c thread_count equals 0, EMBB is not initialized and the Functor is + * executed without EMBB support. + * + * \notthreadsafe + * \ingroup CPP_BASE_PERF + */ +template +class PerfTestUnit : public partest::TestUnit { + public: + /** + * Constructs PerfTestUnit and sets up partest::TestUnit with Functor \c F. + */ + explicit PerfTestUnit( + size_t thread_count = partest::TestSuite::GetDefaultNumThreads(), + size_t iteration_count = partest::TestSuite::GetDefaultNumIterations()) : + partest::TestUnit("PTU"), duration_(0), thread_count_(thread_count), + iteration_count_(iteration_count) { + /* TODO: move creation and deletion of functor data (e.g. vector of doubles) + * to functor-specific Pre/Post methods to avoid memory shortage */ + /* TODO: create possibility to initialize memory in these functor-specific + * Pre/Post methods to avoid first-touch problem. */ + func = new F; + Pre(&PerfTestUnit::Tic, this); + Add(&F::Run, func, 1, iteration_count_); + Post(&PerfTestUnit::Toc, this); + } + + /** + * Destructs PerfTestUnit + */ + ~PerfTestUnit() { + delete func; + } + + /** + * Returns duration of this unit in microseconds. + * \return Duration of this unit in microseconds. + */ + double GetDuration() const { return duration_; } + + /** + * Returns thread count of this unit. + * \return Thread count of this unit. + */ + size_t GetThreadCount() const { return thread_count_; } + + /** + * Returns iteration count of this unit. + * \return Iteration count of this unit. + */ + size_t GetIterationCount() const { return iteration_count_; } + + private: + /** + * Sets up EMBB and starts timer. + */ + void Tic() { + /* if thread_count equals 0, run without EMBB */ + if (thread_count_ > 0) { + /* initialize EMBB with thread_count worker threads */ + embb::base::CoreSet core_set_(false); + for (unsigned int i = 0; (i < embb::base::CoreSet::CountAvailable()) && + (i < thread_count_); i++) { + core_set_.Add(i); + } + embb::mtapi::Node::Initialize(THIS_DOMAIN_ID, THIS_NODE_ID, core_set_, + MTAPI_NODE_MAX_TASKS_DEFAULT, + MTAPI_NODE_MAX_GROUPS_DEFAULT, + MTAPI_NODE_MAX_QUEUES_DEFAULT, + MTAPI_NODE_QUEUE_LIMIT_DEFAULT, + MTAPI_NODE_MAX_PRIORITIES_DEFAULT); + } + /* start timer */ + timer_ = Timer(); + } + + /** + * Stops timer and resets EMBB */ + void Toc() { + /* stop timer */ + duration_ = timer_.Elapsed(); + /* execute EMBB Finalize (if EMBB was initialized) */ + if (thread_count_ > 0) { + embb::mtapi::Node::Finalize(); + /* reset internal thread count in EMBB. required in order to avoid + * lock-ups */ + /* TODO: Talk to TobFuchs about nicer implementation */ + embb_internal_thread_index_reset(); + } + } + + double duration_; + size_t thread_count_; + size_t iteration_count_; + Timer timer_; + F *func; + + /* prohibit copy and assignment */ + PerfTestUnit(const PerfTestUnit &other); + PerfTestUnit& operator=(const PerfTestUnit &other); +}; + +} /* perf */ +} /* base */ +} /* embb */ + +#endif /* EMBB_BASE_PERF_PERF_TEST_UNIT_H_ */ diff --git a/base_cpp/perf/include/embb/base/perf/performance_metrics.h b/base_cpp/perf/include/embb/base/perf/performance_metrics.h new file mode 100644 index 0000000..4e2f194 --- /dev/null +++ b/base_cpp/perf/include/embb/base/perf/performance_metrics.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2014, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_CPP_PERF_PERFORMANCE_METRICS_H_ +#define EMBB_BASE_CPP_PERF_PERFORMANCE_METRICS_H_ + +namespace embb { +namespace base { +namespace perf { + +struct PerformanceMetrics { + float real_time; + float proc_time; + float mflops; + long long flpins; +}; + +} // namespace perf +} // namespace base +} // namespace embb + +#endif // EMBB_BASE_CPP_PERF_PERFORMANCE_METRICS_H_ + diff --git a/base_cpp/perf/include/embb/base/perf/performance_test.h b/base_cpp/perf/include/embb/base/perf/performance_test.h new file mode 100644 index 0000000..aebfe19 --- /dev/null +++ b/base_cpp/perf/include/embb/base/perf/performance_test.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2014, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_PERF_PERFORMANCE_TEST_H_ +#define EMBB_BASE_PERF_PERFORMANCE_TEST_H_ + +#include +#include + +namespace embb { +namespace base { +namespace perf { + +/** + * Performance Test + * + * Runs a non-copyable Functor \c iteration_count times with \c thread_count + * working threads and reports the duration. + * + * \notthreadsafe + * \ingroup CPP_BASE_PERF + */ +template +class PerformanceTest : public partest::TestCase { + public: + /** + * Constructs PerformanceTest. + */ + explicit PerformanceTest( + size_t thread_count = partest::TestSuite::GetDefaultNumThreads(), + size_t iteration_count = partest::TestSuite::GetDefaultNumIterations()) : + partest::TestCase() { + /* maximum one thread per available core */ + size_t threads = std::min(thread_count, + embb::base::CoreSet::CountAvailable()); + + unit = &CreateUnit< PerfTestUnit >(threads, iteration_count); + } + + /** + * Destructs PerformanceTest. + */ + ~PerformanceTest() { + PrintReport(std::cout); + } + + /** + * Prints the durations of all units in comma separated format. + */ + void PrintReport(std::ostream &ostr) const { + /* print execution duration */ + ostr << "P" << unit->GetThreadCount << std::endl << unit->GetDuration() + << std::endl; + } + + private: + PerfTestUnit *unit; + + /* prohibit copy and assignment */ + PerformanceTest(const PerformanceTest &other); + PerformanceTest& operator=(const PerformanceTest &other); +}; + +} /* perf */ +} /* base */ +} /* embb */ + +#endif /* EMBB_BASE_PERF_PERFORMANCE_TEST_H_ */ diff --git a/base_cpp/perf/include/embb/base/perf/performance_test_runner.h b/base_cpp/perf/include/embb/base/perf/performance_test_runner.h new file mode 100644 index 0000000..a0e4ec0 --- /dev/null +++ b/base_cpp/perf/include/embb/base/perf/performance_test_runner.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2014, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_PERF_PERFORMANCE_TEST_RUNNER_H_ +#define EMBB_BASE_PERF_PERFORMANCE_TEST_RUNNER_H_ + +#include + +namespace embb { +namespace base { +namespace perf { + +class PerformanceTestRunner { + +public: + + PerformanceTestRunner( + const embb::base::perf::CallArgs & args) : + cargs(args) { + } + virtual ~PerformanceTestRunner() { } + virtual void Run() = 0; + +protected: + + inline const embb::base::perf::CallArgs & Args() const { + return cargs; + } + +private: + + const embb::base::perf::CallArgs & cargs; +}; + +} // namespace perf +} // namespace base +} // namespace embb + +#endif \ No newline at end of file diff --git a/base_cpp/perf/include/embb/base/perf/speedup_test.h b/base_cpp/perf/include/embb/base/perf/speedup_test.h new file mode 100644 index 0000000..a856dd7 --- /dev/null +++ b/base_cpp/perf/include/embb/base/perf/speedup_test.h @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2014, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_PERF_SPEEDUP_TEST_H_ +#define EMBB_BASE_PERF_SPEEDUP_TEST_H_ + +#include +#include +#include +#include +#include + +namespace embb { +namespace base { +namespace perf { + +/** + * Speedup Test + * + * Runs a non-copyable Functor \c iteration_count times with 1...\c thread_count + * working threads (log2 steps) and reports the duration. + * + * Executes another Functor without EMBB. This can be used to compare a serial + * version of an algorithm with its parallel counterpart. + * + * \notthreadsafe + * \ingroup CPP_BASE_PERF + */ +template +class SpeedupTest : public partest::TestCase { + public: + /** + * Constructs SpeedupTest and creates test units. + */ + explicit SpeedupTest( + size_t max_thread_count = partest::TestSuite::GetDefaultNumThreads(), + size_t iteration_count = partest::TestSuite::GetDefaultNumIterations()) : + partest::TestCase() { + /* maximum one thread per available core */ + size_t threads = std::min( + max_thread_count, + embb::base::CoreSet::CountAvailable()); + + std::cout << "Test configuration ------------------------------------" << std::endl; + std::cout << " Num threads: " << threads << std::endl; + std::cout << " Iterations: " << iteration_count << std::endl; + + /* create unit for serial version */ + ser_unit_ = &CreateUnit< PerfTestUnit >(0, iteration_count); + /* create log2(threads)+1 units for parallel version */ + for (size_t i = 1; i <= threads; i = i * 2) { + par_units_.push_back( + &CreateUnit< PerfTestUnit >(i, iteration_count)); + } + } + + /** + * Destructs SpeedupTest. + */ + ~SpeedupTest() { + } + + /** + * Prints the durations of all units in comma separated format. + */ + void PrintReport(std::ostream &ostr) { + /* print sample row for sequential run (degree 0): */ + ostr << "0," + << std::fixed << std::setprecision(2) + << ser_unit_->GetDuration() << std::endl; + /* print sample rows for parallel runs (degree > 0): */ + for (int i = 0; i < par_units_.size(); ++i) { + ostr << std::fixed << par_units_[i]->GetThreadCount() + << "," + << std::fixed << std::setprecision(2) + << par_units_[i]->GetDuration() + << std::endl; + } + } + + private: + std::vector *> par_units_; + PerfTestUnit *ser_unit_; + + /* prohibit copy and assignment */ + SpeedupTest(const SpeedupTest &other); + SpeedupTest& operator=(const SpeedupTest &other); +}; + +} /* perf */ +} /* base */ +} /* embb */ + +#endif /* EMBB_BASE_PERF_SPEEDUP_TEST_H_ */ diff --git a/base_cpp/perf/include/embb/base/perf/time_measure.h b/base_cpp/perf/include/embb/base/perf/time_measure.h new file mode 100644 index 0000000..82f54ff --- /dev/null +++ b/base_cpp/perf/include/embb/base/perf/time_measure.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2014, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_CPP_PERF_TIME_MEASURE_H_ +#define EMBB_BASE_CPP_PERF_TIME_MEASURE_H_ + +namespace embb { +namespace base { +namespace perf { + +class TimeMeasure { + public: + typedef enum { + Counter = 0, + Clock = 1 + } MeasureMode; + + typedef enum { + Cycles = 0, + Time = 1 + } MeasureDomain; +}; + +} // namespace perf +} // namespace base +} // namespace embb + +#endif // EMBB_BASE_CPP_PERF_TIME_MEASURE_H_ + diff --git a/base_cpp/perf/include/embb/base/perf/timer.h b/base_cpp/perf/include/embb/base/perf/timer.h new file mode 100644 index 0000000..fa6be8c --- /dev/null +++ b/base_cpp/perf/include/embb/base/perf/timer.h @@ -0,0 +1,267 @@ +/* + * Copyright (c) 2014, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_CPP_PERF_TIMER_H_ +#define EMBB_BASE_CPP_PERF_TIMER_H_ + +#ifdef EMBB_PLATFORM_THREADING_POSIXTHREADS +// #define EMBB_BASE_CPP_PERF_TIMER_PAPI +#endif + +#include +#include +#include + +#include + +#include + +#if defined(EMBB_BASE_CPP_PERF_TIMER_PAPI) +# include +#endif +#if defined(EMBB_PLATFORM_THREADING_WINTHREADS) +# include +# include +#elif defined(EMBB_PLATFORM_THREADING_POSIXTHREADS) +# include +# include +#endif + +namespace embb { +namespace base { +namespace perf { + +class Timer { + public: + typedef Timestamp::counter_t timestamp_t; + + private: + typedef Timer self_t; + static embb::base::perf::TimeMeasure::MeasureMode Type; + timestamp_t timestampStart; + + private: +#if defined(EMBB_BASE_CPP_PERF_TIMER_PAPI) +// PAPI support, use optimized performance measurements from PAPI + typedef embb::base::perf::internal::timer::TimestampPAPI + TimestampClockBased; + typedef embb::base::perf::internal::timer::TimestampPAPI + TimestampCounterBased; +#else // No PAPI + +# if defined(EMBB_BASE_CPP_PERF_TIMER_WIN32) + // Windows: + typedef embb::base::perf::internal::timer::TimestampCounterWin32 + TimestampCounterBased; + typedef embb::base::perf::internal::timer::TimestampClockWin32 + TimestampClockBased; +# else + // POSIX: + typedef embb::base::perf::internal::timer::TimestampCounterPosix + TimestampCounterBased; + typedef embb::base::perf::internal::timer::TimestampClockPosix + TimestampClockBased; +# endif // WIN32 / POSIX + +#endif // No PAPI + + public: + inline Timer() { + timestampStart = Timer::Now(); + } + + inline Timer(const self_t & other) : timestampStart(other.timestampStart) + { } + + inline Timer & operator=(const self_t & other) { + if (this != &other) { + timestampStart = other.timestampStart; + } + return *this; + } + + /** + * @brief Microeconds elapsed since instantiation of this Timer object. + */ + inline double Elapsed() const { + timestamp_t now; + + if (Timer::Type == TimeMeasure::Counter) { + TimestampCounterBased timestamp; + now = timestamp.Value(); + return (static_cast(now - timestampStart) * + static_cast(TimestampCounterBased::FrequencyPrescale())) / + static_cast(TimestampCounterBased::FrequencyScaling()); + } + if (Timer::Type == TimeMeasure::Clock) { + TimestampClockBased timestamp; + now = timestamp.Value(); + return (static_cast(now - timestampStart) * + static_cast(TimestampClockBased::FrequencyPrescale())) / + static_cast(TimestampClockBased::FrequencyScaling()); + } + return 0.0f; + } + + /** + * Returns timestamp from instantiation of this Timer. + */ + inline const timestamp_t & Start() const { + return timestampStart; + } + + /** + * Microseconds elapsed since given timestamp. + */ + inline static double ElapsedSince(timestamp_t timestamp) { + if (Timer::Type == TimeMeasure::Counter) { + TimestampCounterBased now; + return (static_cast(now.Value() - timestamp) * + static_cast(TimestampCounterBased::FrequencyPrescale())) / + static_cast(TimestampCounterBased::FrequencyScaling()); + } + if (Timer::Type == TimeMeasure::Clock) { + TimestampClockBased now; + return (static_cast(now.Value() - timestamp) * + static_cast(TimestampClockBased::FrequencyPrescale())) / + static_cast(TimestampClockBased::FrequencyScaling()); + } + return 0.0f; + } + + /** + * Produces current timestamp. + */ + inline static timestamp_t Now() { + if (Timer::Type == TimeMeasure::Counter) { + TimestampCounterBased timestamp; + return timestamp.Value(); + } + if (Timer::Type == TimeMeasure::Clock) { + TimestampClockBased timestamp; + return timestamp.Value(); + } + return 0; + } + + /** + * Convert interval of two timestamp values to mircoseconds. + */ + inline static double FromInterval( + const timestamp_t & start, + const timestamp_t & end) + { + if (Timer::Type == TimeMeasure::Counter) { + return (static_cast(end - start) * + static_cast(TimestampCounterBased::FrequencyPrescale())) / + static_cast(TimestampCounterBased::FrequencyScaling()); + } + if (Timer::Type == TimeMeasure::Clock) { + return (static_cast(end - start) * + static_cast(TimestampClockBased::FrequencyPrescale())) / + static_cast(TimestampClockBased::FrequencyScaling()); + } + return -1.0f; + } + + /** + * Convert interval of two timestamp values to mircoseconds. + */ + inline static double FromInterval( + const double & start, + const double & end) + { + if (Timer::Type == TimeMeasure::Counter) { + return ((end - start) * + TimestampCounterBased::FrequencyPrescale() / + TimestampCounterBased::FrequencyScaling()); + } + if (Timer::Type == TimeMeasure::Clock) { + return ((end - start) * + TimestampClockBased::FrequencyPrescale() / + TimestampClockBased::FrequencyScaling()); + } + return -1.0f; + } + + inline static void Calibrate( + TimeMeasure::MeasureMode mode, + unsigned int freq = 0) { + Timer::Type = mode; + if (Timer::Type == TimeMeasure::Counter) { + Timer::TimestampCounterBased::Calibrate(freq); + } + else if (Timer::Type == TimeMeasure::Clock) { + Timer::TimestampClockBased::Calibrate(freq); + } + } + + inline static const char * TimerName() { + if (Timer::Type == TimeMeasure::Counter) { + return TimestampCounterBased::TimerName(); + } + if (Timer::Type == TimeMeasure::Clock) { + return TimestampClockBased::TimerName(); + } + return "Undefined"; + } + + inline static Timestamp::counter_t TimestampInfinity() { + if (Timer::Type == TimeMeasure::Counter) { + return TimestampCounterBased::TimestampInfinity(); + } + if (Timer::Type == TimeMeasure::Clock) { + return TimestampClockBased::TimestampInfinity(); + } + return LLONG_MAX; + } + + inline static Timestamp::counter_t TimestampNegInfinity() { + if (Timer::Type == TimeMeasure::Counter) { + return Timer::TimestampCounterBased::TimestampNegInfinity(); + } + if (Timer::Type == TimeMeasure::Clock) { + return Timer::TimestampClockBased::TimestampNegInfinity(); + } + return 0; + } + + inline static double FrequencyScaling() { + if (Timer::Type == TimeMeasure::Counter) { + return Timer::TimestampCounterBased::FrequencyScaling(); + } + if (Timer::Type == TimeMeasure::Clock) { + return Timer::TimestampClockBased::FrequencyScaling(); + } + return 1.0f; + } +}; + +} // namespace perf +} // namespace base +} // namespace embb + +#endif // EMBB_BASE_CPP_PERF_TIMER_H_ diff --git a/base_cpp/perf/include/embb/base/perf/timestamp.h b/base_cpp/perf/include/embb/base/perf/timestamp.h new file mode 100644 index 0000000..b6b6abb --- /dev/null +++ b/base_cpp/perf/include/embb/base/perf/timestamp.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2014, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EMBB_BASE_CPP_PERF_TIMER_H_ +#define EMBB_BASE_CPP_PERF_TIMER_H_ + +namespace embb { +namespace base { +namespace perf { + +class TimeMeasure { + public: + typedef enum { + Counter = 0, + Clock = 1 + } MeasureMode; +}; + +} // namespace perf +} // namespace base +} // namespace embb + +#endif // EMBB_BASE_CPP_PERF_TIMER_H_ + diff --git a/base_cpp/perf/src/call_args.cc b/base_cpp/perf/src/call_args.cc new file mode 100644 index 0000000..e204a2a --- /dev/null +++ b/base_cpp/perf/src/call_args.cc @@ -0,0 +1,100 @@ + +#include +#include +#include +#include +#include +#include + +namespace embb { +namespace base { +namespace perf { + +void CallArgs::Parse(int argc, char * argv[]) { + // Set config from command line arguments: + for (int paramIndex = 1; paramIndex < argc; paramIndex += 2) { + // Max. number of threads to resolve speedup: + if (std::string(argv[paramIndex]) == "-t") { + size_t threads_param = static_cast( + atoi(argv[paramIndex + 1])); + if (threads_param > 0 && threads_param < max_threads) { + max_threads = threads_param; + } + } + // Test vector size: + if (std::string(argv[paramIndex]) == "-n") { + size_t vsize_param = static_cast( + atoi(argv[paramIndex + 1])); + if (vsize_param > 0) { + vector_size = vsize_param; + } + } + // Performance counter scaling: + if (std::string(argv[paramIndex]) == "-f") { + unsigned int scale_param = static_cast( + atoi(argv[paramIndex + 1])); + if (scale_param > 0) { + counter_scale = scale_param; + } + } + // Element type: + if (std::string(argv[paramIndex]) == "-e") { + element_type = UNDEFINED_SCALAR_TYPE; + ::std::string type = argv[paramIndex + 1]; + if (type == "float") { + element_type = FLOAT; + } + else if (type == "double") { + element_type = DOUBLE; + } + } + // Stress type: + if (std::string(argv[paramIndex]) == "-s") { + stress_type = UNDEFINED_STRESS_TYPE; + ::std::string type = argv[paramIndex + 1]; + if (type == "cpu") { + stress_type = CPU_STRESS; + } + else if (type == "ram") { + stress_type = RAM_STRESS; + } + } + // Test load factor: + if (std::string(argv[paramIndex]) == "-l") { + load_factor = static_cast( + atoi(argv[paramIndex + 1])); + } + // Additional test parameter: + if (std::string(argv[paramIndex]) == "-p") { + parallel_base_ref = atoi(argv[paramIndex + 1]); + } + // Sanitizing and error handling: + if (element_type == UNDEFINED_SCALAR_TYPE) { + throw ::std::runtime_error( + "Invalid setting for element type (-e int|float|double)"); + } + if (stress_type == UNDEFINED_STRESS_TYPE) { + throw ::std::runtime_error( + "Invalid setting for stress test type (-s ram|cpu)"); + } + } + // Calibrate performance time sampling: + embb::base::perf::Timer::Calibrate( + embb::base::perf::TimeMeasure::Counter, + CounterScale()); +} + +void CallArgs::Print(std::ostream & os) { + os << "Max. threads: (-t) " << MaxThreads() << std::endl + << "Vector size: (-n) " << VectorSize() << std::endl + << "Load factor: (-l) " << LoadFactor() << std::endl + << "Element type: (-e) " << ElementTypeName() << std::endl + << "Stress mode: (-s) " << StressModeName() << std::endl + << "Serial base ref: (-p) " << ParallelBaseReference() << std::endl + << "Time sampling: (-f) " << embb::base::perf::Timer::TimerName() + << std::endl; +} + +} // namespace perf +} // namespace base +} // namespace embb diff --git a/base_cpp/perf/src/timer.cc b/base_cpp/perf/src/timer.cc new file mode 100644 index 0000000..9d2cad2 --- /dev/null +++ b/base_cpp/perf/src/timer.cc @@ -0,0 +1,13 @@ + +#include +#include + +namespace embb { +namespace base { +namespace perf { + +TimeMeasure::MeasureMode Timer::Type = TimeMeasure::Counter; + +} // namespace perf +} // namespace base +} // namespace embb diff --git a/base_cpp/perf/src/timestamp_clock_posix.cc b/base_cpp/perf/src/timestamp_clock_posix.cc new file mode 100644 index 0000000..e6792c6 --- /dev/null +++ b/base_cpp/perf/src/timestamp_clock_posix.cc @@ -0,0 +1,192 @@ + +#include + +#if defined(EMBB_BASE_CPP_PERF_TIMER_POSIX) || \ + defined(EMBB_BASE_CPP_PERF_TIMER_OSX) || \ + defined(EMBB_BASE_CPP_PERF_TIMER_UX) + +#include + +#include + +// POSIX standard header +#include /* POSIX flags */ +#include /* clock_gettime(), time(), needs librt.a */ +#include /* gethrtime(), gettimeofday() */ + +#if defined(__MACH__) && defined(__APPLE__) +// OS X / Mach +#include +#include +#endif + +namespace embb { +namespace base { +namespace perf { +namespace internal { +namespace timer { + +TimestampClockPosix::TimestampClockPosix() { +// {{{{ +#if defined(EMBB_BASE_CPP_PERF_TIMER_UX) +// HP-UX, Solaris + value = static_cast(gethrtime()); + +#elif defined(EMBB_BASE_CPP_PERF_TIMER_OSX) +// OS X + static double timeConvert = 0.0; + if (timeConvert == 0.0) + { + mach_timebase_info_data_t timeBase; + (void)mach_timebase_info(&timeBase); + timeConvert = (double)timeBase.numer / + (double)timeBase.denom; + } + value = static_cast( + static_cast(mach_absolute_time() * timeConvert)); + +#elif defined(EMBB_BASE_CPP_PERF_TIMER_POSIX) +// POSIX +#if defined(_POSIX_TIMERS) && (_POSIX_TIMERS > 0) +// POSIX clock_gettime + struct timespec ts; + + if (clockId != (clockid_t)-1 && clock_gettime(clockId, &ts) != -1) { + value = static_cast( + static_cast(ts.tv_sec * 1000000) + + static_cast(ts.tv_nsec / 1000)); + return; + } + +#endif +// No support for POSIX clock_gettime + // Imprecise fallback to wall-clock time + struct timeval tm; + gettimeofday(&tm, NULL); + value = static_cast( + static_cast(ts.tv_sec * 1000000) + + static_cast(ts.tv_nsec / 1000)); +#else +// No POSIX-compliant time mechanism found. + throw std::runtime_error("Could not resolve timer"); +#endif +// }}} +} + +const char * +TimestampClockPosix::clockModeNames[] = { +// {{{ + "Default", + "GENERIC", + "UX", + "MACH", + "THREAD_CPUTIME", + "PROC_CPUTIME", + "HIGHRES", + "MONOTONIC_PRECISE", + "MONOTONIC_RAW", + "MONOTONIC", + "REALTIME" +// }}} +}; + +TimestampClockPosix::ClockDef +TimestampClockPosix::availableModes[] = { +// {{{ + TimestampClockPosix::ClockDef( + UNDEFINED_CLOCK, static_cast(-1)), +#if defined(EMBB_BASE_CPP_PERF_TIMER_UX) + TimestampClockPosix::ClockDef( + UX, static_cast(-1)), +#endif +#if defined(EMBB_BASE_CPP_PERF_TIMER_OSX) + TimestampClockPosix::ClockDef( + MACH, static_cast(-1)), +#endif +#if defined(_CLOCK_HIGHRES) + TimestampClockPosix::ClockDef( + HIGHRES, CLOCK_HIGHRES), +#endif +#if defined(_POSIX_MONOTONIC_CLOCK) +# if defined(EMBB_BASE_CPP_PERF_TIMER_FREEBSD) + TimestampClockPosix::ClockDef( + MONOTONIC_PRECISE, CLOCK_MONOTONIC_PRECISE), + ///< FreeBSD specific, correcponds to MONOTONIC on Linux +# endif // FreeBSD +# if defined(EMBB_BASE_CPP_PERF_TIMER_LINUX) + TimestampClockPosix::ClockDef( + MONOTONIC_RAW, CLOCK_MONOTONIC_RAW), + ///< Linux specific, like MONOTONIC but without NTP adjustment +# endif // Linux + TimestampClockPosix::ClockDef( + MONOTONIC, CLOCK_MONOTONIC), +#endif // _POSIX_MONOTONIC_CLOCK +#if defined(_POSIX_THREAD_CPUTIME) + TimestampClockPosix::ClockDef( + THREAD_CPUTIME, CLOCK_THREAD_CPUTIME_ID), +#endif +#if defined(_POSIX_CPUTIME) + TimestampClockPosix::ClockDef( + PROC_CPUTIME, CLOCK_PROCESS_CPUTIME_ID), +#endif + TimestampClockPosix::ClockDef( + REALTIME, CLOCK_REALTIME), + TimestampClockPosix::ClockDef( + GENERIC_CLOCK, static_cast(-1)) +// }}} +}; + +void TimestampClockPosix:: +Calibrate(unsigned int mode) { +// {{{ + const unsigned int lastAvMode = static_cast( + TimestampClockPosix::GENERIC_CLOCK); + // Default to second index in available modes, which + // is the first and preferred clock type + unsigned int selectedModeIndex = 1; + // Iterate over all available clock types: + std::cout << "Available modes: "; + for (unsigned int avModeIdx = 1; + TimestampClockPosix::availableModes[avModeIdx].first != lastAvMode; + ++avModeIdx) { + unsigned int modeNum = TimestampClockPosix::availableModes[avModeIdx].first; + if (modeNum == mode) { + // Selected mode id is contained in available modes + selectedModeIndex = avModeIdx; + } + std::cout << TimestampClockPosix::clockModeNames[modeNum] + << "(" << modeNum << ") "; + } + std::cout << std::endl; + + clockMode = TimestampClockPosix::availableModes[selectedModeIndex].first; + clockId = TimestampClockPosix::availableModes[selectedModeIndex].second; + // Print mode that finally has been activated: + unsigned int modeNum = static_cast(clockMode); + std::cout << "Active mode: " + << TimestampClockPosix::clockModeNames[modeNum] + << "(" << modeNum << ")" << std::endl; + // Print resolution of the active clock: + struct timespec res; + if (clock_getres(clockId, &res) == 0) { + std::cout << "Resolution: " << res.tv_nsec << "ns" << std::endl; + } +// }}} +} + +TimestampClockPosix::ClockMode +TimestampClockPosix::clockMode = TimestampClockPosix::UNDEFINED_CLOCK; + +clockid_t +TimestampClockPosix::clockId = static_cast(-1); + +Timestamp::counter_t +TimestampClockPosix::frequencyScaling = 1; + +} // namespace timer +} // namespace internal +} // namespace perf +} // namespace base +} // namespace embb + +#endif // POSIX || OSX || UX diff --git a/base_cpp/perf/src/timestamp_counter_posix.cc b/base_cpp/perf/src/timestamp_counter_posix.cc new file mode 100644 index 0000000..c6a7032 --- /dev/null +++ b/base_cpp/perf/src/timestamp_counter_posix.cc @@ -0,0 +1,30 @@ + +#include + +#if defined(EMBB_BASE_CPP_PERF_TIMER_POSIX) || \ + defined(EMBB_BASE_CPP_PERF_TIMER_UX) + +#include + +namespace embb { +namespace base { +namespace perf { +namespace internal { +namespace timer { + +void TimestampCounterPosix::Calibrate(unsigned int freq) { + frequencyScaling = freq == 0 + ? 1900.0f + : static_cast(freq); +} + +Timestamp::counter_t TimestampCounterPosix::frequencyScaling = 1; + +} // namespace timer +} // namespace internal +} // namespace perf +} // namespace base +} // namespace embb + +#endif // EMBB_BASE_CPP_PERF_TIMER_POSIX || EMBB_BASE_CPP_PERF_TIMER_UX + diff --git a/base_cpp/perf/src/timestamp_counter_win32.cc b/base_cpp/perf/src/timestamp_counter_win32.cc new file mode 100644 index 0000000..e7d84ba --- /dev/null +++ b/base_cpp/perf/src/timestamp_counter_win32.cc @@ -0,0 +1,22 @@ + +#include + +#if defined(EMBB_BASE_CPP_PERF_TIMER_WIN32) + +#include + +namespace embb { +namespace base { +namespace perf { +namespace internal { +namespace timer { + +TimestampCounterWin32::counter_t TimestampCounterWin32::frequencyScaling = 0; + +} // namespace timer +} // namespace internal +} // namespace perf +} // namespace base +} // namespace embb + +#endif // EMBB_BASE_CPP_PERF_TIMER_WIN32