4coder/buffer/4coder_test_main.cpp

556 lines
15 KiB
C++
Raw Normal View History

2015-11-06 19:54:24 +00:00
/*
* Mr. 4th Dimention - Allen Webster
* Four Tech
*
* public domain -- no warranty is offered or implied; use this code at your own risk
*
* 06.11.2015
*
* Buffer experiment testing layer
*
*/
// TOP
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <string.h>
#define inline_4tech inline
inline_4tech int
CEIL32(float x){
int extra;
extra = ((x!=(int)(x) && x>0)?1:0);
extra += (int)(x);
return(extra);
}
inline_4tech int
DIVCEIL32(int n, int d) {
int q = (n/d);
q += (q*d < n);
return(q);
}
inline_4tech unsigned int
ROUNDPOT32(unsigned int v){
v--;
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
v++;
return(v);
}
#ifdef fast_test
#define debug_4tech(x)
#define assert_4tech(x)
#endif
#define hard_assert_4tech(x) assert(x)
2015-11-08 21:00:25 +00:00
#ifdef __linux__
#define memzero_4tech(x) memset_4tech(&(x), 0, sizeof(x))
#endif
2015-11-06 19:54:24 +00:00
#include "4coder_shared.cpp"
#include "4coder_golden_array.cpp"
#include "4coder_gap_buffer.cpp"
#include "4coder_multi_gap_buffer.cpp"
#include "4coder_rope_buffer.cpp"
#define Buffer_Type Buffer
#include "4coder_buffer_abstract.cpp"
#undef Buffer_Type
2015-11-08 21:00:25 +00:00
2015-11-06 19:54:24 +00:00
#define Buffer_Type Gap_Buffer
#include "4coder_buffer_abstract.cpp"
#undef Buffer_Type
2015-11-08 21:00:25 +00:00
2015-11-06 19:54:24 +00:00
#define Buffer_Type Multi_Gap_Buffer
#include "4coder_buffer_abstract.cpp"
#undef Buffer_Type
2015-11-08 21:00:25 +00:00
2015-11-06 19:54:24 +00:00
#define Buffer_Type Rope_Buffer
#include "4coder_buffer_abstract.cpp"
#undef Buffer_Type
2015-11-08 21:00:25 +00:00
#if defined(_WIN32)
2015-11-06 19:54:24 +00:00
#include <Windows.h>
typedef unsigned long long time_int;
unsigned long long win32_counts_per_second_4tech;
int time_init(unsigned long long *resolution){
int result;
LARGE_INTEGER time;
result = 0;
if (QueryPerformanceFrequency(&time)){
win32_counts_per_second_4tech = (unsigned long long)(time.QuadPart);
result = 1;
*resolution = win32_counts_per_second_4tech;
}
return(result);
}
time_int get_time(){
LARGE_INTEGER time;
time_int result;
result = 0;
if (QueryPerformanceCounter(&time)){
result = (time_int)(time.QuadPart);
result = result * 1000000 / win32_counts_per_second_4tech;
}
return(result);
}
2015-11-08 21:00:25 +00:00
#elif defined(__linux__)
#include <time.h>
2015-11-06 19:54:24 +00:00
2015-11-08 21:00:25 +00:00
typedef unsigned long long time_int;
int time_init(unsigned long long *resolution){
int result;
struct timespec res;
result = 0;
if (!clock_getres(CLOCK_MONOTONIC, &res)){
result = 1;
if (res.tv_sec > 0 || res.tv_nsec == 0) *resolution = 0;
else *resolution = (unsigned long long)(1000000/res.tv_nsec);
2015-11-06 19:54:24 +00:00
}
2015-11-08 21:00:25 +00:00
return(result);
}
2015-11-06 19:54:24 +00:00
2015-11-08 21:00:25 +00:00
time_int get_time(){
time_int result;
struct timespec time;
result = 0;
if (!clock_gettime(CLOCK_MONOTONIC, &time)){
result = (time.tv_sec * 1000000) + (time.tv_nsec / 1000);
}
return(result);
2015-11-06 19:54:24 +00:00
}
2015-11-08 21:00:25 +00:00
#else
#error Timer not supported on this platform
#endif
2015-11-06 19:54:24 +00:00
typedef struct File_Data{
char *data;
int size;
} File_Data;
2015-11-08 21:00:25 +00:00
File_Data get_file(const char *filename){
2015-11-06 19:54:24 +00:00
FILE *file;
File_Data result;
file = fopen(filename, "rb");
if (!file){
printf("error: could not find file %s\n", filename);
exit(1);
}
fseek(file, 0, SEEK_END);
result.size = ftell(file);
fseek(file, 0, SEEK_SET);
if (result.size == 0){
printf("error: file %s was empty\n", filename);
exit(1);
}
result.data = (char*)malloc(result.size);
fread(result.data, result.size, 1, file);
fclose(file);
return(result);
}
void free_file(File_Data file){
free(file.data);
}
2015-11-08 21:00:25 +00:00
#define STB_TRUETYPE_IMPLEMENTATION
#include "stb_truetype.h"
float* get_font_data(const char *font_file){
float *data = 0;
stbtt_bakedchar *baked;
File_Data file = get_file(font_file);
int stride, offset;
if (file.data){
int size = sizeof(*baked)*256;
baked = (stbtt_bakedchar*)malloc(size);
memset_4tech(baked, 0, sizeof(*baked)*256);
offset = (int)((char*)&baked->xadvance - (char*)baked);
stride = sizeof(*baked);
int w, h;
w = 10*256;
h = 25;
unsigned char *pixels = (unsigned char*)malloc(w * h);
stbtt_BakeFontBitmap((unsigned char*)file.data, 0, 17.f, pixels, w, h, 0, 128, baked);
free(pixels);
free_file(file);
data = (float*)malloc(sizeof(float)*256);
memset_4tech(data, 0, sizeof(float)*256);
char *pos = (char*)baked;
pos += offset;
for (int i = 0; i < 128; ++i){
data[i] = *(float*)pos;
pos += stride;
}
free(baked);
}
else{
printf("error: cannot continue without font\n");
}
return data;
}
void setup(){
unsigned long long resolution;
if (!time_init(&resolution)){
printf("error: could not initialize timer");
exit(1);
}
if (resolution < 1000000)
printf("warning: timer is not actually at high enough resolution for good measurements!\n");
}
2015-11-06 19:54:24 +00:00
typedef struct Time_Record{
time_int buffer;
time_int gap_buffer;
time_int multi_gap_buffer;
time_int rope_buffer;
} Time_Record;
typedef struct Record_Statistics{
Time_Record max, min;
Time_Record expected;
int count;
} Record_Statistics;
Time_Record
operator+(const Time_Record &a, const Time_Record &b){
Time_Record r;
r.buffer = a.buffer + b.buffer;
r.gap_buffer = a.gap_buffer + b.gap_buffer;
r.multi_gap_buffer = a.multi_gap_buffer + b.multi_gap_buffer;
r.rope_buffer = a.rope_buffer + b.rope_buffer;
return r;
}
#define minify(a,b) if ((a)>(b)) (a) = (b)
#define maxify(a,b) if ((a)<(b)) (a) = (b)
void
get_record_statistics(Record_Statistics *stats_out, Time_Record *records, int count){
Record_Statistics stats;
stats.max = records[0];
stats.min = records[0];
stats.expected = records[0];
stats.count = count;
Time_Record *record = records + 1;
for (int i = 1; i < count; ++i, ++record){
stats.expected = stats.expected + *record;
minify(stats.min.buffer, record->buffer);
minify(stats.min.gap_buffer, record->gap_buffer);
minify(stats.min.multi_gap_buffer, record->multi_gap_buffer);
minify(stats.min.rope_buffer, record->rope_buffer);
maxify(stats.max.buffer, record->buffer);
maxify(stats.max.gap_buffer, record->gap_buffer);
maxify(stats.max.multi_gap_buffer, record->multi_gap_buffer);
maxify(stats.max.rope_buffer, record->rope_buffer);
}
stats.expected.buffer /= count;
stats.expected.gap_buffer /= count;
stats.expected.multi_gap_buffer /= count;
stats.expected.rope_buffer /= count;
*stats_out = stats;
}
int test_is_silenced;
void
silence_test(){
test_is_silenced = 1;
}
void
print_record(Time_Record record){
printf("%-16s - %25lluus\n%-16s - %25lluus\n%-16s - %25lluus\n%-16s - %25lluus\n",
"Golden Array", record.buffer,
"Gap Buffer", record.gap_buffer,
"Multi-Gap Buffer", record.multi_gap_buffer,
"Rope", record.rope_buffer);
}
void
print_statistics(Time_Record *records, int count, Record_Statistics *stats_out){
Record_Statistics stats;
get_record_statistics(&stats, records, count);
if (!test_is_silenced){
printf("samples: %d\n", count);
printf("---averages---\n");
print_record(stats.expected);
printf("---max---\n");
print_record(stats.max);
printf("---min---\n");
print_record(stats.min);
}
if (stats_out) *stats_out = stats;
}
typedef struct Buffer_Set{
Buffer buffer;
Gap_Buffer gap_buffer;
Multi_Gap_Buffer multi_gap_buffer;
Rope_Buffer rope_buffer;
} Buffer_Set;
2015-11-08 21:00:25 +00:00
#define Buffer_Type Buffer
#include "4coder_test_abstract.cpp"
#undef Buffer_Type
#define Buffer_Type Gap_Buffer
#include "4coder_test_abstract.cpp"
#undef Buffer_Type
#define Buffer_Type Multi_Gap_Buffer
#include "4coder_test_abstract.cpp"
#undef Buffer_Type
#define Buffer_Type Rope_Buffer
#include "4coder_test_abstract.cpp"
#undef Buffer_Type
2015-11-06 19:54:24 +00:00
#define print_name() printf("%s:\n", __FUNCTION__)
void
initialization_test(Buffer_Set *set, File_Data file, int test_repitions,
void *scratch, int scratch_size, Record_Statistics *stats_out){
time_int tstart, tend;
Time_Record *init_time = (Time_Record*)scratch;
scratch = init_time + test_repitions;
assert_4tech(test_repitions*sizeof(*init_time) < scratch_size);
scratch_size -= test_repitions*sizeof(*init_time);
for (int i = 0; i < test_repitions; ++i){
tstart = get_time();
2015-11-08 21:00:25 +00:00
init_buffer(&set->buffer, file, scratch, scratch_size);
2015-11-06 19:54:24 +00:00
tend = get_time();
init_time[i].buffer = tend - tstart;
tstart = get_time();
2015-11-08 21:00:25 +00:00
init_buffer(&set->gap_buffer, file, scratch, scratch_size);
2015-11-06 19:54:24 +00:00
tend = get_time();
init_time[i].gap_buffer = tend - tstart;
tstart = get_time();
2015-11-08 21:00:25 +00:00
init_buffer(&set->multi_gap_buffer, file, scratch, scratch_size);
2015-11-06 19:54:24 +00:00
tend = get_time();
init_time[i].multi_gap_buffer = tend - tstart;
tstart = get_time();
2015-11-08 21:00:25 +00:00
init_buffer(&set->rope_buffer, file, scratch, scratch_size);
2015-11-06 19:54:24 +00:00
tend = get_time();
init_time[i].rope_buffer = tend - tstart;
if (i+1 != test_repitions){
free(set->buffer.data);
free(set->gap_buffer.data);
for (int j = 0; j < set->multi_gap_buffer.chunk_alloced; ++j){
free(set->multi_gap_buffer.gaps[j].data);
}
free(set->multi_gap_buffer.gaps);
free(set->rope_buffer.data);
free(set->rope_buffer.nodes);
}
}
if (!test_is_silenced) print_name();
print_statistics(init_time, test_repitions, stats_out);
if (!test_is_silenced) printf("\n");
test_is_silenced = 0;
}
void
2015-11-08 21:00:25 +00:00
measure_starts_widths_test(Buffer_Set *set, int test_repitions,
void *scratch, int scratch_size, Record_Statistics *stats_out,
float *font_widths){
2015-11-06 19:54:24 +00:00
time_int tstart, tend;
Time_Record *measure_time = (Time_Record*)scratch;
scratch = measure_time + test_repitions;
assert_4tech(test_repitions*sizeof(*measure_time) < scratch_size);
scratch_size -= test_repitions*sizeof(*measure_time);
for (int i = 0; i < test_repitions; ++i){
tstart = get_time();
2015-11-08 21:00:25 +00:00
measure_starts_widths(&set->buffer, font_widths);
2015-11-06 19:54:24 +00:00
tend = get_time();
measure_time[i].buffer = tend - tstart;
tstart = get_time();
2015-11-08 21:00:25 +00:00
measure_starts_widths(&set->gap_buffer, font_widths);
2015-11-06 19:54:24 +00:00
tend = get_time();
measure_time[i].gap_buffer = tend - tstart;
tstart = get_time();
2015-11-08 21:00:25 +00:00
measure_starts_widths(&set->multi_gap_buffer, font_widths);
2015-11-06 19:54:24 +00:00
tend = get_time();
measure_time[i].multi_gap_buffer = tend - tstart;
tstart = get_time();
2015-11-08 21:00:25 +00:00
measure_starts_widths(&set->rope_buffer, font_widths);
2015-11-06 19:54:24 +00:00
tend = get_time();
measure_time[i].rope_buffer = tend - tstart;
if (i+1 != test_repitions){
free(set->buffer.line_starts);
free(set->gap_buffer.line_starts);
free(set->multi_gap_buffer.line_starts);
free(set->rope_buffer.line_starts);
2015-11-08 21:00:25 +00:00
2015-11-06 19:54:24 +00:00
free(set->buffer.line_widths);
free(set->gap_buffer.line_widths);
free(set->multi_gap_buffer.line_widths);
free(set->rope_buffer.line_widths);
}
}
if (!test_is_silenced) print_name();
print_statistics(measure_time, test_repitions, stats_out);
if (!test_is_silenced) printf("\n");
test_is_silenced = 0;
}
int
page_compare(char *page_1, char *page_2, int page_size){
int result = 1;
for (int i = 0; i < page_size; ++i){
hard_assert_4tech(page_1[i] == page_2[i]);
}
return result;
}
void
stream_check_test(Buffer_Set *buffers, void *scratch, int scratch_size){
int i, page_size, size;
size = buffer_size(&buffers->buffer);
{
int size2;
size2 = buffer_size(&buffers->gap_buffer);
hard_assert_4tech(size == size2);
size2 = buffer_size(&buffers->multi_gap_buffer);
hard_assert_4tech(size == size2);
size2 = buffer_size(&buffers->rope_buffer);
hard_assert_4tech(size == size2);
}
page_size = 1 << 10;
char *page_1 = (char*)scratch;
char *page_2 = page_1 + page_size;
scratch_size -= page_size*2;
hard_assert_4tech(scratch_size > 0);
for (i = 0; i < size; i += page_size){
int end = i + page_size;
if (end > size) end = size;
buffer_stringify(&buffers->buffer, i, end, page_1);
buffer_stringify(&buffers->gap_buffer, i, end, page_2);
page_compare(page_1, page_2, page_size);
buffer_stringify(&buffers->multi_gap_buffer, i, end, page_2);
page_compare(page_1, page_2, page_size);
buffer_stringify(&buffers->rope_buffer, i, end, page_2);
page_compare(page_1, page_2, page_size);
}
2015-11-08 21:00:25 +00:00
for (i = size-1; i > 0; i -= page_size){
int end = i - page_size;
if (end < 0) end = 0;
buffer_backify(&buffers->buffer, i, end, page_1);
buffer_backify(&buffers->gap_buffer, i, end, page_2);
page_compare(page_1, page_2, page_size);
buffer_backify(&buffers->multi_gap_buffer, i, end, page_2);
page_compare(page_1, page_2, page_size);
buffer_backify(&buffers->rope_buffer, i, end, page_2);
page_compare(page_1, page_2, page_size);
}
2015-11-06 19:54:24 +00:00
}
2015-11-08 21:00:25 +00:00
int main(int argc, char **argv){
2015-11-06 19:54:24 +00:00
Buffer_Set buffers;
File_Data file;
2015-11-08 21:00:25 +00:00
float *widths_data;
2015-11-06 19:54:24 +00:00
void *scratch;
int scratch_size;
2015-11-08 21:00:25 +00:00
if (argc < 2){
printf("usage: buffer_test <filename>\n");
exit(1);
}
2015-11-06 19:54:24 +00:00
setup();
scratch_size = 1 << 20;
scratch = malloc(scratch_size);
2015-11-08 21:00:25 +00:00
file = get_file(argv[1]);
widths_data = get_font_data("LiberationSans-Regular.ttf");
2015-11-06 19:54:24 +00:00
2015-11-08 21:00:25 +00:00
Record_Statistics init_rec, starts_widths_rec;
2015-11-06 19:54:24 +00:00
initialization_test(&buffers, file, 100, scratch, scratch_size, &init_rec);
stream_check_test(&buffers, scratch, scratch_size);
2015-11-08 21:00:25 +00:00
measure_starts_widths_test(&buffers, 100, scratch, scratch_size, &starts_widths_rec, widths_data);
2015-11-06 19:54:24 +00:00
Time_Record expected_file_open;
2015-11-08 21:00:25 +00:00
expected_file_open = init_rec.expected + starts_widths_rec.expected;
2015-11-06 19:54:24 +00:00
printf("average file open:\n");
print_record(expected_file_open);
return(0);
}
// BOTTOM