// Initially generated by AI (Google Gemini) #include #include #include #include #include #include #include #define HM_IMPLEMENTATION #include "hm.h" using namespace std::chrono; typedef time_point time_p; // Configuration const size_t NUM_ELEMENTS = 1000000; time_p now() { return high_resolution_clock::now(); } double time_diff_ms(time_p end, time_p start) { return duration(end - start).count(); } void print_div() { std::cout << "---------------------------------------------------------" << std::endl; } void print_head() { std::cout << std::left << std::setw(10) << "Operation" << std::right << std::setw(22) << "std::unordered_map" << std::right << std::setw(13) << "hm.h" << std::right << std::setw(11) << "Ratio" << std::endl; } void print_row(const std::string& label, double hm_ms, double std_ms) { std::cout << std::left << std::setw(10) << label << std::right << std::setw(19) << std::fixed << std::setprecision(2) << std_ms << " ms" << std::right << std::setw(10) << std::fixed << std::setprecision(2) << hm_ms << " ms" << std::right << std::setw(10) << (hm_ms / std_ms) << "x" << std::endl; } // Test with string key type void test_str() { std::vector key_pool; std::vector value_pool; // Generate random strings to ensure we aren't just hashing "1", "2", "3" for (size_t i = 0; i < NUM_ELEMENTS; ++i) { key_pool.push_back("key_prefix_" + std::to_string(i) + "_suffix_" + std::to_string(rand() % 1000)); value_pool.push_back("value_" + std::to_string(i)); } // --- Custom implementation --- // Using 0 for sizes tells the library to treat them as NULL-terminated strings HashMap hm = hm_create(0, 0); // Insertion auto start = now(); for (size_t i = 0; i < NUM_ELEMENTS; ++i) { hm_put(&hm, key_pool[i].c_str(), value_pool[i].c_str()); } auto end = now(); double custom_put = time_diff_ms(end, start); if (hm_count(&hm) != NUM_ELEMENTS) { std::cerr << "Error in custom insertion!" << std::endl; return; } // Lookup start = now(); for (size_t i = 0; i < NUM_ELEMENTS; ++i) { const char* val = (const char*)hm_get(&hm, key_pool[i].c_str()); if (!val || strcmp(val, value_pool[i].c_str()) != 0) { std::cerr << "Error in custom lookup!" << std::endl; return; } } end = now(); double custom_get = time_diff_ms(end, start); // Deletion start = now(); for (size_t i = 0; i < NUM_ELEMENTS; ++i) { hm_remove(&hm, key_pool[i].c_str()); } end = now(); double custom_del = time_diff_ms(end, start); hm_free(&hm); // --- Benchmark std::unordered_map --- std::unordered_map std_map; // Insertion start = now(); for (size_t i = 0; i < NUM_ELEMENTS; ++i) { std_map[key_pool[i]] = value_pool[i]; } end = now(); double std_put = time_diff_ms(end, start); // Lookup start = now(); for (size_t i = 0; i < NUM_ELEMENTS; ++i) { auto it = std_map.find(key_pool[i]); if (it == std_map.end() || it->second != value_pool[i]) { std::cerr << "Error in std lookup!" << std::endl; } } end = now(); double std_get = time_diff_ms(end, start); // Deletion start = now(); for (size_t i = 0; i < NUM_ELEMENTS; ++i) { std_map.erase(key_pool[i]); } end = now(); double std_del = time_diff_ms(end, start); // Output Results print_head(); print_div(); print_row("Insert", custom_put, std_put); print_row("Lookup", custom_get, std_get); print_row("Delete", custom_del, std_del); print_div(); } // Test with integer key type uint64_t hash_int(const void *buffer, size_t size) { (void)size; return *reinterpret_cast(buffer); } void test_int() { // Prepare test data (random integers) to avoid string allocation overhead in the loops std::vector keys(NUM_ELEMENTS); std::vector values(NUM_ELEMENTS); for (size_t i = 0; i < NUM_ELEMENTS; ++i) { keys[i] = static_cast(i); values[i] = static_cast(i * 2); } // --- Custom implementation --- HashMap hm = hm_create_ex(sizeof(int), sizeof(int), hash_int); // Insertion auto start = now(); for (size_t i = 0; i < NUM_ELEMENTS; ++i) { hm_put(&hm, &keys[i], &values[i]); } auto end = now(); double custom_put = time_diff_ms(end, start); // Lookup start = now(); for (size_t i = 0; i < NUM_ELEMENTS; ++i) { int* val = (int*)hm_get(&hm, &keys[i]); if (!val || *val != values[i]) { std::cerr << "Error in custom lookup!" << std::endl; return; } } end = now(); double custom_get = time_diff_ms(end, start); // Deletion start = now(); for (size_t i = 0; i < NUM_ELEMENTS; ++i) { hm_remove(&hm, &keys[i]); } end = now(); double custom_del = time_diff_ms(end, start); hm_free(&hm); // --- Benchmark std::unordered_map --- std::unordered_map std_map; // Insertion start = now(); for (size_t i = 0; i < NUM_ELEMENTS; ++i) { std_map[keys[i]] = values[i]; } end = now(); double std_put = time_diff_ms(end, start); // Lookup start = now(); for (size_t i = 0; i < NUM_ELEMENTS; ++i) { auto it = std_map.find(keys[i]); if (it == std_map.end() || it->second != values[i]) { std::cerr << "Error in std lookup!" << std::endl; } } end = now(); double std_get = time_diff_ms(end, start); // Deletion start = now(); for (size_t i = 0; i < NUM_ELEMENTS; ++i) { std_map.erase(keys[i]); } end = now(); double std_del = time_diff_ms(end, start); // Output Results print_head(); print_div(); print_row("Insert", custom_put, std_put); print_row("Lookup", custom_get, std_get); print_row("Delete", custom_del, std_del); print_div(); } int main() { std::cout << "Benchmarking " << NUM_ELEMENTS << " (integer keys)" << std::endl << std::endl; test_int(); std::cout << std::endl; std::cout << "Benchmarking " << NUM_ELEMENTS << " (string keys)" << std::endl << std::endl; test_str(); return 0; }