aboutsummaryrefslogtreecommitdiffstats
path: root/vespalib
diff options
context:
space:
mode:
authorHåvard Pettersen <havardpe@oath.com>2022-01-06 15:44:33 +0000
committerHåvard Pettersen <havardpe@oath.com>2022-01-07 14:16:28 +0000
commitad3649d3dcb3bc53d878da8e7447cbdf74de4592 (patch)
tree9115327fd7f48122777420c1782d5056a44ee80e /vespalib
parentcdb6a8521b56ff30ff8abda7b5342986df977f2e (diff)
low-level code to sample CPU usage per thread
Diffstat (limited to 'vespalib')
-rw-r--r--vespalib/CMakeLists.txt1
-rw-r--r--vespalib/src/tests/cpu_usage/CMakeLists.txt8
-rw-r--r--vespalib/src/tests/cpu_usage/cpu_usage_test.cpp94
-rw-r--r--vespalib/src/vespa/vespalib/util/CMakeLists.txt1
-rw-r--r--vespalib/src/vespa/vespalib/util/cpu_usage.cpp26
-rw-r--r--vespalib/src/vespa/vespalib/util/cpu_usage.h27
-rw-r--r--vespalib/src/vespa/vespalib/util/time.h4
7 files changed, 161 insertions, 0 deletions
diff --git a/vespalib/CMakeLists.txt b/vespalib/CMakeLists.txt
index 9f98d23d6a9..eb7e1f7d4c0 100644
--- a/vespalib/CMakeLists.txt
+++ b/vespalib/CMakeLists.txt
@@ -31,6 +31,7 @@ vespa_define_module(
src/tests/component
src/tests/compress
src/tests/compression
+ src/tests/cpu_usage
src/tests/crypto
src/tests/data/databuffer
src/tests/data/input_reader
diff --git a/vespalib/src/tests/cpu_usage/CMakeLists.txt b/vespalib/src/tests/cpu_usage/CMakeLists.txt
new file mode 100644
index 00000000000..e3e2def6056
--- /dev/null
+++ b/vespalib/src/tests/cpu_usage/CMakeLists.txt
@@ -0,0 +1,8 @@
+# Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(vespalib_cpu_usage_test_app TEST
+ SOURCES
+ cpu_usage_test.cpp
+ DEPENDS
+ vespalib
+)
+vespa_add_test(NAME vespalib_cpu_usage_test_app COMMAND vespalib_cpu_usage_test_app)
diff --git a/vespalib/src/tests/cpu_usage/cpu_usage_test.cpp b/vespalib/src/tests/cpu_usage/cpu_usage_test.cpp
new file mode 100644
index 00000000000..c8835d82cd8
--- /dev/null
+++ b/vespalib/src/tests/cpu_usage/cpu_usage_test.cpp
@@ -0,0 +1,94 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include <vespa/vespalib/util/cpu_usage.h>
+#include <vespa/vespalib/util/benchmark_timer.h>
+#include <vespa/vespalib/testkit/test_kit.h>
+
+#include <thread>
+
+using namespace vespalib;
+
+bool verbose = false;
+size_t loop_cnt = 10;
+double budget = 0.25;
+
+using Sampler = vespalib::cpu_usage::ThreadSampler;
+
+//-----------------------------------------------------------------------------
+
+void be_busy(duration d) {
+ if (d > 0ms) {
+ volatile int tmp = 123;
+ auto t0 = steady_clock::now();
+ while ((steady_clock::now() - t0) < d) {
+ for (int i = 0; i < 1000; ++i) {
+ tmp = (tmp + i);
+ tmp = (tmp - i);
+ }
+ }
+ }
+}
+
+std::vector<duration> sample(const std::vector<Sampler*> &list) {
+ std::vector<duration> result;
+ result.reserve(list.size());
+ for (Sampler *sampler: list) {
+ result.push_back(sampler->sample());
+ }
+ return result;
+}
+
+//-----------------------------------------------------------------------------
+
+TEST_MT_F("require that external thread-based CPU usage sampling works", 5, std::vector<Sampler*>(4, nullptr)) {
+ if (thread_id == 0) {
+ TEST_BARRIER(); // #1
+ auto t0 = steady_clock::now();
+ std::vector<duration> pre_usage = sample(f1);
+ TEST_BARRIER(); // #2
+ TEST_BARRIER(); // #3
+ auto t1 = steady_clock::now();
+ std::vector<duration> post_usage = sample(f1);
+ TEST_BARRIER(); // #4
+ double wall = to_s(t1 - t0);
+ std::vector<double> load(4, 0.0);
+ for (size_t i = 0; i < 4; ++i) {
+ load[i] = to_s(post_usage[i] - pre_usage[i]) / wall;
+ }
+ EXPECT_GREATER(load[3], load[0]);
+ fprintf(stderr, "loads: { %.2f, %.2f, %.2f, %.2f }\n", load[0], load[1], load[2], load[3]);
+ } else {
+ int idx = (thread_id - 1);
+ Sampler sampler;
+ f1[idx] = &sampler;
+ TEST_BARRIER(); // #1
+ TEST_BARRIER(); // #2
+ for (size_t i = 0; i < loop_cnt; ++i) {
+ be_busy(std::chrono::milliseconds(idx));
+ }
+ TEST_BARRIER(); // #3
+ TEST_BARRIER(); // #4
+ }
+}
+
+//-----------------------------------------------------------------------------
+
+TEST("measure thread CPU clock overhead") {
+ Sampler sampler;
+ duration d;
+ double min_time_us = BenchmarkTimer::benchmark([&d, &sampler]() noexcept { d = sampler.sample(); }, budget) * 1000000.0;
+ fprintf(stderr, "approx overhead per sample (thread CPU clock): %f us\n", min_time_us);
+}
+
+//-----------------------------------------------------------------------------
+
+int main(int argc, char **argv) {
+ TEST_MASTER.init(__FILE__);
+ if ((argc == 2) && (argv[1] == std::string("verbose"))) {
+ verbose = true;
+ loop_cnt = 1000;
+ budget = 5.0;
+ }
+ TEST_RUN_ALL();
+ return (TEST_MASTER.fini() ? 0 : 1);
+}
diff --git a/vespalib/src/vespa/vespalib/util/CMakeLists.txt b/vespalib/src/vespa/vespalib/util/CMakeLists.txt
index 58f6a93babc..32f679d22d7 100644
--- a/vespalib/src/vespa/vespalib/util/CMakeLists.txt
+++ b/vespalib/src/vespa/vespalib/util/CMakeLists.txt
@@ -18,6 +18,7 @@ vespa_add_library(vespalib_vespalib_util OBJECT
classname.cpp
compress.cpp
compressor.cpp
+ cpu_usage.cpp
destructor_callbacks.cpp
dual_merge_director.cpp
error.cpp
diff --git a/vespalib/src/vespa/vespalib/util/cpu_usage.cpp b/vespalib/src/vespa/vespalib/util/cpu_usage.cpp
new file mode 100644
index 00000000000..b5bd3ed76ac
--- /dev/null
+++ b/vespalib/src/vespa/vespalib/util/cpu_usage.cpp
@@ -0,0 +1,26 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include "cpu_usage.h"
+#include "require.h"
+
+namespace vespalib {
+
+namespace cpu_usage {
+
+ThreadSampler::ThreadSampler()
+ : _my_clock()
+{
+ REQUIRE_EQ(pthread_getcpuclockid(pthread_self(), &_my_clock), 0);
+}
+
+duration
+ThreadSampler::sample() const
+{
+ timespec ts;
+ REQUIRE_EQ(clock_gettime(_my_clock, &ts), 0);
+ return from_timespec(ts);
+}
+
+} // cpu_usage
+
+} // namespace
diff --git a/vespalib/src/vespa/vespalib/util/cpu_usage.h b/vespalib/src/vespa/vespalib/util/cpu_usage.h
new file mode 100644
index 00000000000..452e96ba0ff
--- /dev/null
+++ b/vespalib/src/vespa/vespalib/util/cpu_usage.h
@@ -0,0 +1,27 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include <pthread.h>
+#include <vespa/vespalib/util/time.h>
+
+namespace vespalib {
+
+namespace cpu_usage {
+
+/**
+ * Samples the total CPU usage of the thread that created it. Note
+ * that this must not be used after thread termination. Enables
+ * sampling the CPU usage of a thread from outside the thread.
+ *
+ * uses: pthread_self, pthread_getcpuclockid, clock_gettime
+ **/
+class ThreadSampler {
+private:
+ clockid_t _my_clock;
+public:
+ ThreadSampler();
+ duration sample() const;
+};
+
+} // cpu_usage
+
+} // namespace
diff --git a/vespalib/src/vespa/vespalib/util/time.h b/vespalib/src/vespa/vespalib/util/time.h
index f1f529b64b6..f19d71afb32 100644
--- a/vespalib/src/vespa/vespalib/util/time.h
+++ b/vespalib/src/vespa/vespalib/util/time.h
@@ -67,6 +67,10 @@ constexpr duration from_timeval(const timeval & tv) {
return duration(tv.tv_sec*1000000000L + tv.tv_usec*1000L);
}
+constexpr duration from_timespec(const timespec & ts) {
+ return duration(ts.tv_sec*1000000000L + ts.tv_nsec);
+}
+
vespalib::string to_string(system_time time);
/**