|
// Like in the c++ version, we |
|
// have a regular hash table |
|
// with a custom hash function |
|
|
|
#include <string.h> |
|
#include <stdlib.h> |
|
#include <stdio.h> |
|
|
|
struct fht_node { |
|
char* key; |
|
void* data; |
|
struct fht_node * next; |
|
}; |
|
|
|
typedef struct fht { |
|
struct fht_node ** nodes; |
|
size_t size; |
|
} FHT; |
|
|
|
|
|
FHT* fht_create() { |
|
FHT *ht = malloc(sizeof(struct fht)); |
|
ht->size = 1 << (sizeof(char)*8); |
|
ht->nodes = calloc(ht->size, sizeof(struct fht_node)); |
|
return ht; |
|
} |
|
|
|
fht_put(FHT* hashtbl, char* key, void* data) { |
|
struct fht_node *node = hashtbl->nodes[key[0]]; |
|
|
|
while(node) { |
|
if(!strcmp(node->key, key)) { |
|
node->data=data; |
|
return 0; |
|
} |
|
node=node->next; |
|
} |
|
|
|
node=malloc(sizeof(struct fht_node)); |
|
node->key= strdup(key); |
|
node->data=data; |
|
node->next=hashtbl->nodes[key[0]]; |
|
hashtbl->nodes[key[0]]=node; |
|
} |
|
|
|
void* fht_get(FHT* hashtbl, char* key) { |
|
struct fht_node *node = hashtbl->nodes[key[0]]; |
|
while (node) { |
|
if (!strcmp(node->key, key)) return node->data; |
|
node = node->next; |
|
} |
|
return NULL; |
|
} |
|
|
|
int main() { |
|
|
|
char* text[19] = { |
|
"The", "quick", "brown", "fox", "jumped", "over", "the", "lazy", "dog", "at", "a", |
|
"restaurant", "near", "the", "lake", "of", "a", "new", "era" |
|
}; |
|
|
|
FHT *hashtbl = fht_create(); |
|
|
|
int textLen = 19; |
|
|
|
int times = 1000000; |
|
int k, *cnt; |
|
|
|
|
|
while (times--) |
|
for (k = 0; k < textLen; ++k) { |
|
cnt = fht_get(hashtbl, text[k]); |
|
if (!cnt) { |
|
cnt = malloc(sizeof(int)); |
|
*cnt = 1; |
|
fht_put(hashtbl, text[k], cnt); |
|
} else { *cnt += 1; } |
|
} |
|
|
|
|
|
for (k = 0; k < hashtbl->size; ++k) { |
|
struct fht_node *n = hashtbl->nodes[k]; |
|
while (n) { |
|
printf("%s %d\n", n->key, *((int *)n->data)); |
|
n = n->next; |
|
} |
|
} |
|
} |
As a common sense of such micro benchmarks, the init (and deinit) time counts. For example, in hosted implementation of C and C++ on x86_64 Linux, typically the C runtime will have an overhead of less than 100k of insns and C++ will have 3M. This still does not indicate that which one is definitely more efficint though: both can have freestanding implementations with less dynamic initialization required (e.g. getting rid of the
iostream
initialization mandated by the standard on a C++ hosted implementation). For typical manner of hosted implementations, if the kernel mode runtime/loader is implemented in C++, processes in userland can even share with pre-initized environments with no overhead at all. That said, why benchmarks of userland programs should rely on such stuff? Because this really makes differences when your benchmark runs sufficiently fast.OTOH, there is nothing comparable on Lua or JS. There are no specs on what exactly mean to implement freestanding environments for them. The runtime initialization do incur some non-deterministic overhead which typically in turn rely on the native init overhead. Then for userland programs, you will compare "impure" implementations: Lua+C vs. JS+C++ instead of Lua vs. JS, for sure. This will not change before you can implement a hosted environment runtime (e.g. the OS kernel and loader) in your language being compared. Well, in this sense, Linux C++ is actually implemented in C (Linux kernel + e.g. glibc ld.so) + C++, but at least there are standard-conforming ways to replace non-C++ parts as long as you can replace Linux and libc, in reality. And where is the native loader implemented in JS/Lua?
I'm a bit surprised that nobody has pointed out this...