Enhance perf-main with higher granularity output, optional parseable output and higher granularity duration setting

This commit is contained in:
Jeffrey Pfau 2014-10-06 23:32:26 -07:00
parent 32bc6750c1
commit bac0d35034
3 changed files with 64 additions and 25 deletions

View File

@ -618,6 +618,10 @@ bool GBAIsROM(struct VFile* vf) {
return memcmp(signature, GBA_ROM_MAGIC, sizeof(signature)) == 0; return memcmp(signature, GBA_ROM_MAGIC, sizeof(signature)) == 0;
} }
void GBAGetGameCode(struct GBA* gba, char* out) {
memcpy(out, &((struct GBACartridge*) gba->memory.rom)->id, 4);
}
void GBAHitStub(struct ARMCore* cpu, uint32_t opcode) { void GBAHitStub(struct ARMCore* cpu, uint32_t opcode) {
struct GBA* gba = (struct GBA*) cpu->master; struct GBA* gba = (struct GBA*) cpu->master;
enum GBALogLevel level = GBA_LOG_FATAL; enum GBALogLevel level = GBA_LOG_FATAL;

View File

@ -148,6 +148,7 @@ void GBALoadBIOS(struct GBA* gba, struct VFile* vf);
void GBAApplyPatch(struct GBA* gba, struct Patch* patch); void GBAApplyPatch(struct GBA* gba, struct Patch* patch);
bool GBAIsROM(struct VFile* vf); bool GBAIsROM(struct VFile* vf);
void GBAGetGameCode(struct GBA* gba, char* out);
__attribute__((format (printf, 3, 4))) __attribute__((format (printf, 3, 4)))
void GBALog(struct GBA* gba, enum GBALogLevel level, const char* format, ...); void GBALog(struct GBA* gba, enum GBALogLevel level, const char* format, ...);

View File

@ -7,18 +7,22 @@
#include <signal.h> #include <signal.h>
#include <sys/time.h> #include <sys/time.h>
#define PERF_OPTIONS "NS:" #define PERF_OPTIONS "F:NPS:"
#define PERF_USAGE \ #define PERF_USAGE \
"\nBenchmark options:\n" \ "\nBenchmark options:\n" \
" -F FRAMES Run for the specified number of FRAMES before exiting\n" \
" -N Disable video rendering entirely\n" \ " -N Disable video rendering entirely\n" \
" -P CSV output, useful for parsing\n" \
" -S SEC Run for SEC in-game seconds before exiting" " -S SEC Run for SEC in-game seconds before exiting"
struct PerfOpts { struct PerfOpts {
bool noVideo; bool noVideo;
int duration; bool csv;
unsigned duration;
unsigned frames;
}; };
static void _GBAPerfRunloop(struct GBAThread* context, int* frames); static void _GBAPerfRunloop(struct GBAThread* context, int* frames, bool quiet);
static void _GBAPerfShutdown(int signal); static void _GBAPerfShutdown(int signal);
static bool _parsePerfOpts(struct SubParser* parser, int option, const char* arg); static bool _parsePerfOpts(struct SubParser* parser, int option, const char* arg);
@ -30,7 +34,7 @@ int main(int argc, char** argv) {
struct GBAVideoSoftwareRenderer renderer; struct GBAVideoSoftwareRenderer renderer;
GBAVideoSoftwareRendererCreate(&renderer); GBAVideoSoftwareRendererCreate(&renderer);
struct PerfOpts perfOpts = { false, 0 }; struct PerfOpts perfOpts = { false, false, 0, 0 };
struct SubParser subparser = { struct SubParser subparser = {
.usage = PERF_USAGE, .usage = PERF_USAGE,
.parse = _parsePerfOpts, .parse = _parsePerfOpts,
@ -58,16 +62,24 @@ int main(int argc, char** argv) {
} }
context.debugger = createDebugger(&opts); context.debugger = createDebugger(&opts);
char gameCode[5] = { 0 };
GBAMapOptionsToContext(&opts, &context); GBAMapOptionsToContext(&opts, &context);
GBAThreadStart(&context); GBAThreadStart(&context);
GBAGetGameCode(context.gba, gameCode);
int frames = perfOpts.duration; int frames = perfOpts.frames;
time_t start = time(0); if (!frames) {
_GBAPerfRunloop(&context, &frames); frames = perfOpts.duration * 60;
time_t end = time(0); }
int duration = end - start; struct timeval tv;
gettimeofday(&tv, 0);
uint64_t start = 1000000 * tv.tv_sec + tv.tv_usec;
_GBAPerfRunloop(&context, &frames, perfOpts.csv);
gettimeofday(&tv, 0);
uint64_t end = 1000000 * tv.tv_sec + tv.tv_usec;
uint64_t duration = end - start;
GBAThreadJoin(&context); GBAThreadJoin(&context);
freeOptions(&opts); freeOptions(&opts);
@ -75,12 +87,24 @@ int main(int argc, char** argv) {
free(renderer.outputBuffer); free(renderer.outputBuffer);
printf("%u frames in %i seconds: %g fps (%gx)\n", frames, duration, frames / (float) duration, frames / (duration * 60.f)); float scaledFrames = frames * 1000000.f;
if (perfOpts.csv) {
puts("game_code,frames,duration,renderer");
const char* rendererName;
if (perfOpts.noVideo) {
rendererName = "none";
} else {
rendererName = "software";
}
printf("%s,%i,%lli,%s\n", gameCode, frames, duration, rendererName);
} else {
printf("%u frames in %lli microseconds: %g fps (%gx)\n", frames, duration, scaledFrames / duration, scaledFrames / (duration * 60.f));
}
return 0; return 0;
} }
static void _GBAPerfRunloop(struct GBAThread* context, int* frames) { static void _GBAPerfRunloop(struct GBAThread* context, int* frames, bool quiet) {
struct timeval lastEcho; struct timeval lastEcho;
gettimeofday(&lastEcho, 0); gettimeofday(&lastEcho, 0);
int duration = *frames; int duration = *frames;
@ -90,25 +114,29 @@ static void _GBAPerfRunloop(struct GBAThread* context, int* frames) {
if (GBASyncWaitFrameStart(&context->sync, 0)) { if (GBASyncWaitFrameStart(&context->sync, 0)) {
++*frames; ++*frames;
++lastFrames; ++lastFrames;
struct timeval currentTime; if (!quiet) {
long timeDiff; struct timeval currentTime;
gettimeofday(&currentTime, 0); long timeDiff;
timeDiff = currentTime.tv_sec - lastEcho.tv_sec; gettimeofday(&currentTime, 0);
timeDiff *= 1000; timeDiff = currentTime.tv_sec - lastEcho.tv_sec;
timeDiff += (currentTime.tv_usec - lastEcho.tv_usec) / 1000; timeDiff *= 1000;
if (timeDiff >= 1000) { timeDiff += (currentTime.tv_usec - lastEcho.tv_usec) / 1000;
printf("\033[2K\rCurrent FPS: %g (%gx)", lastFrames / (timeDiff / 1000.0f), lastFrames / (float) (60 * (timeDiff / 1000.0f))); if (timeDiff >= 1000) {
fflush(stdout); printf("\033[2K\rCurrent FPS: %g (%gx)", lastFrames / (timeDiff / 1000.0f), lastFrames / (float) (60 * (timeDiff / 1000.0f)));
lastEcho = currentTime; fflush(stdout);
lastFrames = 0; lastEcho = currentTime;
lastFrames = 0;
}
} }
} }
GBASyncWaitFrameEnd(&context->sync); GBASyncWaitFrameEnd(&context->sync);
if (*frames == duration * 60) { if (*frames == duration) {
_GBAPerfShutdown(0); _GBAPerfShutdown(0);
} }
} }
printf("\033[2K\r"); if (!quiet) {
printf("\033[2K\r");
}
} }
static void _GBAPerfShutdown(int signal) { static void _GBAPerfShutdown(int signal) {
@ -121,11 +149,17 @@ static void _GBAPerfShutdown(int signal) {
static bool _parsePerfOpts(struct SubParser* parser, int option, const char* arg) { static bool _parsePerfOpts(struct SubParser* parser, int option, const char* arg) {
struct PerfOpts* opts = parser->opts; struct PerfOpts* opts = parser->opts;
switch (option) { switch (option) {
case 'F':
opts->frames = strtoul(arg, 0, 10);
return !errno;
case 'N': case 'N':
opts->noVideo = true; opts->noVideo = true;
return true; return true;
case 'P':
opts->csv = true;
return true;
case 'S': case 'S':
opts->duration = strtol(arg, 0, 10); opts->duration = strtoul(arg, 0, 10);
return !errno; return !errno;
default: default:
return false; return false;