It currently beats std::map and std::hash_map by >10x.pull/13171/head
parent
0aedd1825f
commit
f4c00fc979
6 changed files with 314 additions and 39 deletions
@ -1,23 +1,18 @@ |
||||
|
||||
.PHONY: all clean |
||||
CC=gcc
|
||||
CFLAGS=-std=c99 -O3 -Wall -Wextra -pedantic
|
||||
CFLAGS=-std=c99
|
||||
CPPFLAGS=-O3 -Wall -Wextra -pedantic -g
|
||||
OBJ=upb_parse.o upb_table.o upb_struct.o descriptor.o
|
||||
all: $(OBJ) |
||||
all: $(OBJ) test_table |
||||
clean: |
||||
rm -f $(OBJ) tests
|
||||
|
||||
upb_parse.o: upb_parse.c upb_parse.h |
||||
$(CC) $(CFLAGS) -o upb_parse.o -c upb_parse.c
|
||||
|
||||
upb_table.o: upb_table.c upb_table.h |
||||
$(CC) $(CFLAGS) -o upb_table.o -c upb_table.c
|
||||
|
||||
upb_struct.o: upb_struct.c upb_struct.h |
||||
$(CC) $(CFLAGS) -o upb_struct.o -c upb_struct.c
|
||||
|
||||
descriptor.o: descriptor.c descriptor.h |
||||
$(CC) $(CFLAGS) -o descriptor.o -c descriptor.c
|
||||
test_table: test_table.cc upb_table.o |
||||
|
||||
tests: tests.c upb_parse.c upb_parse.h |
||||
$(CC) $(CFLAGS) -o tests tests.c
|
||||
|
@ -0,0 +1,192 @@ |
||||
|
||||
#include "upb_table.h" |
||||
#include "test_util.h" |
||||
#include <assert.h> |
||||
#include <map> |
||||
#include <ext/hash_map> |
||||
#include <sys/resource.h> |
||||
|
||||
struct table_entry { |
||||
struct upb_inttable_entry e; |
||||
int32_t value; /* key+2 */ |
||||
}; |
||||
|
||||
double get_usertime() |
||||
{ |
||||
struct rusage usage; |
||||
getrusage(RUSAGE_SELF, &usage); |
||||
return usage.ru_utime.tv_sec + (usage.ru_utime.tv_usec/1000000.0); |
||||
} |
||||
|
||||
static uint32_t max(uint32_t a, uint32_t b) { return a > b ? a : b; } |
||||
|
||||
/* num_entries must be a power of 2. */ |
||||
void test(int32_t *keys, size_t num_entries) |
||||
{ |
||||
/* Initialize structures. */ |
||||
struct table_entry *entries = new table_entry[num_entries]; |
||||
struct upb_inttable table; |
||||
int32_t largest_key = 0; |
||||
std::map<int32_t, int32_t> m; |
||||
__gnu_cxx::hash_map<int32_t, int32_t> hm; |
||||
for(size_t i = 0; i < num_entries; i++) { |
||||
int32_t key = keys[i]; |
||||
largest_key = max(largest_key, key); |
||||
entries[i].e.key = key; |
||||
entries[i].value = key*2; |
||||
m[key] = key*2; |
||||
hm[key] = key*2; |
||||
} |
||||
upb_inttable_init(&table, entries, num_entries, sizeof(struct table_entry)); |
||||
printf("size: %lu\n", sizeof(struct table_entry)); |
||||
delete[] entries; |
||||
|
||||
/* Test correctness. */ |
||||
for(int32_t i = 0; i < largest_key; i++) { |
||||
struct table_entry *e = (struct table_entry*)upb_inttable_lookup( |
||||
&table, i, sizeof(struct table_entry)); |
||||
if(m.find(i) != m.end()) { /* Assume map implementation is correct. */ |
||||
assert(e); |
||||
assert(e->e.key == i); |
||||
assert(e->value == i*2); |
||||
assert(m[i] == i*2); |
||||
assert(hm[i] == i*2); |
||||
} else { |
||||
assert(e == NULL); |
||||
} |
||||
} |
||||
|
||||
/* Test performance. We only test lookups for keys that are known to exist. */ |
||||
uintptr_t x = 0; |
||||
const unsigned int iterations = 0xFFFFFF; |
||||
const int32_t mask = num_entries - 1; |
||||
printf("Measuring sequential loop overhead..."); |
||||
fflush(stdout); |
||||
double before = get_usertime(); |
||||
for(unsigned int i = 0; i < iterations; i++) { |
||||
int32_t key = keys[i & mask]; |
||||
x += key; |
||||
} |
||||
double seq_overhead = get_usertime() - before; |
||||
printf("%0.3f seconds for %d iterations\n", seq_overhead, iterations); |
||||
|
||||
printf("Measuring random loop overhead..."); |
||||
rand(); |
||||
fflush(stdout); |
||||
before = get_usertime(); |
||||
for(unsigned int i = 0; i < iterations; i++) { |
||||
int32_t key = keys[rand() & mask]; |
||||
x += key; |
||||
} |
||||
double rand_overhead = get_usertime() - before; |
||||
printf("%0.3f seconds for %d iterations\n", rand_overhead, iterations); |
||||
|
||||
printf("upb_table(seq): "); |
||||
fflush(stdout); |
||||
before = get_usertime(); |
||||
for(unsigned int i = 0; i < iterations; i++) { |
||||
int32_t key = keys[i & mask]; |
||||
struct table_entry *e = (struct table_entry*)upb_inttable_lookup( |
||||
&table, key, sizeof(struct table_entry)); |
||||
x += (uintptr_t)e; |
||||
} |
||||
double total = get_usertime() - before; |
||||
double without_overhead = total - seq_overhead; |
||||
printf("%0.3f seconds (%0.3f - %0.3f overhead) for %d iterations. %s/s\n", without_overhead, total, seq_overhead, iterations, eng(iterations/without_overhead, 3, false)); |
||||
|
||||
printf("upb_table(rand): "); |
||||
fflush(stdout); |
||||
before = get_usertime(); |
||||
for(unsigned int i = 0; i < iterations; i++) { |
||||
int32_t key = keys[rand() & mask]; |
||||
struct table_entry *e = (struct table_entry*)upb_inttable_lookup( |
||||
&table, key, sizeof(struct table_entry)); |
||||
x += (uintptr_t)e; |
||||
} |
||||
total = get_usertime() - before; |
||||
without_overhead = total - rand_overhead; |
||||
printf("%0.3f seconds (%0.3f - %0.3f overhead) for %d iterations. %s/s\n", without_overhead, total, rand_overhead, iterations, eng(iterations/without_overhead, 3, false)); |
||||
|
||||
printf("map(seq): "); |
||||
fflush(stdout); |
||||
before = get_usertime(); |
||||
for(unsigned int i = 0; i < iterations; i++) { |
||||
int32_t key = keys[i & mask]; |
||||
x += m[key]; |
||||
} |
||||
total = get_usertime() - before; |
||||
without_overhead = total - seq_overhead; |
||||
printf("%0.3f seconds (%0.3f - %0.3f overhead) for %d iterations. %s/s\n", without_overhead, total, seq_overhead, iterations, eng(iterations/without_overhead, 3, false)); |
||||
|
||||
printf("map(rand): "); |
||||
fflush(stdout); |
||||
before = get_usertime(); |
||||
for(unsigned int i = 0; i < iterations; i++) { |
||||
int32_t key = keys[rand() & mask]; |
||||
x += m[key]; |
||||
} |
||||
total = get_usertime() - before; |
||||
without_overhead = total - rand_overhead; |
||||
printf("%0.3f seconds (%0.3f - %0.3f overhead) for %d iterations. %s/s\n", without_overhead, total, rand_overhead, iterations, eng(iterations/without_overhead, 3, false)); |
||||
|
||||
printf("hash_map(seq): "); |
||||
fflush(stdout); |
||||
before = get_usertime(); |
||||
for(unsigned int i = 0; i < iterations; i++) { |
||||
int32_t key = keys[i & mask]; |
||||
x += hm[key]; |
||||
} |
||||
total = get_usertime() - before; |
||||
without_overhead = total - seq_overhead; |
||||
printf("%0.3f seconds (%0.3f - %0.3f overhead) for %d iterations. %s/s\n", without_overhead, total, seq_overhead, iterations, eng(iterations/without_overhead, 3, false)); |
||||
|
||||
printf("hash_map(rand): "); |
||||
fflush(stdout); |
||||
before = get_usertime(); |
||||
for(unsigned int i = 0; i < iterations; i++) { |
||||
int32_t key = keys[rand() & mask]; |
||||
x += hm[key]; |
||||
} |
||||
total = get_usertime() - before; |
||||
without_overhead = total - rand_overhead; |
||||
printf("%0.3f seconds (%0.3f - %0.3f overhead) for %d iterations. %s/s\n\n", without_overhead, total, rand_overhead, iterations, eng(iterations/without_overhead, 3, false)); |
||||
upb_inttable_free(&table); |
||||
} |
||||
|
||||
int32_t *get_contiguous_keys(int32_t num) |
||||
{ |
||||
int32_t *buf = new int32_t[num]; |
||||
for(int32_t i = 0; i < num; i++) |
||||
buf[i] = i; |
||||
return buf; |
||||
} |
||||
|
||||
int main() |
||||
{ |
||||
int32_t *keys1 = get_contiguous_keys(8); |
||||
printf("Contiguous 1-8 ====\n"); |
||||
test(keys1, 8); |
||||
delete[] keys1; |
||||
|
||||
int32_t *keys2 = get_contiguous_keys(64); |
||||
printf("Contiguous 1-64 ====\n"); |
||||
test(keys2, 64); |
||||
delete[] keys2; |
||||
|
||||
int32_t *keys3 = get_contiguous_keys(512); |
||||
printf("Contiguous 1-512 ====\n"); |
||||
test(keys3, 512); |
||||
delete[] keys3; |
||||
|
||||
int32_t *keys4 = new int32_t[64]; |
||||
for(int32_t i = 0; i < 64; i++) { |
||||
if(i < 32) |
||||
keys4[i] = i; |
||||
else |
||||
keys4[i] = 10100+i; |
||||
} |
||||
printf("1-32 and 10033-10064 ====\n"); |
||||
test(keys4, 64); |
||||
delete[] keys4; |
||||
} |
||||
|
@ -0,0 +1,50 @@ |
||||
#define PREFIX_START (-24) |
||||
/* Smallest power of then for which there is a prefix defined.
|
||||
If the set of prefixes will be extended, change this constant |
||||
and update the table "prefix". */ |
||||
|
||||
#include <stdio.h> |
||||
#include <math.h> |
||||
|
||||
static char *eng(double value, int digits, int numeric) |
||||
{ |
||||
static char *prefix[] = { |
||||
"y", "z", "a", "f", "p", "n", "u", "m", "", |
||||
"k", "M", "G", "T", "P", "E", "Z", "Y" |
||||
}; |
||||
#define PREFIX_END (PREFIX_START+\ |
||||
(int)((sizeof(prefix)/sizeof(char *)-1)*3)) |
||||
|
||||
int expof10; |
||||
static char result[100]; |
||||
char *res = result; |
||||
|
||||
if (value < 0.) |
||||
{ |
||||
*res++ = '-'; |
||||
value = -value; |
||||
} |
||||
|
||||
expof10 = (int) log10(value); |
||||
if(expof10 > 0) |
||||
expof10 = (expof10/3)*3; |
||||
else |
||||
expof10 = (-expof10+3)/3*(-3);
|
||||
|
||||
value *= pow(10,-expof10); |
||||
|
||||
if (value >= 1000.) |
||||
{ value /= 1000.0; expof10 += 3; } |
||||
else if(value >= 100.0) |
||||
digits -= 2; |
||||
else if(value >= 10.0) |
||||
digits -= 1; |
||||
|
||||
if(numeric || (expof10 < PREFIX_START) ||
|
||||
(expof10 > PREFIX_END)) |
||||
sprintf(res, "%.*fe%d", digits-1, value, expof10);
|
||||
else |
||||
sprintf(res, "%.*f %s", digits-1, value,
|
||||
prefix[(expof10-PREFIX_START)/3]); |
||||
return result; |
||||
} |
Loading…
Reference in new issue