summaryrefslogtreecommitdiffstats
path: root/vespalib/src/tests/atomic
diff options
context:
space:
mode:
authorJon Bratseth <bratseth@yahoo-inc.com>2016-06-15 23:09:44 +0200
committerJon Bratseth <bratseth@yahoo-inc.com>2016-06-15 23:09:44 +0200
commit72231250ed81e10d66bfe70701e64fa5fe50f712 (patch)
tree2728bba1131a6f6e5bdf95afec7d7ff9358dac50 /vespalib/src/tests/atomic
Publish
Diffstat (limited to 'vespalib/src/tests/atomic')
-rw-r--r--vespalib/src/tests/atomic/.gitignore6
-rw-r--r--vespalib/src/tests/atomic/CMakeLists.txt15
-rw-r--r--vespalib/src/tests/atomic/DESC1
-rw-r--r--vespalib/src/tests/atomic/FILES1
-rw-r--r--vespalib/src/tests/atomic/atomic_bench.cpp104
-rw-r--r--vespalib/src/tests/atomic/atomic_test.cpp338
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)