Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Performance tests #3172

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions include/gba/io_reg.h
Original file line number Diff line number Diff line change
Expand Up @@ -664,6 +664,7 @@
#define TIMER_64CLK 0x01
#define TIMER_256CLK 0x02
#define TIMER_1024CLK 0x03
#define TIMER_COUNTUP 0x04
#define TIMER_INTR_ENABLE 0x40
#define TIMER_ENABLE 0x80

Expand Down
57 changes: 57 additions & 0 deletions test/random.c
Original file line number Diff line number Diff line change
Expand Up @@ -151,3 +151,60 @@ TEST("RandomElement generates a uniform distribution")

EXPECT_LT(error, UQ_4_12(0.025));
}

TEST("RandomUniform mul-based faster than mod-based (compile-time)")
{
u32 i;
struct Benchmark mulBenchmark, modBenchmark;
u32 mulSum = 0, modSum = 0;

BENCHMARK(&mulBenchmark)
{
mulSum += RandomUniformDefault(RNG_NONE, 0, 1);
mulSum += RandomUniformDefault(RNG_NONE, 0, 2);
mulSum += RandomUniformDefault(RNG_NONE, 0, 3);
mulSum += RandomUniformDefault(RNG_NONE, 0, 4);
}

BENCHMARK(&modBenchmark)
{
modSum += Random() % 2;
modSum += Random() % 3;
modSum += Random() % 4;
modSum += Random() % 5;
}

EXPECT_FASTER(mulBenchmark, modBenchmark);

// Reference mulSum/modSum to prevent optimization.
// These numbers are different because multiplication and modulus
// have subtly different biases (so subtle that it's irrelevant for
// our purposes).
EXPECT_EQ(mulSum, 3);
EXPECT_EQ(modSum, 4);
}

TEST("RandomUniform mul-based faster than mod-based (run-time)")
{
u32 i;
struct Benchmark mulBenchmark, modBenchmark;
u32 mulSum = 0, modSum = 0;

BENCHMARK(&mulBenchmark)
{
for (i = 0; i < 32; i++)
mulSum += RandomUniformDefault(RNG_NONE, 0, i);
}

BENCHMARK(&modBenchmark)
{
for (i = 0; i < 32; i++)
modSum += Random() % (i + 1);
}

EXPECT_FASTER(mulBenchmark, modBenchmark);

// Reference mulSum/modSum to prevent optimization.
EXPECT_EQ(mulSum, 232);
EXPECT_EQ(modSum, 249);
}
44 changes: 44 additions & 0 deletions test/test.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ struct TestRunnerState
u8 result;
u8 expectedResult;
bool8 expectLeaks:1;
bool8 inBenchmark:1;
u32 timeoutSeconds;
};

Expand Down Expand Up @@ -158,6 +159,49 @@ s32 MgbaPrintf_(const char *fmt, ...);
Test_ExitWithResult(TEST_RESULT_FAIL, "%s:%d: EXPECT_GE(%d, %d) failed", gTestRunnerState.test->filename, __LINE__, _a, _b); \
} while (0)

struct Benchmark { s32 ticks; };

static inline void BenchmarkStart(void)
{
gTestRunnerState.inBenchmark = TRUE;
REG_TM3CNT = (TIMER_ENABLE | TIMER_64CLK) << 16;
}

static inline struct Benchmark BenchmarkStop(void)
{
REG_TM3CNT_H = 0;
gTestRunnerState.inBenchmark = FALSE;
return (struct Benchmark) { REG_TM3CNT_L };
}

#define BENCHMARK(id) \
for (BenchmarkStart(); gTestRunnerState.inBenchmark; *(id) = BenchmarkStop())

// An approximation of how much overhead benchmarks introduce.
#define BENCHMARK_ABS 2

// An approximation for what percentage faster a benchmark has to be for
// us to be confident that it's faster than another.
#define BENCHMARK_REL 95

#define EXPECT_FASTER(a, b) \
do \
{ \
u32 a_ = (a).ticks; u32 b_ = (b).ticks; \
MgbaPrintf_(#a ": %d ticks, " #b ": %d ticks", a_, b_); \
if (((a_ - BENCHMARK_ABS) * BENCHMARK_REL) >= (b_ * 100)) \
Test_ExitWithResult(TEST_RESULT_FAIL, "%s:%d: EXPECT_FASTER(" #a ", " #b ") failed", gTestRunnerState.test->filename, __LINE__); \
} while (0)

#define EXPECT_SLOWER(a, b) \
do \
{ \
u32 a_ = (a).ticks; u32 b_ = (b).ticks; \
MgbaPrintf_(#a ": %d ticks, " #b ": %d ticks", a_, b_); \
if ((a_ * 100) <= ((b_ - BENCHMARK_ABS) * BENCHMARK_REL)) \
Test_ExitWithResult(TEST_RESULT_FAIL, "%s:%d: EXPECT_SLOWER(" #a ", " #b ") failed", gTestRunnerState.test->filename, __LINE__); \
} while (0)

#define KNOWN_FAILING \
Test_ExpectedResult(TEST_RESULT_FAIL)

Expand Down