From d186e4361e87e406d5f5376c8f0df19aa47bfbf6 Mon Sep 17 00:00:00 2001 From: Alexander Boettcher Date: Sat, 12 Sep 2020 10:14:12 +0200 Subject: [PATCH] Measure TSC variance of CPUs in test-smp Issue #3965 --- repos/base/src/test/smp/main.cc | 188 +++++++++++++++++++++++++++++++- 1 file changed, 186 insertions(+), 2 deletions(-) diff --git a/repos/base/src/test/smp/main.cc b/repos/base/src/test/smp/main.cc index 6eb9b2d540..53ada8a743 100644 --- a/repos/base/src/test/smp/main.cc +++ b/repos/base/src/test/smp/main.cc @@ -6,7 +6,7 @@ */ /* - * Copyright (C) 2013-2017 Genode Labs GmbH + * Copyright (C) 2013-2020 Genode Labs GmbH * * This file is part of the Genode OS framework, which is distributed * under the terms of the GNU Affero General Public License version 3. @@ -19,6 +19,7 @@ #include #include #include +#include namespace Genode { @@ -202,7 +203,7 @@ namespace Affinity_test { volatile uint64_t cnt = 0; unsigned round = 0; - char const text_cpu[] = "Affinity: CPU: "; + char const text_cpu[] = "Affinity: CPU: "; char const text_round[] = "Affinity: Round %2u: "; char * output_buffer = new (heap) char [sizeof(text_cpu) + 3 * cpus.total()]; @@ -323,6 +324,188 @@ namespace Tlb_shootdown_test { log("TLB: --- test finished ---"); } } + +namespace Tsc_test { + + struct Tsc_thread : Genode::Thread + { + enum { STACK_SIZE = 4 * 4096 }; + + Genode::Affinity::Location const location; + Genode::Blockade barrier { }; + Genode::uint64_t volatile cnt { 0 }; + Genode::uint64_t volatile tsc_value { 0 }; + Genode::uint64_t last_cnt { 0 }; + Genode::uint64_t last_tsc { 0 }; + Genode::uint64_t diff { 0 }; + bool volatile loop { true }; + bool volatile spin { true }; + + void entry() override + { + last_tsc = Genode::Trace::timestamp(); + + Genode::log(this, " ", Genode::Hex(last_tsc)); + barrier.wakeup(); + + while (loop) { + while (spin && loop) cnt++; + + measure(); + spin = true; + } + } + + void measure() { tsc_value = Genode::Trace::timestamp(); } + + Tsc_thread(Genode::Env &env, Location location) + : Genode::Thread(env, Name("tsc_thread"), STACK_SIZE, location, + Weight(), env.cpu()), location(location) + { } + }; + + template + Genode::String _align_right(Genode::String const &s) + { + Genode::String result = s; + + for (Genode::uint64_t i = s.length(); i < T; i++) + result = Genode::String(" ", result); + + return result; + } + + template + Genode::String _align_right(Genode::uint64_t const value) + { + Genode::String result("",Genode::Hex(value)); + + Genode::uint64_t pow = 16; + + for (Genode::uint64_t i = 3; i < (T - 1); i++, pow *= 16) { + if (value < pow) { + result = Genode::String(" ", result); + } + } + + if (value > pow) { + result = Genode::String("?"); + for (Genode::uint64_t i = 1; i < (T - 1); i++) + result = Genode::String(" ", result); + } + + return result; + } + + void execute(Genode::Env &env, Genode::Heap &heap, + Genode::Affinity::Space &cpus) + { + using namespace Genode; + + log("TSC: --- test started ---"); + + /* get some memory for the thread objects */ + Tsc_thread ** threads = new (heap) Tsc_thread*[cpus.total()]; + + /* construct the thread objects */ + for (unsigned i = 0; i < cpus.total(); i++) { + threads[i] = new (heap) Tsc_thread(env, cpus.location_of_index(i)); + /* skip first thread, current thread will do the measurement */ + if (i) threads[i]->start(); + } + + /* wait until all threads are up and running */ + for (unsigned i = 1; i < cpus.total(); i++) threads[i]->barrier.block(); + + { + String<128> legend(" "); + for (unsigned i = 0; i < cpus.total(); i++) { + legend = String<128>(legend, _align_right<15>(String<128>("cpu (", threads[i]->affinity(),")"))); + } + legend = String<128>(legend, _align_right<13>("diff-min")); + legend = String<128>(legend, _align_right<13>("diff-max")); + log(legend); + } + + log("round / tsc per cpu"); + + /* we handle the first cpu */ + threads[0]->measure(); + threads[0]->last_tsc = threads[0]->tsc_value; + + /* make some rounds */ + for (unsigned round = 0; round < 20; round++) { + + /* stop spinning */ + for (unsigned i = 1; i < cpus.total(); i++) + threads[i]->spin = false; + + /* wait for valid results */ + for (unsigned i = 1; i < cpus.total(); i++) + while (!threads[i]->spin) { }; + + /* do measure for cpu 0 */ + threads[0]->measure(); + + /* calculate results */ + String<128> show; + String<128> show_diff; + + for (unsigned i = 0; i < cpus.total(); i++) { + uint64_t diff = threads[i]->tsc_value - threads[i]->last_tsc; + if (round) { + bool plus = diff > threads[i]->diff; + show_diff = String<128>(show_diff, " ", plus ? "+" : "-", + _align_right<13>(plus ? + (diff - threads[i]->diff) : + (threads[i]->diff - diff))); + } + + threads[i]->diff = diff; + threads[i]->last_cnt = threads[i]->cnt; + threads[i]->last_tsc = threads[i]->tsc_value; + + show = String<128>(show, " ", _align_right<14>(threads[i]->diff)); + + } + uint64_t min_diff = ~0ULL; + uint64_t max_diff = 0; + + for (unsigned i = 0; i < cpus.total(); i++) { + for (unsigned j = 0; j < cpus.total(); j++) { + if (i == j) continue; + + uint64_t diff = (threads[i]->diff > threads[j]->diff) ? + (threads[i]->diff - threads[j]->diff) : + (threads[j]->diff - threads[i]->diff); + + if (diff < min_diff) min_diff = diff; + if (diff > max_diff) max_diff = diff; + } + } + + /* show result */ + if (round) + log(" ", show_diff); /* diff to prev column */ + log(round, round < 10 ? " " : " ", show, + " ", _align_right<12>(min_diff), + " ", _align_right<12>(max_diff)); + } + + /* break loop and stop spinning */ + for (unsigned i = 1; i < cpus.total(); i++) threads[i]->loop = false; + + /* join finished worker threads */ + for (unsigned i = 1; i < cpus.total(); i++) threads[i]->join(); + + /* cleanup */ + for (unsigned i = 0; i < cpus.total(); i++) destroy(heap, threads[i]); + destroy(heap, threads); + + log("TSC: --- test finished ---"); + } +} + void Component::construct(Genode::Env & env) { using namespace Genode; @@ -335,6 +518,7 @@ void Component::construct(Genode::Env & env) Heap heap(env.ram(), env.rm()); + Tsc_test::execute(env, heap, cpus); Mp_server_test::execute(env, heap, cpus); Affinity_test::execute(env, heap, cpus); Tlb_shootdown_test::execute(env, heap, cpus);