From 65eb5f8903eb7b42d244113bd14fabaef9a4230f Mon Sep 17 00:00:00 2001 From: souldbminersmwc Date: Thu, 11 Jun 2026 19:01:15 -0400 Subject: [PATCH] benchmark_toolbox: hanoi cpu test --- Source/Benchmark-Toolbox/Makefile | 4 +- .../source/cpustress/cpu_stress.c | 178 ++++++++++++++++++ .../source/cpustress/cpu_stress.h | 23 +++ Source/Benchmark-Toolbox/source/main.cpp | 123 ++++++++++++ 4 files changed, 326 insertions(+), 2 deletions(-) create mode 100644 Source/Benchmark-Toolbox/source/cpustress/cpu_stress.c create mode 100644 Source/Benchmark-Toolbox/source/cpustress/cpu_stress.h diff --git a/Source/Benchmark-Toolbox/Makefile b/Source/Benchmark-Toolbox/Makefile index 5ce8671a..975b9b43 100644 --- a/Source/Benchmark-Toolbox/Makefile +++ b/Source/Benchmark-Toolbox/Makefile @@ -12,10 +12,10 @@ include $(DEVKITPRO)/libnx/switch_rules #--------------------------------------------------------------------------------- TARGET := Benchmark-Toolbox BUILD := build -SOURCES := source source/furmark source/memtester +SOURCES := source source/furmark source/memtester source/cpustress DATA := data ICON := icon.jpg -INCLUDES := source source/furmark source/memtester ../hoc-clk/common/include +INCLUDES := source source/furmark source/memtester source/cpustress ../hoc-clk/common/include APP_TITLE := Benchmark Toolbox APP_AUTHOR := Horizon-OC diff --git a/Source/Benchmark-Toolbox/source/cpustress/cpu_stress.c b/Source/Benchmark-Toolbox/source/cpustress/cpu_stress.c new file mode 100644 index 00000000..ddd46be4 --- /dev/null +++ b/Source/Benchmark-Toolbox/source/cpustress/cpu_stress.c @@ -0,0 +1,178 @@ +#include "cpu_stress.h" + +#include +#include +#include + +#define CS_N 128 +#define CS_THREADS 3 +#define CS_PRIO 0x3B +#define CS_STACK 0x10000 + +typedef struct { + int core; + volatile uint64_t iters; + volatile uint64_t mismatches; +} cs_worker_t; + +static Thread s_t[CS_THREADS]; +static bool s_started[CS_THREADS]; +static cs_worker_t s_w[CS_THREADS]; + +static volatile bool s_stop = false; +static volatile bool s_running = false; +static int s_mode = 0; +static volatile int s_nthreads = 0; + +static volatile long double s_sink; + +static void mwc_seed(uint32_t *w, uint32_t *z, int core) { + uint32_t mix = (uint32_t)core * 0x9e3779b9u; + uint32_t ww = 0xdeadbeefu ^ mix; + uint32_t zz = 0x12345678u ^ ~mix; + *w = ww ? ww : 0xdeadbeefu; + *z = zz ? zz : 0x12345678u; +} + +static inline uint32_t mwc32(uint32_t *w, uint32_t *z) { + *z = 36969u * (*z & 65535u) + (*z >> 16); + *w = 18000u * (*w & 65535u) + (*w >> 16); + return (*z << 16) + *w; +} + +static void matrixprod(long double *a, long double *b, long double *r, uint32_t *w, uint32_t *z) { + const long double v = 1.0L / (long double)((uint32_t)~0u); + + for (int i = 0; i < CS_N; i++) { + for (int j = 0; j < CS_N; j++) { + a[i * CS_N + j] = (long double)mwc32(w, z) * v; + b[i * CS_N + j] = (long double)mwc32(w, z) * v; + r[i * CS_N + j] = 0.0L; + } + } + + for (int i = 0; i < CS_N && !s_stop; i++) { + for (int j = 0; j < CS_N; j++) { + for (int k = 0; k < CS_N; k++) + r[i * CS_N + j] += a[i * CS_N + k] * b[k * CS_N + j]; + } + } + + long double sum = 0.0L; + for (int i = 0; i < CS_N; i++) + for (int j = 0; j < CS_N; j++) + sum += r[i * CS_N + j]; + s_sink = sum; +} + +static uint32_t hanoi_rec(int n) { + if (n == 0) + return 1; + if (n == 1) + return 2; + return hanoi_rec(n - 1) + hanoi_rec(n - 1); +} + +static int hanoi_pass(void) { + uint32_t sum = 0; + for (int k = 19; k >= 0; k--) + sum += hanoi_rec(k); + return sum == 0xfffffu; +} + +static void cs_worker(void *arg) { + cs_worker_t *c = (cs_worker_t *)arg; + uint32_t w, z; + mwc_seed(&w, &z, c->core); + + long double *a = NULL, *b = NULL, *r = NULL; + if (s_mode == 0) { + size_t bytes = sizeof(long double) * CS_N * CS_N; + a = (long double *)malloc(bytes); + b = (long double *)malloc(bytes); + r = (long double *)malloc(bytes); + if (!a || !b || !r) { + free(a); + free(b); + free(r); + return; + } + } + + while (!s_stop) { + if (s_mode == 0) { + matrixprod(a, b, r, &w, &z); + } else { + if (!hanoi_pass()) + c->mismatches++; + } + c->iters++; + } + + free(a); + free(b); + free(r); +} + +void cpu_stress_start(int mode) { + if (s_running) + return; + s_mode = mode; + s_stop = false; + s_running = true; + s_nthreads = CS_THREADS; + appletSetAutoSleepDisabled(true); + + int started = 0; + for (int i = 0; i < CS_THREADS; i++) { + s_started[i] = false; + s_w[i].core = i; + s_w[i].iters = 0; + s_w[i].mismatches = 0; + if (R_FAILED(threadCreate(&s_t[i], cs_worker, &s_w[i], NULL, CS_STACK, CS_PRIO, i))) + continue; + if (R_SUCCEEDED(threadStart(&s_t[i]))) { + s_started[i] = true; + started++; + } else { + threadClose(&s_t[i]); + } + } + + if (started == 0) { + s_running = false; + appletSetAutoSleepDisabled(false); + } +} + +void cpu_stress_stop(void) { + s_stop = true; + for (int i = 0; i < CS_THREADS; i++) { + if (!s_started[i]) + continue; + threadWaitForExit(&s_t[i]); + threadClose(&s_t[i]); + s_started[i] = false; + } + s_running = false; + appletSetAutoSleepDisabled(false); +} + +int cpu_stress_running(void) { + return s_running ? 1 : 0; +} + +void cpu_stress_get(cpu_stress_status_t *out) { + if (!out) + return; + uint64_t it = 0, mm = 0; + int n = s_nthreads; + for (int i = 0; i < n; i++) { + it += s_w[i].iters; + mm += s_w[i].mismatches; + } + out->iters = it; + out->mismatches = mm; + out->threads = s_nthreads; + out->running = s_running ? 1 : 0; +} diff --git a/Source/Benchmark-Toolbox/source/cpustress/cpu_stress.h b/Source/Benchmark-Toolbox/source/cpustress/cpu_stress.h new file mode 100644 index 00000000..297c8365 --- /dev/null +++ b/Source/Benchmark-Toolbox/source/cpustress/cpu_stress.h @@ -0,0 +1,23 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + uint64_t iters; + uint64_t mismatches; + int threads; + int running; +} cpu_stress_status_t; + +void cpu_stress_start(int mode); +void cpu_stress_stop(void); +int cpu_stress_running(void); +void cpu_stress_get(cpu_stress_status_t *out); + +#ifdef __cplusplus +} +#endif diff --git a/Source/Benchmark-Toolbox/source/main.cpp b/Source/Benchmark-Toolbox/source/main.cpp index 0f0f432c..6471076d 100644 --- a/Source/Benchmark-Toolbox/source/main.cpp +++ b/Source/Benchmark-Toolbox/source/main.cpp @@ -13,6 +13,7 @@ extern "C" { #include #include "bench.h" +#include "cpu_stress.h" #include "gpu_bw.h" #include "gpu_stress.h" #include "hoc_clk.h" @@ -658,6 +659,125 @@ class MemtesterTab : public brls::Box { brls::Rectangle *barFill, *barTrack; }; +class CpuStressTab : public brls::Box { + public: + CpuStressTab() { + this->setAxis(brls::Axis::COLUMN); + this->setGrow(1.0f); + this->setPadding(40.0f, 60.0f, 40.0f, 60.0f); + + auto *modeRow = new brls::Box(brls::Axis::ROW); + modeRow->setMarginBottom(10.0f); + auto *ml = new brls::Label(); + ml->setText("Mode"); + ml->setGrow(1.0f); + modeRow->addView(ml); + modeVal = new brls::Label(); + modeVal->setText(modeName(mode)); + modeRow->addView(modeVal); + this->addView(modeRow); + + auto *hint = new brls::Label(); + hint->setText("Use L / R to select the CPU stress mode. Press A to run."); + hint->setFontSize(15.0f); + hint->setTextColor(nvgRGB(150, 150, 150)); + hint->setMarginBottom(14.0f); + this->addView(hint); + + toggle = new brls::Button(); + toggle->setText("Start"); + toggle->registerClickAction([this](brls::View *) { + onToggle(); + return true; + }); + this->addView(toggle); + + this->registerAction("Prev mode", brls::ControllerButton::BUTTON_LB, [this](brls::View *) { + cycle(-1); + return true; + }); + this->registerAction("Next mode", brls::ControllerButton::BUTTON_RB, [this](brls::View *) { + cycle(1); + return true; + }); + + statusL = new brls::Label(); + statusL->setText("Stopped"); + statusL->setMarginTop(10.0f); + statusL->setMarginBottom(8.0f); + this->addView(statusL); + + auto *h = new brls::Header(); + h->setTitle("Live"); + this->addView(h); + rowA = makeRow(this, "Iterations"); + rowB = makeRow(this, "Mismatches"); + rowC = makeRow(this, "Threads"); + } + + ~CpuStressTab() override { + if (cpu_stress_running()) + cpu_stress_stop(); + } + + void willDisappear(bool resetState = false) override { + if (cpu_stress_running()) + cpu_stress_stop(); + brls::Box::willDisappear(resetState); + } + + void frame(brls::FrameContext *ctx) override { + cpu_stress_status_t s; + cpu_stress_get(&s); + if (cpu_stress_running() || lastRunning) { + rowA->setText(fstru("%llu", (unsigned long long)s.iters)); + rowB->setText(fstru("%llu", (unsigned long long)s.mismatches)); + rowC->setText(fstru("%llu", (unsigned long long)s.threads)); + if (cpu_stress_running()) + statusL->setText(s.mismatches ? "Errors found! Running..." : "Running..."); + } + syncToggle(cpu_stress_running() != 0); + brls::Box::frame(ctx); + } + + private: + static const char *modeName(int m) { + switch (m) { + case 0: return "Matrixprod"; + case 1: return "Hanoi (verify)"; + } + return ""; + } + void cycle(int dir) { + if (cpu_stress_running()) + return; + mode = (mode + dir + 2) % 2; + modeVal->setText(modeName(mode)); + } + void onToggle() { + if (cpu_stress_running()) + cpu_stress_stop(); + else { + rowA->setText("-"); + rowB->setText("-"); + rowC->setText("-"); + cpu_stress_start(mode); + } + } + void syncToggle(bool running) { + if (running == lastRunning) + return; + lastRunning = running; + toggle->setText(running ? "Stop" : "Start"); + if (!running) + statusL->setText("Stopped"); + } + int mode = 0; + bool lastRunning = false; + brls::Label *modeVal, *statusL, *rowA, *rowB, *rowC; + brls::Button *toggle; +}; + class CreditsTab : public brls::Box { public: CreditsTab() { @@ -773,6 +893,7 @@ class MainActivity : public brls::Activity { tab->addTab("Membench", [] { return new BenchTab(); }); tab->addTab("GPU Test", [] { return new StressTab(); }); tab->addTab("Memtester", [] { return new MemtesterTab(); }); + tab->addTab("CPU Stress", [] { return new CpuStressTab(); }); tab->addSeparator(); tab->addTab("Furmark", [] { return new FurmarkTab(0, "FurMark for Switch (48 step)"); }); tab->addTab("Furmark RAM", [] { return new FurmarkTab(1, "FurMark with extra ram stress"); }); @@ -813,6 +934,8 @@ int main(int argc, char *argv[]) { mt_gpu_stop(); if (mt_cpu_running()) mt_cpu_stop(); + if (cpu_stress_running()) + cpu_stress_stop(); _exit(EXIT_SUCCESS); }