diff options
author | Jon Bratseth <bratseth@yahoo-inc.com> | 2016-06-15 23:09:44 +0200 |
---|---|---|
committer | Jon Bratseth <bratseth@yahoo-inc.com> | 2016-06-15 23:09:44 +0200 |
commit | 72231250ed81e10d66bfe70701e64fa5fe50f712 (patch) | |
tree | 2728bba1131a6f6e5bdf95afec7d7ff9358dac50 /vespalib/src/tests/atomic |
Publish
Diffstat (limited to 'vespalib/src/tests/atomic')
-rw-r--r-- | vespalib/src/tests/atomic/.gitignore | 6 | ||||
-rw-r--r-- | vespalib/src/tests/atomic/CMakeLists.txt | 15 | ||||
-rw-r--r-- | vespalib/src/tests/atomic/DESC | 1 | ||||
-rw-r--r-- | vespalib/src/tests/atomic/FILES | 1 | ||||
-rw-r--r-- | vespalib/src/tests/atomic/atomic_bench.cpp | 104 | ||||
-rw-r--r-- | vespalib/src/tests/atomic/atomic_test.cpp | 338 |
6 files changed, 465 insertions, 0 deletions
diff --git a/vespalib/src/tests/atomic/.gitignore b/vespalib/src/tests/atomic/.gitignore new file mode 100644 index 00000000000..7ec00432065 --- /dev/null +++ b/vespalib/src/tests/atomic/.gitignore @@ -0,0 +1,6 @@ +.depend +Makefile +atomic_test +/atomic_bench +vespalib_atomic_test_app +vespalib_atomic_bench_app diff --git a/vespalib/src/tests/atomic/CMakeLists.txt b/vespalib/src/tests/atomic/CMakeLists.txt new file mode 100644 index 00000000000..c7cffde606c --- /dev/null +++ b/vespalib/src/tests/atomic/CMakeLists.txt @@ -0,0 +1,15 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +vespa_add_executable(vespalib_atomic_test_app + SOURCES + atomic_test.cpp + DEPENDS + vespalib +) +vespa_add_test(NAME vespalib_atomic_test_app COMMAND vespalib_atomic_test_app) +vespa_add_executable(vespalib_atomic_bench_app + SOURCES + atomic_bench.cpp + DEPENDS + vespalib +) +vespa_add_test(NAME vespalib_atomic_bench_app COMMAND vespalib_atomic_bench_app BENCHMARK) diff --git a/vespalib/src/tests/atomic/DESC b/vespalib/src/tests/atomic/DESC new file mode 100644 index 00000000000..ec5b4379673 --- /dev/null +++ b/vespalib/src/tests/atomic/DESC @@ -0,0 +1 @@ +atomic test. Take a look at atomic.cpp for details. diff --git a/vespalib/src/tests/atomic/FILES b/vespalib/src/tests/atomic/FILES new file mode 100644 index 00000000000..83c6c518c67 --- /dev/null +++ b/vespalib/src/tests/atomic/FILES @@ -0,0 +1 @@ +atomic.cpp diff --git a/vespalib/src/tests/atomic/atomic_bench.cpp b/vespalib/src/tests/atomic/atomic_bench.cpp new file mode 100644 index 00000000000..499af1daa65 --- /dev/null +++ b/vespalib/src/tests/atomic/atomic_bench.cpp @@ -0,0 +1,104 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +#include <vespa/fastos/fastos.h> +#include <vespa/log/log.h> +LOG_SETUP("atomic_bench"); +#include <vespa/vespalib/testkit/testapp.h> +#include <vespa/vespalib/util/atomic.h> +#include <vector> +#include <algorithm> +#include <sstream> + + +class Test : public vespalib::TestApp +{ +public: + template<typename C, typename T> + void testInc(size_t threads, size_t loops); + int Main(); +}; + +template <typename T> +class Changer : public FastOS_Runnable +{ +protected: + volatile T * const _idata; + const int _times; +public: + Changer(int times, T *data) + : _idata(data), _times(times) {} +}; + +template <typename T> +class Incrementer : public Changer<T> +{ +public: + Incrementer(int times, T *data) : Changer<T>(times, data) {} + void Run(FastOS_ThreadInterface *, void *) { + using vespalib::Atomic; + for (int i = 0; i < this->_times; ++i) { + Atomic::postInc(this->_idata); + } + } +}; + +template <typename T> +class IncrementerByCmpSwap : public Changer<T> +{ +public: + IncrementerByCmpSwap(int times, T *data) : Changer<T>(times, data) {} + void Run(FastOS_ThreadInterface *, void *) { + using vespalib::Atomic; + T oldVal(0); + for (int i = 0; i < this->_times; ++i) { + do { + oldVal = *this->_idata; + } while ( ! Atomic::cmpSwap(this->_idata, oldVal+1, oldVal)); + } + } +}; + +int +Test::Main() +{ + TEST_INIT("atomic_bench"); + size_t concurrency(1); + size_t numRuns(10000000ul); + size_t benchType(0); + if (_argc > 1) { + benchType = strtoul(_argv[1], NULL, 0); + if (_argc > 2) { + numRuns = strtoul(_argv[2], NULL, 0); + if (_argc > 3) { + concurrency = strtoul(_argv[3], NULL, 0); + } + } + } + LOG(info, "Running test number %ld with %ld loops and concurrency of %ld", benchType, numRuns, concurrency); + if (benchType == 1) { + testInc<IncrementerByCmpSwap<uint64_t>, uint64_t>(concurrency, numRuns); + } else { + testInc<Incrementer<uint64_t>, uint64_t>(concurrency, numRuns); + } + + TEST_FLUSH(); + TEST_DONE(); +} + + +template<typename C, typename T> +void +Test::testInc(size_t numThreads, size_t loopCount) +{ + std::vector<std::unique_ptr<C>> threads3(numThreads); + T uintcounter = 0; + FastOS_ThreadPool tpool3(65000, numThreads); + for (size_t i = 0; i < numThreads; i++) { + threads3[i] = std::make_unique<C>(loopCount, &uintcounter); + tpool3.NewThread(threads3[i].get()); + } + tpool3.Close(); + EXPECT_TRUE(uintcounter == numThreads * loopCount); + TEST_FLUSH(); +} + +TEST_APPHOOK(Test) diff --git a/vespalib/src/tests/atomic/atomic_test.cpp b/vespalib/src/tests/atomic/atomic_test.cpp new file mode 100644 index 00000000000..6a84e7a32fc --- /dev/null +++ b/vespalib/src/tests/atomic/atomic_test.cpp @@ -0,0 +1,338 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +#include <vespa/fastos/fastos.h> +#include <vespa/log/log.h> +LOG_SETUP("atomic_test"); +#include <vespa/vespalib/testkit/testapp.h> +#include <vespa/vespalib/util/atomic.h> +#include <vector> +#include <algorithm> +#include <sstream> + + +class Test : public vespalib::TestApp +{ +public: + template<typename T, typename U> + void testAdd(); + template<typename T, typename U> + void testAddSub(); + template<typename T> + void testInc(); + template<typename T> + void testDec(); + template<typename T> + void testSemantics(); + int Main(); +}; + +static const int numadders = 7; +static const int loopcnt = 100000; + +int +Test::Main() +{ + TEST_INIT("atomic_test"); + + testSemantics<int32_t>(); + testSemantics<int64_t>(); + testAdd<int32_t, uint32_t>(); + testAdd<int64_t, uint64_t>(); + testAddSub<int32_t, uint32_t>(); + testAddSub<int64_t, uint64_t>(); + testInc<uint32_t>(); + testInc<uint64_t>(); + testDec<uint32_t>(); + testDec<uint64_t>(); + + TEST_FLUSH(); + TEST_DONE(); +} + +template<typename T> +void +Test::testSemantics() +{ + using vespalib::Atomic; + volatile T value(0); + EXPECT_EQUAL(0, value); + EXPECT_EQUAL(0, Atomic::postInc(&value)); + EXPECT_EQUAL(1, Atomic::postInc(&value)); + EXPECT_EQUAL(2, value); + EXPECT_EQUAL(2, Atomic::postDec(&value)); + EXPECT_EQUAL(1, value); + EXPECT_EQUAL(1, Atomic::postAdd(&value, 17)); + EXPECT_EQUAL(18, value); + EXPECT_EQUAL(18, Atomic::postAdd(&value, 17)); + EXPECT_EQUAL(35, value); + EXPECT_EQUAL(35, Atomic::postAdd(&value, -7)); + EXPECT_EQUAL(28, value); +} + +class NotAtomic +{ +public: + static inline void add(volatile int *data, int xdelta) { + (*data) += xdelta; + } + static inline void sub(volatile int *data, int xdelta) { + (*data) -= xdelta; + } + static inline void add(volatile unsigned int *data, unsigned int xdelta) { + (*data) += xdelta; + } + static inline void sub(volatile unsigned int *data, unsigned int xdelta) { + (*data) -= xdelta; + } + static inline unsigned int postDec(volatile unsigned int *data) { + return (*data)--; + } + static inline unsigned int postInc(volatile unsigned int *data) { + return (*data)++; + } +}; + +template<typename T, typename U> +class Adder : public FastOS_Runnable +{ +private: + int _added; + const T _toadd; + const int _times; + volatile T * const _idata; + volatile U * const _udata; +public: + Adder(T toadd, int times, T *i, U *u) : + _added(0), + _toadd(toadd), + _times(times), + _idata(i), + _udata(u) + {} + void Run(FastOS_ThreadInterface *, void *) { + using vespalib::Atomic; + for (int i = 0; i < _times; ++i) { + Atomic::add(_idata, _toadd); + Atomic::add(_udata, _toadd); + _added += _toadd; + } + } + int getAdded() { return _added; } +}; + + +template<typename T, typename U> +class Subtracter : public FastOS_Runnable +{ +private: + int _subed; + const T _tosub; + const int _times; + volatile T * const _idata; + volatile U * const _udata; +public: + Subtracter(T tosub, int times, T *i, U *u) : + _subed(0), + _tosub(tosub), + _times(times), + _idata(i), + _udata(u) + {} + void Run(FastOS_ThreadInterface *, void *) { + using vespalib::Atomic; + for (int i = 0; i < _times; ++i) { + Atomic::sub(_idata, _tosub); + Atomic::sub(_udata, _tosub); + _subed += _tosub; + } + } + int getSubtracted() { return _subed; } +}; + +template <typename T> +class Changer : public FastOS_Runnable +{ +protected: + std::vector<T> _counts; + volatile T * const _idata; + const int _times; +public: + Changer(int times, T *data) + : _counts(), _idata(data), _times(times) {} + const std::vector<T> & getCounts() const { return _counts; } +}; + + +template <typename T> +class Incrementer : public Changer<T> +{ +public: + Incrementer(int times, T *data) : Changer<T>(times, data) {} + void Run(FastOS_ThreadInterface *, void *) { + using vespalib::Atomic; + for (int i = 0; i < this->_times; ++i) { + this->_counts.push_back(Atomic::postInc(this->_idata)); + } + } +}; + + +template <typename T> +class Decrementer : public Changer<T> +{ +public: + Decrementer(int times, T *data) : Changer<T>(times, data) {} + void Run(FastOS_ThreadInterface *, void *) { + using vespalib::Atomic; + for (int i = 0; i < this->_times; ++i) { + this->_counts.push_back(Atomic::postDec(this->_idata)); + } + } +}; + + +template<typename T, typename U> +void +Test::testAdd() +{ + Adder<T, U> *threads1[numadders]; + + T intcounter = 0; + U uintcounter = 0; + + FastOS_ThreadPool tpool1(65000, numadders); + for (int i = 0; i < numadders; i++) { + threads1[i] = new Adder<T, U>(2+i, loopcnt, &intcounter, &uintcounter); + tpool1.NewThread(threads1[i]); + } + tpool1.Close(); + T intcorrect = 0; + U uintcorrect = 0; + for (int i = 0; i < numadders; i++) { + intcorrect += threads1[i]->getAdded(); + uintcorrect += threads1[i]->getAdded(); + } + for (int i = 0; i < numadders; i++) { + delete threads1[i]; + } + std::ostringstream os; + os << "intcounter = " << intcounter << ", intcorrect = " << intcorrect; + LOG(debug, "%s", os.str().c_str()); + EXPECT_TRUE( intcounter == intcorrect); + std::ostringstream uos; + uos << "uintcounter = " << uintcounter << ", uintcorrect = " << uintcorrect; + LOG(debug, "%s", uos.str().c_str()); + EXPECT_TRUE(uintcounter == uintcorrect); +} + + +template<typename T, typename U> +void +Test::testAddSub() +{ + FastOS_Runnable *threads2[numadders*2]; + T intcounter = 0; + U uintcounter = 0; + + FastOS_ThreadPool tpool2(65000, 2*numadders); + for (int i = 0; i < numadders; i++) { + threads2[i] = new Adder<T, U>(2+i, loopcnt, &intcounter, &uintcounter); + threads2[numadders+i] = new Subtracter<T, U>(2+i, loopcnt, + &intcounter, &uintcounter); + } + for (int i = 0; i < numadders*2; i++) { + tpool2.NewThread(threads2[i]); + } + tpool2.Close(); + + for (int i = 0; i < numadders*2; i++) { + delete threads2[i]; + } + std::ostringstream os; + os << "intcounter = " << intcounter << ", uintcounter = " << uintcounter; + LOG(debug, "%s", os.str().c_str()); + EXPECT_TRUE( intcounter == 0); + EXPECT_TRUE(uintcounter == 0); +} + + +template<typename T> +void +Test::testInc() +{ + Incrementer<T> *threads3[numadders]; + T uintcounter = 0; + FastOS_ThreadPool tpool3(65000, numadders); + for (int i = 0; i < numadders; i++) { + threads3[i] = new Incrementer<T>(loopcnt, &uintcounter); + tpool3.NewThread(threads3[i]); + } + tpool3.Close(); + std::vector<T> all; + for (int i = 0; i < numadders; i++) { + const std::vector<T> & cnts = threads3[i]->getCounts(); + typename std::vector<T>::const_iterator it = cnts.begin(); + while (it != cnts.end()) { + all.push_back(*it); + ++it; + } + } + for (int i = 0; i < numadders; i++) { + delete threads3[i]; + } + std::sort(all.begin(), all.end()); + for (unsigned int n = 0; n < all.size(); ++n) { + EXPECT_TRUE(all[n] == n); + if (all[n] != n) { + std::ostringstream os; + os << all[n]; + LOG(info, "all[%d] = %s", n, os.str().c_str()); + break; + } + } + TEST_FLUSH(); + EXPECT_TRUE(uintcounter == numadders * loopcnt); + TEST_FLUSH(); +} + +template<typename T> +void +Test::testDec() +{ + T uintcounter = numadders * loopcnt; + Decrementer<T> *threads4[numadders]; + FastOS_ThreadPool tpool4(65000, numadders); + for (int i = 0; i < numadders; i++) { + threads4[i] = new Decrementer<T>(loopcnt, &uintcounter); + tpool4.NewThread(threads4[i]); + } + tpool4.Close(); + std::vector<T> all; + for (int i = 0; i < numadders; i++) { + const std::vector<T> & cnts = threads4[i]->getCounts(); + typename std::vector<T>::const_iterator it = cnts.begin(); + while (it != cnts.end()) { + all.push_back(*it); + ++it; + } + } + for (int i = 0; i < numadders; i++) { + delete threads4[i]; + } + std::sort(all.begin(), all.end()); + for (size_t n = 0; n < all.size(); ++n) { + EXPECT_TRUE(all[n] == n+1); + if (all[n] != n+1) { + for (size_t i = n; i < std::min(n+20, all.size()); ++i) { + std::ostringstream os; + os << std::dec << "all[" << i << "] = " << std::hex << all[i]; + LOG(warning, "%s", os.str().c_str()); + } + break; + } + } + TEST_FLUSH(); + EXPECT_TRUE(uintcounter == 0); +} + + +TEST_APPHOOK(Test) |