From 73fccf339fd165c4cdd3b0f1937fc1236c2697c3 Mon Sep 17 00:00:00 2001 From: souldbminersmwc Date: Thu, 11 Jun 2026 18:56:01 -0400 Subject: [PATCH] benchmark_toolbox: memtester --- Source/Benchmark-Toolbox/Makefile | 6 +- Source/Benchmark-Toolbox/source/main.cpp | 233 ++++++++++ .../source/memtester/mt_cpu.c | 331 +++++++++++++++ .../source/memtester/mt_cpu.h | 28 ++ .../source/memtester/mt_gpu.cpp | 336 +++++++++++++++ .../source/memtester/mt_gpu.h | 25 ++ .../source/memtester/mt_tests.c | 398 ++++++++++++++++++ .../source/memtester/mt_tests.h | 45 ++ 8 files changed, 1399 insertions(+), 3 deletions(-) create mode 100644 Source/Benchmark-Toolbox/source/memtester/mt_cpu.c create mode 100644 Source/Benchmark-Toolbox/source/memtester/mt_cpu.h create mode 100644 Source/Benchmark-Toolbox/source/memtester/mt_gpu.cpp create mode 100644 Source/Benchmark-Toolbox/source/memtester/mt_gpu.h create mode 100644 Source/Benchmark-Toolbox/source/memtester/mt_tests.c create mode 100644 Source/Benchmark-Toolbox/source/memtester/mt_tests.h diff --git a/Source/Benchmark-Toolbox/Makefile b/Source/Benchmark-Toolbox/Makefile index 6780765b..ed662512 100644 --- a/Source/Benchmark-Toolbox/Makefile +++ b/Source/Benchmark-Toolbox/Makefile @@ -12,14 +12,14 @@ include $(DEVKITPRO)/libnx/switch_rules #--------------------------------------------------------------------------------- TARGET := Benchmark-Toolbox BUILD := build -SOURCES := source source/furmark +SOURCES := source source/furmark source/memtester DATA := data ICON := icon.jpg -INCLUDES := source source/furmark ../hoc-clk/common/include +INCLUDES := source source/furmark source/memtester ../hoc-clk/common/include APP_TITLE := Benchmark Toolbox APP_AUTHOR := Horizon-OC, KazushiMe, Souldbminer -APP_VERSION := 1.0.0 +APP_VERSION := 2.0.0 ROMFS := resources BOREALIS_PATH := lib/borealis diff --git a/Source/Benchmark-Toolbox/source/main.cpp b/Source/Benchmark-Toolbox/source/main.cpp index 001a8a1e..b9b6d948 100644 --- a/Source/Benchmark-Toolbox/source/main.cpp +++ b/Source/Benchmark-Toolbox/source/main.cpp @@ -16,6 +16,8 @@ extern "C" { #include "gpu_bw.h" #include "gpu_stress.h" #include "hoc_clk.h" +#include "mt_cpu.h" +#include "mt_gpu.h" #include "run_furmark.h" } @@ -476,6 +478,224 @@ class FurmarkTab : public brls::Box { brls::Label *statusL; }; +class MemtesterTab : public brls::Box { + public: + MemtesterTab() { + 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 memtester 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); + + bar = new brls::Box(brls::Axis::ROW); + bar->setHeight(18.0f); + bar->setWidthPercentage(100.0f); + bar->setMarginBottom(14.0f); + barFill = new brls::Rectangle(); + barFill->setColor(nvgRGB(0, 193, 210)); + barFill->setWidthPercentage(0.0f); + barTrack = new brls::Rectangle(); + barTrack->setColor(nvgRGB(48, 48, 54)); + barTrack->setGrow(1.0f); + bar->addView(barFill); + bar->addView(barTrack); + this->addView(bar); + + auto *h = new brls::Header(); + h->setTitle("Live"); + this->addView(h); + rowA = makeRow(this, "Loops"); + rowB = makeRow(this, "Mismatches"); + rowC = makeRow(this, "Detail"); + } + + void setProgress(float f) { + barFill->setWidthPercentage(f * 100.0f); + } + + ~MemtesterTab() override { + stopAny(); + } + + void willDisappear(bool resetState = false) override { + stopAny(); + brls::Box::willDisappear(resetState); + } + + void frame(brls::FrameContext *ctx) override { + if (isGpu()) { + mt_gpu_status_t s; + mt_gpu_get(&s); + if (s.running || lastRunning) { + rowA->setText(fstru("%llu", (unsigned long long)s.loop)); + rowB->setText(fstru("%llu", (unsigned long long)s.mismatches)); + rowC->setText(fstru("%llu MB tested", (unsigned long long)s.size_mb)); + statusL->setText(s.error ? std::string("Error: ") + s.status : std::string(s.status)); + } + setProgress(0.0f); + syncToggle(s.running != 0); + } else { + mt_cpu_status_t s; + mt_cpu_get(&s); + if (mt_cpu_running() || lastRunning) { + rowA->setText(fstru("%llu", (unsigned long long)s.loop)); + rowB->setText(fstru("%llu", (unsigned long long)s.mismatches)); + char d[96]; + if (mode == 1) + std::snprintf(d, sizeof d, "%llu MB, burn-in x%llu", + (unsigned long long)s.total_mb, (unsigned long long)s.burnin_iters); + else + std::snprintf(d, sizeof d, "%llu MB, %d threads", + (unsigned long long)s.total_mb, s.threads); + rowC->setText(d); + setProgress(mt_cpu_running() ? s.progress : 0.0f); + if (mt_cpu_running()) + statusL->setText(std::string(s.mismatches ? "Errors found! Running - " : "Running - ") + + (s.test ? s.test : "")); + } + syncToggle(mt_cpu_running() != 0); + } + brls::Box::frame(ctx); + } + + private: + static const char *modeName(int m) { + switch (m) { + case 0: return "CPU - Memtester"; + case 1: return "CPU - Memtester + BW Burn-in"; + case 2: return "GPU - Memtester (Fast)"; + case 3: return "GPU - Memtester (Full)"; + } + return ""; + } + bool isGpu() const { + return mode >= 2; + } + bool anyRunning() { + return mt_cpu_running() || mt_gpu_running(); + } + void cycle(int dir) { + if (anyRunning()) + return; + mode = (mode + dir + 4) % 4; + modeVal->setText(modeName(mode)); + } + void onToggle() { + if (anyRunning()) + stopAny(); + else + startAny(); + } + void startAny() { + rowA->setText("-"); + rowB->setText("-"); + rowC->setText("-"); + switch (mode) { + case 0: mt_cpu_start(0); break; + case 1: mt_cpu_start(1); break; + case 2: mt_gpu_start(0); break; + case 3: mt_gpu_start(1); break; + } + } + void stopAny() { + if (mt_cpu_running()) + mt_cpu_stop(); + if (mt_gpu_running()) + mt_gpu_stop(); + } + 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; + brls::Box *bar; + brls::Rectangle *barFill, *barTrack; +}; + +class CreditsTab : public brls::Box { + public: + CreditsTab() { + this->setAxis(brls::Axis::COLUMN); + this->setGrow(1.0f); + this->setPadding(40.0f, 60.0f, 40.0f, 60.0f); + + auto *title = new brls::Label(); + title->setText("Benchmark Toolbox"); + title->setFontSize(26.0f); + this->addView(title); + + auto *by = new brls::Label(); + by->setText("by Souldbminer"); + by->setFontSize(16.0f); + by->setTextColor(nvgRGB(150, 150, 150)); + by->setMarginBottom(12.0f); + this->addView(by); + + auto *h = new brls::Header(); + h->setTitle("Credits"); + this->addView(h); + + makeRow(this, "Memtester")->setText("Simon Kirby, Charles Cazabon, KazushiMe & CTCaer"); + makeRow(this, "FurMark")->setText("AnxietyTimmy"); + makeRow(this, "GPU Test")->setText("NaGaa95"); + makeRow(this, "Membench")->setText("Siarhei Siamashka, KazushiMe & Lineon"); + makeRow(this, "Stress-ng")->setText("ColinIanKing & Lineon"); + + auto *note = new brls::Label(); + note->setText("Thanks to all the original authors."); + note->setFontSize(15.0f); + note->setTextColor(nvgRGB(150, 150, 150)); + note->setMarginTop(18.0f); + this->addView(note); + } +}; + class AppFrame : public brls::TabFrame { public: AppFrame() { @@ -552,6 +772,7 @@ class MainActivity : public brls::Activity { tab->addSeparator(); tab->addTab("Membench", [] { return new BenchTab(); }); tab->addTab("GPU Test", [] { return new StressTab(); }); + tab->addTab("Memtester", [] { return new MemtesterTab(); }); 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"); }); @@ -559,6 +780,8 @@ class MainActivity : public brls::Activity { tab->addTab("Black Hole", [] { return new FurmarkTab(3, "CPU+GPU black-hole."); }); tab->addTab("CPU Ray Trace", [] { return new FurmarkTab(4, "CPU Path Tracer"); }); tab->addTab("CPU RAM", [] { return new FurmarkTab(5, "CPU RT with extra RAM stress"); }); + tab->addSeparator(); + tab->addTab("Credits", [] { return new CreditsTab(); }); return tab; } }; @@ -581,5 +804,15 @@ int main(int argc, char *argv[]) { while (brls::Application::mainLoop()) ; + // Global quit (+) can return from the main loop without destroying the + // activity, leaving background workers (and their threads / large RAM + // allocations) live across _exit. Tear them down first. + if (run_furmark_running()) + run_furmark_stop(); + if (mt_gpu_running()) + mt_gpu_stop(); + if (mt_cpu_running()) + mt_cpu_stop(); + _exit(EXIT_SUCCESS); } diff --git a/Source/Benchmark-Toolbox/source/memtester/mt_cpu.c b/Source/Benchmark-Toolbox/source/memtester/mt_cpu.c new file mode 100644 index 00000000..7f5416c9 --- /dev/null +++ b/Source/Benchmark-Toolbox/source/memtester/mt_cpu.c @@ -0,0 +1,331 @@ +#include "mt_cpu.h" + +#include +#include +#include +#include +#include + +#include "mt_tests.h" + +#define MT_MAX_THREADS 4 +#define MT_PAGESIZE 4096 +#define MT_MEMSHIFT 20 +#define MT_WANTRAW 2048 +#define MT_WORKER_PRIO 0x3B + +typedef unsigned long ul; +typedef unsigned long volatile ulv; +typedef unsigned long long ull; + +typedef struct { + int idx; + int burnin; + int burn_kernel; + void volatile *aligned; + size_t words; + volatile uint64_t loop; + volatile uint64_t iters; + volatile uint64_t mismatches; +} mt_worker_t; + +static Thread s_coord; +static Thread s_tobj[MT_MAX_THREADS]; +static void volatile *s_buf[MT_MAX_THREADS]; +static mt_worker_t s_workers[MT_MAX_THREADS]; + +static volatile bool s_stop = false; +static volatile bool s_running = false; +static volatile bool s_done = false; +static volatile uint64_t s_total_mb = 0; +static volatile int s_nthreads = 0; +static int s_mode = 0; +static bool s_coord_open = false; +static const char *volatile s_cur_test = "Idle"; +static volatile int s_step = 0; + +#define MT_TESTS_COUNT 15 + +static int run_sequence(mt_worker_t *w) { + size_t words = w->words; + size_t half = words / 2; + ulv *bufa = (ulv *)w->aligned; + ulv *bufb = (ulv *)((size_t)w->aligned + (words / 2) * sizeof(ul)); + + if (w->idx == 0) { + s_cur_test = "Stuck Address"; + s_step = 0; + } + if (mt_test_stuck_address((unsigned long volatile *)w->aligned, words)) + w->mismatches++; + + for (int t = 0; mt_tests[t].name; t++) { + if (s_stop) + return 0; + if (w->idx == 0) { + s_cur_test = mt_tests[t].name; + s_step = t + 1; + } + if (mt_tests[t].fp(bufa, bufb, half)) + w->mismatches++; + } + return 0; +} + +static void burn_kernel_run(int kernel, void *a, void *b, size_t bytes, int pattern) { + switch (kernel) { + case 0: + memcpy(a, b, bytes); + break; + case 1: + memset(a, 0x00, bytes); + break; + default: + memset(b, pattern, bytes); + break; + } +} + +static int run_burnin(mt_worker_t *w) { + size_t bytes = (w->words / 2) * sizeof(ul); + void *a = (void *)w->aligned; + void *b = (void *)((size_t)w->aligned + bytes); + int pattern = (w->idx & 1) ? 0x55 : 0xaa; + int kernel = w->burn_kernel; + + int reps = 1; + while (!s_stop) { + u64 t0 = armGetSystemTick(); + for (int i = 0; i < reps; i++) { + if (s_stop) + return 0; + burn_kernel_run(kernel, a, b, bytes, pattern); + } + double sec = (double)armTicksToNs(armGetSystemTick() - t0) / 1000000000.0; + if (sec >= 0.25 || reps > 0xfffff) + break; + + int grown = (reps + 1 < reps * 2) ? reps * 2 : reps + 1; + int next = reps * 2; + if (sec > 0.0) { + next = (int)((0.25 / sec) * (double)reps); + if (next < grown) + next = grown; + } + reps = next; + } + + w->burn_kernel = (kernel + 1) % 3; + return 0; +} + +static void worker_main(void *arg) { + mt_worker_t *w = (mt_worker_t *)arg; + while (!s_stop) { + if (w->burnin) + run_burnin(w); + else + run_sequence(w); + w->iters++; + if (!w->burnin) + w->loop++; + } +} + +static void coordinator(void *arg) { + (void)arg; + + ull totalmem = 0; + int testThreads = 3; + void volatile *probe[MT_MAX_THREADS]; + size_t want[MT_MAX_THREADS]; + int numMallocs = 0; + size_t wantbytes_orig = ((size_t)MT_WANTRAW << MT_MEMSHIFT); + ptrdiff_t pagemask = (ptrdiff_t)~((size_t)MT_PAGESIZE - 1); + + for (int div = 0; div <= 3; div++) { + probe[div] = NULL; + want[div] = wantbytes_orig; + while (!probe[div] && want[div]) { + probe[div] = (void volatile *)malloc(want[div]); + if (!probe[div]) + want[div] -= MT_PAGESIZE; + } + totalmem += want[div]; + if ((want[div] >> MT_MEMSHIFT) < (MT_WANTRAW - 1)) { + numMallocs = div + 1; + break; + } + if (div == 3) + numMallocs = 4; + } + for (int div = 0; div < numMallocs; div++) + free((void *)probe[div]); + + bool devkit8gb = false; + if ((totalmem >> MT_MEMSHIFT) > 3 * (MT_WANTRAW - 1)) { + devkit8gb = true; + testThreads = 4; + } + + ull stack_reserve = (ull)(0x4000 + MT_PAGESIZE) * testThreads; + if (totalmem > stack_reserve) + totalmem -= stack_reserve; + s_nthreads = testThreads; + + // Combined memtester + BW burn-in uses small per-thread buffers (the RAM + // pressure comes from the burn-in's continuous bandwidth, not capacity), so + // the single memtester thread's loops complete quickly. Full memtester mode + // maps essentially all of RAM. + size_t combined_each = (size_t)testThreads << 25; // threads * 32 MB + + // Create the worker threads BEFORE allocating the (potentially RAM-filling) + // test buffers. libnx allocates each thread's stack from the same heap, so + // doing this after the buffers would leave nothing for the stacks and + // threadCreate would fail, ending the run immediately. + bool created[MT_MAX_THREADS] = { false }; + for (int div = 0; div < testThreads; div++) { + s_buf[div] = NULL; + s_workers[div].idx = div; + s_workers[div].burnin = (s_mode == 1 && div != 0) ? 1 : 0; + s_workers[div].burn_kernel = 0; + s_workers[div].aligned = NULL; + s_workers[div].words = 0; + s_workers[div].loop = 0; + s_workers[div].iters = 0; + s_workers[div].mismatches = 0; + if (R_SUCCEEDED(threadCreate(&s_tobj[div], worker_main, &s_workers[div], NULL, 0x4000, MT_WORKER_PRIO, div == 3 ? -2 : div))) + created[div] = true; + } + + for (int div = 0; div < testThreads; div++) { + if (!created[div]) + continue; + void volatile *buf = NULL; + size_t w; + if (s_mode == 1) + w = combined_each; + else if (devkit8gb) + w = (div != 3) ? (totalmem / 3) : ((size_t)(MT_WANTRAW - 1) << MT_MEMSHIFT); + else + w = totalmem / testThreads; + + while (!buf && w) { + buf = (void volatile *)malloc(w); + if (!buf) + w -= MT_PAGESIZE; + } + s_buf[div] = buf; + + size_t bufsize = w; + void volatile *aligned; + if ((size_t)buf % MT_PAGESIZE) { + aligned = (void volatile *)(((size_t)buf & pagemask) + MT_PAGESIZE); + bufsize -= ((size_t)aligned - (size_t)buf); + } else { + aligned = buf; + } + + s_workers[div].aligned = aligned; + s_workers[div].words = bufsize / sizeof(ul); + s_total_mb += (uint64_t)(bufsize >> MT_MEMSHIFT); + } + + s_cur_test = "Stuck Address"; + s_step = 0; + + bool started[MT_MAX_THREADS] = { false }; + for (int div = 0; div < testThreads; div++) { + if (!created[div]) + continue; + if (R_SUCCEEDED(threadStart(&s_tobj[div]))) + started[div] = true; + } + + for (int div = 0; div < testThreads; div++) { + if (!created[div]) + continue; + if (started[div]) + threadWaitForExit(&s_tobj[div]); + threadClose(&s_tobj[div]); + } + + for (int div = 0; div < testThreads; div++) { + if (s_buf[div]) + free((void *)s_buf[div]); + s_buf[div] = NULL; + } + + s_done = true; + s_running = false; +} + +void mt_cpu_start(int mode) { + if (s_running) + return; + s_mode = mode; + s_stop = false; + mt_abort = 0; + s_done = false; + s_total_mb = 0; + s_nthreads = 0; + s_workers[0].loop = 0; + s_cur_test = "Preparing..."; + s_running = true; + appletSetAutoSleepDisabled(true); + + if (s_coord_open) { + threadWaitForExit(&s_coord); + threadClose(&s_coord); + s_coord_open = false; + } + + if (R_FAILED(threadCreate(&s_coord, coordinator, NULL, NULL, 0x4000, MT_WORKER_PRIO, -2))) { + s_running = false; + return; + } + s_coord_open = true; + threadStart(&s_coord); +} + +void mt_cpu_stop(void) { + s_stop = true; + mt_abort = 1; + if (s_coord_open) { + threadWaitForExit(&s_coord); + threadClose(&s_coord); + s_coord_open = false; + } + s_running = false; + appletSetAutoSleepDisabled(false); +} + +int mt_cpu_running(void) { + return s_running ? 1 : 0; +} + +void mt_cpu_get(mt_cpu_status_t *out) { + if (!out) + return; + uint64_t burnin = 0, mism = 0; + int n = s_nthreads; + for (int i = 0; i < n; i++) { + mism += s_workers[i].mismatches; + if (s_workers[i].burnin) + burnin += s_workers[i].iters; + } + out->loop = s_workers[0].loop; + out->test = s_cur_test; + out->error = mism > 0 ? 1 : 0; + out->mismatches = mism; + out->burnin_iters = burnin; + out->done = s_done; + out->total_mb = s_total_mb; + out->threads = s_nthreads; + int step = s_step; + if (step < 0) + step = 0; + if (step > MT_TESTS_COUNT) + step = MT_TESTS_COUNT; + out->progress = (float)step / (float)MT_TESTS_COUNT; +} diff --git a/Source/Benchmark-Toolbox/source/memtester/mt_cpu.h b/Source/Benchmark-Toolbox/source/memtester/mt_cpu.h new file mode 100644 index 00000000..76413f2d --- /dev/null +++ b/Source/Benchmark-Toolbox/source/memtester/mt_cpu.h @@ -0,0 +1,28 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + uint64_t loop; + const char *test; + uint64_t mismatches; + uint64_t burnin_iters; + int error; + int done; + uint64_t total_mb; + int threads; + float progress; +} mt_cpu_status_t; + +void mt_cpu_start(int mode); +void mt_cpu_stop(void); +int mt_cpu_running(void); +void mt_cpu_get(mt_cpu_status_t *out); + +#ifdef __cplusplus +} +#endif diff --git a/Source/Benchmark-Toolbox/source/memtester/mt_gpu.cpp b/Source/Benchmark-Toolbox/source/memtester/mt_gpu.cpp new file mode 100644 index 00000000..7f629e63 --- /dev/null +++ b/Source/Benchmark-Toolbox/source/memtester/mt_gpu.cpp @@ -0,0 +1,336 @@ +#include "mt_gpu.h" + +#include +#include + +#include + +#include +#include + +namespace { + + const char *SH_FILL = + "#version 430\n" + "layout(std430, binding = 0) buffer srcBuffer { volatile uint src[]; };\n" + "layout(local_size_x = 256, local_size_y = 1, local_size_z = 1) in;\n" + "uniform uint var;\n" + "void main() {\n" + " src[gl_GlobalInvocationID.x] = var + gl_GlobalInvocationID.x;\n" + "}\n"; + + const char *SH_VERIFY_FILL = + "#version 430\n" + "layout(std430, binding = 0) buffer srcBuffer { volatile uint src[]; };\n" + "layout(std430, binding = 2) buffer resBuffer { uint res; };\n" + "layout(local_size_x = 256, local_size_y = 1, local_size_z = 1) in;\n" + "uniform uint var;\n" + "void main() {\n" + " if (src[gl_GlobalInvocationID.x] != (var + gl_GlobalInvocationID.x)) {\n" + " res = 0x55555555u;\n" + " }\n" + "}\n"; + + const char *SH_COPY = + "#version 430\n" + "layout(std430, binding = 0) buffer srcBuffer { volatile uint src[]; };\n" + "layout(std430, binding = 1) buffer dstBuffer { volatile uint dst[]; };\n" + "layout(local_size_x = 256, local_size_y = 1, local_size_z = 1) in;\n" + "void main() {\n" + " dst[gl_GlobalInvocationID.x] = src[gl_GlobalInvocationID.x];\n" + "}\n"; + + const char *SH_VERIFY_COPY = + "#version 430\n" + "layout(std430, binding = 0) buffer srcBuffer { volatile uint src[]; };\n" + "layout(std430, binding = 1) buffer dstBuffer { volatile uint dst[]; };\n" + "layout(std430, binding = 2) buffer resBuffer { uint res; };\n" + "layout(local_size_x = 256, local_size_y = 1, local_size_z = 1) in;\n" + "void main() {\n" + " if (src[gl_GlobalInvocationID.x] != dst[gl_GlobalInvocationID.x]) {\n" + " res = 0x55555555u;\n" + " }\n" + "}\n"; + + std::thread g_thread; + std::atomic g_stop{ false }; + std::atomic g_running{ false }; + std::atomic g_error{ false }; + std::atomic g_loop{ 0 }; + std::atomic g_mismatches{ 0 }; + std::atomic g_size_mb{ 0 }; + const char *volatile g_status = "Stopped"; + + EGLDisplay s_dpy = EGL_NO_DISPLAY; + EGLContext s_ctx = EGL_NO_CONTEXT; + + uint32_t s_rng_a = 0x12345, s_rng_b = 0x34211, s_rng_c = 0x57a3f, s_rng_d = 0x9e3779; + + uint32_t nextRandom() { + s_rng_a = (s_rng_a & 0x3ffe) << 18 | (s_rng_a ^ s_rng_a << 6) >> 13; + s_rng_b = (s_rng_b << 2 & 0xffffffe0) | (s_rng_b << 2 ^ s_rng_b) >> 27; + s_rng_c = (s_rng_c & 0x1fffff0) << 7 | (s_rng_c ^ s_rng_c << 13) >> 21; + s_rng_d = (s_rng_d & 0x7ff80) << 13 | (s_rng_d ^ s_rng_d << 3) >> 12; + return s_rng_a ^ s_rng_b ^ s_rng_c ^ s_rng_d; + } + + bool eglUp() { + s_dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (s_dpy == EGL_NO_DISPLAY) + return false; + if (!eglInitialize(s_dpy, nullptr, nullptr)) + return false; + if (!eglBindAPI(EGL_OPENGL_API)) + return false; + + const EGLint cfgAttr[] = { EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, + EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8, + EGL_NONE }; + EGLConfig cfg; + EGLint n = 0; + if (!eglChooseConfig(s_dpy, cfgAttr, &cfg, 1, &n) || n == 0) + return false; + + const EGLint ctxAttr[] = { EGL_CONTEXT_MAJOR_VERSION, 4, EGL_CONTEXT_MINOR_VERSION, 3, EGL_NONE }; + s_ctx = eglCreateContext(s_dpy, cfg, EGL_NO_CONTEXT, ctxAttr); + if (s_ctx == EGL_NO_CONTEXT) + return false; + + if (eglMakeCurrent(s_dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, s_ctx) != EGL_TRUE) + return false; + return true; + } + + void eglDown() { + if (s_dpy) { + eglMakeCurrent(s_dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + if (s_ctx) + eglDestroyContext(s_dpy, s_ctx); + eglTerminate(s_dpy); + } + s_ctx = EGL_NO_CONTEXT; + s_dpy = EGL_NO_DISPLAY; + eglReleaseThread(); + } + + GLuint buildProgram(const char *src) { + GLuint sh = glCreateShader(GL_COMPUTE_SHADER); + glShaderSource(sh, 1, &src, nullptr); + glCompileShader(sh); + GLint ok = 0; + glGetShaderiv(sh, GL_COMPILE_STATUS, &ok); + if (!ok) { + glDeleteShader(sh); + return 0; + } + GLuint prog = glCreateProgram(); + glAttachShader(prog, sh); + glLinkProgram(prog); + glDeleteShader(sh); + glGetProgramiv(prog, GL_LINK_STATUS, &ok); + if (!ok) { + glDeleteProgram(prog); + return 0; + } + return prog; + } + + void worker(bool full) { + if (!eglUp()) { + g_status = "EGL init failed"; + g_error = true; + eglDown(); + g_running.store(false); + return; + } + gladLoadGLLoader((GLADloadproc)eglGetProcAddress); + + GLint maxSsbo = 0; + glGetIntegerv(GL_MAX_SHADER_STORAGE_BLOCK_SIZE, &maxSsbo); + if (maxSsbo <= 0) { + g_status = "Failed to query SSBO size"; + g_error = true; + eglDown(); + g_running.store(false); + return; + } + + size_t cap = full ? (size_t)0x20000000 : (size_t)0x8000000; + size_t want = full ? ((size_t)maxSsbo << 2) : (size_t)maxSsbo; + size_t bytes = (want < cap + 1) ? (want & ~(size_t)0x3ff) : cap; + if (bytes < 0x400) { + g_status = "GPU memtester buffer too small"; + g_error = true; + eglDown(); + g_running.store(false); + return; + } + + g_size_mb.store((uint64_t)(bytes >> 20)); + GLuint groups = (GLuint)(bytes >> 10); + + GLuint fill = buildProgram(SH_FILL); + GLuint verifyFill = buildProgram(SH_VERIFY_FILL); + GLuint copy = buildProgram(SH_COPY); + GLuint verifyCopy = buildProgram(SH_VERIFY_COPY); + if (!fill || !verifyFill || !copy || !verifyCopy) { + g_status = "Compute program build failed"; + g_error = true; + if (fill) glDeleteProgram(fill); + if (verifyFill) glDeleteProgram(verifyFill); + if (copy) glDeleteProgram(copy); + if (verifyCopy) glDeleteProgram(verifyCopy); + eglDown(); + g_running.store(false); + return; + } + + GLint fillVarLoc = glGetUniformLocation(fill, "var"); + GLint verifyVarLoc = glGetUniformLocation(verifyFill, "var"); + + GLuint bufs[3] = { 0, 0, 0 }; + glGenBuffers(3, bufs); + const GLuint resInit = 0; + glBindBuffer(GL_SHADER_STORAGE_BUFFER, bufs[0]); + glBufferData(GL_SHADER_STORAGE_BUFFER, bytes, nullptr, GL_DYNAMIC_COPY); + glBindBuffer(GL_SHADER_STORAGE_BUFFER, bufs[1]); + glBufferData(GL_SHADER_STORAGE_BUFFER, bytes, nullptr, GL_DYNAMIC_COPY); + glBindBuffer(GL_SHADER_STORAGE_BUFFER, bufs[2]); + glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(GLuint), &resInit, GL_DYNAMIC_COPY); + + if (glGetError() != GL_NO_ERROR) { + g_status = "Failed to allocate GPU memtester buffers"; + g_error = true; + glDeleteBuffers(3, bufs); + glDeleteProgram(fill); + glDeleteProgram(verifyFill); + glDeleteProgram(copy); + glDeleteProgram(verifyCopy); + eglDown(); + g_running.store(false); + return; + } + + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, bufs[0]); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, bufs[1]); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, bufs[2]); + + g_status = "Running"; + + while (!g_stop.load()) { + uint32_t var = nextRandom(); + + glUseProgram(fill); + if (fillVarLoc >= 0) + glUniform1ui(fillVarLoc, var); + glDispatchCompute(groups, 1, 1); + glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT | GL_BUFFER_UPDATE_BARRIER_BIT); + glFinish(); + if (glGetError() != GL_NO_ERROR) { + g_status = "GPU error during GPU memtester"; + g_error = true; + break; + } + + glBindBuffer(GL_SHADER_STORAGE_BUFFER, bufs[2]); + glBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, sizeof(GLuint), &resInit); + glUseProgram(verifyFill); + if (verifyVarLoc >= 0) + glUniform1ui(verifyVarLoc, var); + glDispatchCompute(groups, 1, 1); + glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT | GL_BUFFER_UPDATE_BARRIER_BIT); + glFinish(); + { + GLuint res = 0; + glBindBuffer(GL_SHADER_STORAGE_BUFFER, bufs[2]); + glGetBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, sizeof(GLuint), &res); + if (res == 0x55555555u) + g_mismatches.fetch_add(1); + } + if (g_stop.load()) + break; + + glUseProgram(copy); + glDispatchCompute(groups, 1, 1); + glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT | GL_BUFFER_UPDATE_BARRIER_BIT); + glFinish(); + if (glGetError() != GL_NO_ERROR) { + g_status = "GPU error during GPU memtester"; + g_error = true; + break; + } + + glBindBuffer(GL_SHADER_STORAGE_BUFFER, bufs[2]); + glBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, sizeof(GLuint), &resInit); + glUseProgram(verifyCopy); + glDispatchCompute(groups, 1, 1); + glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT | GL_BUFFER_UPDATE_BARRIER_BIT); + glFinish(); + { + GLuint res = 0; + glBindBuffer(GL_SHADER_STORAGE_BUFFER, bufs[2]); + glGetBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, sizeof(GLuint), &res); + if (res == 0x55555555u) + g_mismatches.fetch_add(1); + } + + g_loop.fetch_add(1); + } + + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, 0); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, 0); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, 0); + glUseProgram(0); + glDeleteBuffers(3, bufs); + glDeleteProgram(copy); + glDeleteProgram(verifyCopy); + glDeleteProgram(verifyFill); + glDeleteProgram(fill); + eglDown(); + + if (!g_error.load()) + g_status = "Stopped"; + g_running.store(false); + } + +} // namespace + +extern "C" void mt_gpu_start(int full) { + if (g_running.load()) + return; + if (g_thread.joinable()) + g_thread.join(); + g_stop.store(false); + g_error.store(false); + g_loop.store(0); + g_mismatches.store(0); + g_size_mb.store(0); + g_status = "Preparing GPU memtester..."; + g_running.store(true); + appletSetAutoSleepDisabled(true); + g_thread = std::thread(worker, full != 0); +} + +extern "C" void mt_gpu_stop(void) { + g_stop.store(true); + if (g_thread.joinable()) + g_thread.join(); + g_running.store(false); + appletSetAutoSleepDisabled(false); +} + +extern "C" int mt_gpu_running(void) { + return g_running.load() ? 1 : 0; +} + +extern "C" void mt_gpu_get(mt_gpu_status_t *out) { + if (!out) + return; + out->loop = g_loop.load(); + out->mismatches = g_mismatches.load(); + out->size_mb = g_size_mb.load(); + out->running = g_running.load() ? 1 : 0; + out->error = g_error.load() ? 1 : 0; + out->status = g_status; +} diff --git a/Source/Benchmark-Toolbox/source/memtester/mt_gpu.h b/Source/Benchmark-Toolbox/source/memtester/mt_gpu.h new file mode 100644 index 00000000..8c5e785f --- /dev/null +++ b/Source/Benchmark-Toolbox/source/memtester/mt_gpu.h @@ -0,0 +1,25 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + uint64_t loop; + uint64_t mismatches; + uint64_t size_mb; + int running; + int error; + const char *status; +} mt_gpu_status_t; + +void mt_gpu_start(int full); +void mt_gpu_stop(void); +int mt_gpu_running(void); +void mt_gpu_get(mt_gpu_status_t *out); + +#ifdef __cplusplus +} +#endif diff --git a/Source/Benchmark-Toolbox/source/memtester/mt_tests.c b/Source/Benchmark-Toolbox/source/memtester/mt_tests.c new file mode 100644 index 00000000..62678d81 --- /dev/null +++ b/Source/Benchmark-Toolbox/source/memtester/mt_tests.c @@ -0,0 +1,398 @@ +#include "mt_tests.h" + +#include +#include +#include + +typedef unsigned long ul; +typedef unsigned long volatile ulv; + +#define rand32() ((unsigned int)rand() | ((unsigned int)rand() << 16)) + +#if (ULONG_MAX == 4294967295UL) +#define rand_ul() rand32() +#define UL_ONEBITS 0xffffffff +#define UL_LEN 32 +#define CHECKERBOARD1 0x55555555 +#define CHECKERBOARD2 0xaaaaaaaa +#define UL_BYTE(x) ((x | x << 8 | x << 16 | x << 24)) +#else +#define rand64() (((ul)rand32()) << 32 | ((ul)rand32())) +#define rand_ul() rand64() +#define UL_ONEBITS 0xffffffffffffffffUL +#define UL_LEN 64 +#define CHECKERBOARD1 0x5555555555555555 +#define CHECKERBOARD2 0xaaaaaaaaaaaaaaaa +#define UL_BYTE(x) (((ul)x | (ul)x << 8 | (ul)x << 16 | (ul)x << 24 | (ul)x << 32 | (ul)x << 40 | (ul)x << 48 | (ul)x << 56)) +#endif + +#define ONE 0x00000001L + +unsigned short mt_dividend = 1; +volatile int mt_abort = 0; + +struct mt_test mt_tests[] = { + { "Random Value", mt_test_random_value }, + { "Compare XOR", mt_test_xor_comparison }, + { "Compare SUB", mt_test_sub_comparison }, + { "Compare MUL", mt_test_mul_comparison }, + { "Compare DIV", mt_test_div_comparison }, + { "Compare OR", mt_test_or_comparison }, + { "Compare AND", mt_test_and_comparison }, + { "Sequential Increment", mt_test_seqinc_comparison }, + { "Solid Bits", mt_test_solidbits_comparison }, + { "Block Sequential", mt_test_blockseq_comparison }, + { "Checkerboard", mt_test_checkerboard_comparison }, + { "Bit Spread", mt_test_bitspread_comparison }, + { "Bit Flip", mt_test_bitflip_comparison }, + { "Walking Ones", mt_test_walkbits0_comparison }, + { "Walking Zeroes", mt_test_walkbits1_comparison }, + { NULL, NULL } +}; + +struct mt_test mt_stress_tests[] = { + { "Stress memcpy x128", mt_test_stress_memcpy }, + { "Stress memset x128", mt_test_stress_memset }, + { "Stress memcmp x32", mt_test_stress_memcmp }, + { NULL, NULL } +}; + +static int compare_regions(ulv *bufa, ulv *bufb, size_t count) { + int r = 0; + size_t i; + ulv *p1 = bufa; + ulv *p2 = bufb; + + for (i = 0; i < count; i++, p1++, p2++) { + if (*p1 != *p2) + r = -1; + } + return r; +} + +int mt_test_stuck_address(ulv *bufa, size_t count) { + ulv *p1 = bufa; + unsigned int j; + size_t i; + + for (j = 0; j < (16 / mt_dividend); j++) { + if (mt_abort) + return 0; + p1 = (ulv *)bufa; + for (i = 0; i < count; i++) { + *p1 = ((j + i) % 2) == 0 ? (ul)p1 : ~((ul)p1); + *p1++; + } + p1 = (ulv *)bufa; + for (i = 0; i < count; i++, p1++) { + if (*p1 != (((j + i) % 2) == 0 ? (ul)p1 : ~((ul)p1))) + return -1; + } + } + return 0; +} + +int mt_test_stress_memcpy(ulv *bufa, ulv *bufb, size_t count) { + unsigned int j; + + int q = rand_ul(); + memset((void *)bufa, q, count * sizeof(ul)); + memset((void *)bufb, q, count * sizeof(ul)); + + for (j = 0; j < 128; j++) + memcpy((void *)bufa, (void *)bufb, count * sizeof(ul)); + + return memcmp((void *)bufa, (void *)bufb, count * sizeof(ul)); +} + +int mt_test_stress_memset(ulv *bufa, ulv *bufb, size_t count) { + unsigned int j; + + for (j = 0; j < 128; j++) { + int q = rand_ul(); + memset((void *)bufa, q, count * sizeof(ul)); + memset((void *)bufb, q, count * sizeof(ul)); + } + return memcmp((void *)bufa, (void *)bufb, count * sizeof(ul)); +} + +int mt_test_stress_memcmp(ulv *bufa, ulv *bufb, size_t count) { + ulv *p1 = bufa; + ulv *p2 = bufb; + unsigned int j; + + int q = rand_ul(); + memset((void *)bufa, q, count * sizeof(ul)); + memset((void *)bufb, q, count * sizeof(ul)); + + int rc = 0; + + for (j = 0; j < 32; j++) { + *p1++ = *p2++ = rand_ul(); + rc = memcmp((void *)bufa, (void *)bufb, count * sizeof(ul)); + if (rc) + return rc; + } + return rc; +} + +int mt_test_random_value(ulv *bufa, ulv *bufb, size_t count) { + ulv *p1 = bufa; + ulv *p2 = bufb; + size_t i; + + for (i = 0; i < count; i++) + *p1++ = *p2++ = rand_ul(); + + return compare_regions(bufa, bufb, count); +} + +int mt_test_xor_comparison(ulv *bufa, ulv *bufb, size_t count) { + ulv *p1 = bufa; + ulv *p2 = bufb; + size_t i; + ul q = rand_ul(); + + for (i = 0; i < count; i++) { + *p1++ ^= q; + *p2++ ^= q; + } + return compare_regions(bufa, bufb, count); +} + +int mt_test_sub_comparison(ulv *bufa, ulv *bufb, size_t count) { + ulv *p1 = bufa; + ulv *p2 = bufb; + size_t i; + ul q = rand_ul(); + + for (i = 0; i < count; i++) { + *p1++ -= q; + *p2++ -= q; + } + return compare_regions(bufa, bufb, count); +} + +int mt_test_mul_comparison(ulv *bufa, ulv *bufb, size_t count) { + ulv *p1 = bufa; + ulv *p2 = bufb; + size_t i; + ul q = rand_ul(); + + for (i = 0; i < count; i++) { + *p1++ *= q; + *p2++ *= q; + } + return compare_regions(bufa, bufb, count); +} + +int mt_test_div_comparison(ulv *bufa, ulv *bufb, size_t count) { + ulv *p1 = bufa; + ulv *p2 = bufb; + size_t i; + ul q = rand_ul(); + + for (i = 0; i < count; i++) { + if (!q) + q++; + *p1++ /= q; + *p2++ /= q; + } + return compare_regions(bufa, bufb, count); +} + +int mt_test_or_comparison(ulv *bufa, ulv *bufb, size_t count) { + ulv *p1 = bufa; + ulv *p2 = bufb; + size_t i; + ul q = rand_ul(); + + for (i = 0; i < count; i++) { + *p1++ |= q; + *p2++ |= q; + } + return compare_regions(bufa, bufb, count); +} + +int mt_test_and_comparison(ulv *bufa, ulv *bufb, size_t count) { + ulv *p1 = bufa; + ulv *p2 = bufb; + size_t i; + ul q = rand_ul(); + + for (i = 0; i < count; i++) { + *p1++ &= q; + *p2++ &= q; + } + return compare_regions(bufa, bufb, count); +} + +int mt_test_seqinc_comparison(ulv *bufa, ulv *bufb, size_t count) { + ulv *p1 = bufa; + ulv *p2 = bufb; + size_t i; + ul q = rand_ul(); + + for (i = 0; i < count; i++) + *p1++ = *p2++ = (i + q); + + return compare_regions(bufa, bufb, count); +} + +int mt_test_solidbits_comparison(ulv *bufa, ulv *bufb, size_t count) { + ulv *p1 = bufa; + ulv *p2 = bufb; + unsigned int j; + ul q; + size_t i; + + for (j = 0; j < (64 / mt_dividend / mt_dividend); j++) { + if (mt_abort) + return 0; + q = (j % 2) == 0 ? UL_ONEBITS : 0; + p1 = (ulv *)bufa; + p2 = (ulv *)bufb; + for (i = 0; i < count; i++) + *p1++ = *p2++ = (i % 2) == 0 ? q : ~q; + if (compare_regions(bufa, bufb, count)) + return -1; + } + return 0; +} + +int mt_test_checkerboard_comparison(ulv *bufa, ulv *bufb, size_t count) { + ulv *p1 = bufa; + ulv *p2 = bufb; + unsigned int j; + ul q; + size_t i; + + for (j = 0; j < (64 / mt_dividend / mt_dividend); j++) { + if (mt_abort) + return 0; + q = (j % 2) == 0 ? CHECKERBOARD1 : CHECKERBOARD2; + p1 = (ulv *)bufa; + p2 = (ulv *)bufb; + for (i = 0; i < count; i++) + *p1++ = *p2++ = (i % 2) == 0 ? q : ~q; + if (compare_regions(bufa, bufb, count)) + return -1; + } + return 0; +} + +int mt_test_blockseq_comparison(ulv *bufa, ulv *bufb, size_t count) { + ulv *p1 = bufa; + ulv *p2 = bufb; + unsigned int j; + size_t i; + + for (j = 0; j < (64 / mt_dividend / mt_dividend); j++) { + if (mt_abort) + return 0; + p1 = (ulv *)bufa; + p2 = (ulv *)bufb; + for (i = 0; i < count; i++) + *p1++ = *p2++ = (ul)UL_BYTE(j); + if (compare_regions(bufa, bufb, count)) + return -1; + } + return 0; +} + +int mt_test_walkbits0_comparison(ulv *bufa, ulv *bufb, size_t count) { + ulv *p1 = bufa; + ulv *p2 = bufb; + unsigned int j; + size_t i; + + for (j = 0; j < (UL_LEN * 2 / mt_dividend / mt_dividend); j++) { + if (mt_abort) + return 0; + p1 = (ulv *)bufa; + p2 = (ulv *)bufb; + for (i = 0; i < count; i++) { + if (j < UL_LEN) + *p1++ = *p2++ = ONE << j; + else + *p1++ = *p2++ = ONE << (UL_LEN * 2 - j - 1); + } + if (compare_regions(bufa, bufb, count)) + return -1; + } + return 0; +} + +int mt_test_walkbits1_comparison(ulv *bufa, ulv *bufb, size_t count) { + ulv *p1 = bufa; + ulv *p2 = bufb; + unsigned int j; + size_t i; + + for (j = 0; j < (UL_LEN * 2 / mt_dividend / mt_dividend); j++) { + if (mt_abort) + return 0; + p1 = (ulv *)bufa; + p2 = (ulv *)bufb; + for (i = 0; i < count; i++) { + if (j < UL_LEN) + *p1++ = *p2++ = UL_ONEBITS ^ (ONE << j); + else + *p1++ = *p2++ = UL_ONEBITS ^ (ONE << (UL_LEN * 2 - j - 1)); + } + if (compare_regions(bufa, bufb, count)) + return -1; + } + return 0; +} + +int mt_test_bitspread_comparison(ulv *bufa, ulv *bufb, size_t count) { + ulv *p1 = bufa; + ulv *p2 = bufb; + unsigned int j; + size_t i; + + for (j = 0; j < (UL_LEN * 2 / mt_dividend / mt_dividend); j++) { + if (mt_abort) + return 0; + p1 = (ulv *)bufa; + p2 = (ulv *)bufb; + for (i = 0; i < count; i++) { + if (j < UL_LEN) + *p1++ = *p2++ = (i % 2 == 0) + ? (ONE << j) | (ONE << (j + 2)) + : UL_ONEBITS ^ ((ONE << j) | (ONE << (j + 2))); + else + *p1++ = *p2++ = (i % 2 == 0) + ? (ONE << (UL_LEN * 2 - 1 - j)) | (ONE << (UL_LEN * 2 + 1 - j)) + : UL_ONEBITS ^ (ONE << (UL_LEN * 2 - 1 - j) | (ONE << (UL_LEN * 2 + 1 - j))); + } + if (compare_regions(bufa, bufb, count)) + return -1; + } + return 0; +} + +int mt_test_bitflip_comparison(ulv *bufa, ulv *bufb, size_t count) { + ulv *p1 = bufa; + ulv *p2 = bufb; + unsigned int j, k; + ul q; + size_t i; + + for (k = 0; k < (UL_LEN / mt_dividend / mt_dividend); k++) { + if (mt_abort) + return 0; + q = ONE << k; + for (j = 0; j < 8; j++) { + q = ~q; + p1 = (ulv *)bufa; + p2 = (ulv *)bufb; + for (i = 0; i < count; i++) + *p1++ = *p2++ = (i % 2) == 0 ? q : ~q; + if (compare_regions(bufa, bufb, count)) + return -1; + } + } + return 0; +} diff --git a/Source/Benchmark-Toolbox/source/memtester/mt_tests.h b/Source/Benchmark-Toolbox/source/memtester/mt_tests.h new file mode 100644 index 00000000..ac63950b --- /dev/null +++ b/Source/Benchmark-Toolbox/source/memtester/mt_tests.h @@ -0,0 +1,45 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern unsigned short mt_dividend; +extern volatile int mt_abort; + +typedef int (*mt_test_fp)(unsigned long volatile *, unsigned long volatile *, size_t); + +struct mt_test { + const char *name; + mt_test_fp fp; +}; + +extern struct mt_test mt_tests[]; +extern struct mt_test mt_stress_tests[]; + +int mt_test_stuck_address(unsigned long volatile *bufa, size_t count); +int mt_test_random_value(unsigned long volatile *bufa, unsigned long volatile *bufb, size_t count); +int mt_test_xor_comparison(unsigned long volatile *bufa, unsigned long volatile *bufb, size_t count); +int mt_test_sub_comparison(unsigned long volatile *bufa, unsigned long volatile *bufb, size_t count); +int mt_test_mul_comparison(unsigned long volatile *bufa, unsigned long volatile *bufb, size_t count); +int mt_test_div_comparison(unsigned long volatile *bufa, unsigned long volatile *bufb, size_t count); +int mt_test_or_comparison(unsigned long volatile *bufa, unsigned long volatile *bufb, size_t count); +int mt_test_and_comparison(unsigned long volatile *bufa, unsigned long volatile *bufb, size_t count); +int mt_test_seqinc_comparison(unsigned long volatile *bufa, unsigned long volatile *bufb, size_t count); +int mt_test_solidbits_comparison(unsigned long volatile *bufa, unsigned long volatile *bufb, size_t count); +int mt_test_checkerboard_comparison(unsigned long volatile *bufa, unsigned long volatile *bufb, size_t count); +int mt_test_blockseq_comparison(unsigned long volatile *bufa, unsigned long volatile *bufb, size_t count); +int mt_test_walkbits0_comparison(unsigned long volatile *bufa, unsigned long volatile *bufb, size_t count); +int mt_test_walkbits1_comparison(unsigned long volatile *bufa, unsigned long volatile *bufb, size_t count); +int mt_test_bitspread_comparison(unsigned long volatile *bufa, unsigned long volatile *bufb, size_t count); +int mt_test_bitflip_comparison(unsigned long volatile *bufa, unsigned long volatile *bufb, size_t count); + +int mt_test_stress_memcpy(unsigned long volatile *bufa, unsigned long volatile *bufb, size_t count); +int mt_test_stress_memset(unsigned long volatile *bufa, unsigned long volatile *bufb, size_t count); +int mt_test_stress_memcmp(unsigned long volatile *bufa, unsigned long volatile *bufb, size_t count); + +#ifdef __cplusplus +} +#endif