benchmark_toolbox: memtester
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
331
Source/Benchmark-Toolbox/source/memtester/mt_cpu.c
Normal file
331
Source/Benchmark-Toolbox/source/memtester/mt_cpu.c
Normal file
@@ -0,0 +1,331 @@
|
||||
#include "mt_cpu.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <switch.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
28
Source/Benchmark-Toolbox/source/memtester/mt_cpu.h
Normal file
28
Source/Benchmark-Toolbox/source/memtester/mt_cpu.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#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
|
||||
336
Source/Benchmark-Toolbox/source/memtester/mt_gpu.cpp
Normal file
336
Source/Benchmark-Toolbox/source/memtester/mt_gpu.cpp
Normal file
@@ -0,0 +1,336 @@
|
||||
#include "mt_gpu.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
|
||||
#include <switch.h>
|
||||
|
||||
#include <EGL/egl.h>
|
||||
#include <glad/glad.h>
|
||||
|
||||
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<bool> g_stop{ false };
|
||||
std::atomic<bool> g_running{ false };
|
||||
std::atomic<bool> g_error{ false };
|
||||
std::atomic<uint64_t> g_loop{ 0 };
|
||||
std::atomic<uint64_t> g_mismatches{ 0 };
|
||||
std::atomic<uint64_t> 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;
|
||||
}
|
||||
25
Source/Benchmark-Toolbox/source/memtester/mt_gpu.h
Normal file
25
Source/Benchmark-Toolbox/source/memtester/mt_gpu.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#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
|
||||
398
Source/Benchmark-Toolbox/source/memtester/mt_tests.c
Normal file
398
Source/Benchmark-Toolbox/source/memtester/mt_tests.c
Normal file
@@ -0,0 +1,398 @@
|
||||
#include "mt_tests.h"
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
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;
|
||||
}
|
||||
45
Source/Benchmark-Toolbox/source/memtester/mt_tests.h
Normal file
45
Source/Benchmark-Toolbox/source/memtester/mt_tests.h
Normal file
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#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
|
||||
Reference in New Issue
Block a user