#include #include #include #include #include #include #include #include extern "C" { #include #include #include #include } struct Sample { uint64_t open_ns; uint64_t read_ns; uint64_t knn_ns; uint64_t filter_ns; uint64_t canonicalize_ns; uint64_t serialize_ns; uint64_t sha_ns; uint64_t publish_ns; uint64_t database_ns; uint64_t total_ns; }; static uint64_t median(std::vector values) { std::sort(values.begin(), values.end()); return values[values.size() / 2]; } static void image_path(const unsigned char hash[32], char path[4096]) { static const char digits[] = "0123456789abcdef"; char hex[65]; for (size_t i = 0; i < 32; ++i) { hex[2 * i] = digits[hash[i] >> 4]; hex[2 * i + 1] = digits[hash[i] & 15U]; } hex[64] = '\0'; (void)snprintf(path, 4096, "assets/images/%c%c/%s", hex[0], hex[1], hex); } static bool register_image(Lardon3DProjectDb *db, uint64_t scanset_id, unsigned char seed, Lardon3DProjectDbImage *image) { unsigned char hash[32]; memset(hash, seed, sizeof(hash)); char path[4096]; image_path(hash, path); Lardon3DProjectDbImageRegisterStatus status; return lardon3d_project_db_register_image(db, scanset_id, hash, path, 1, "fixture.bin", "/synthetic/fixture.bin", 0, seed, &status, image) == LARDON3D_PROJECT_DB_OK; } static void fill_features(Lardon3DExtractedFeatures *features, uint32_t count, Lardon3DFeatureDescriptorType type, unsigned char salt) { (void)salt; memset(features, 0, sizeof(*features)); features->image_width = 4096; features->image_height = 4096; features->feature_count = count; features->keypoints = static_cast( calloc(count == 0 ? 1 : count, sizeof(*features->keypoints))); for (uint32_t i = 0; i < count; ++i) { features->keypoints[i].x = (float)(i % 4096U); features->keypoints[i].y = (float)(i % 4096U); features->keypoints[i].size = 1.0F; } size_t scalar = type == LARDON3D_FEATURE_DESCRIPTOR_U8 ? 1 : sizeof(float); features->descriptor_bytes = (size_t)count * 128 * scalar; features->descriptors = static_cast(malloc(features->descriptor_bytes)); if (type == LARDON3D_FEATURE_DESCRIPTOR_U8) { features->descriptor_bytes = (size_t)count * 32; features->descriptors = static_cast( realloc(features->descriptors, features->descriptor_bytes)); for (uint32_t row = 0; row < count; ++row) { uint32_t value = row + 1U; for (uint32_t column = 0; column < 32; ++column) { value ^= value << 13; value ^= value >> 17; value ^= value << 5; features->descriptors[(size_t)row * 32 + column] = (unsigned char)value; } } } else { float *values = reinterpret_cast(features->descriptors); size_t value_count = (size_t)count * 128; for (size_t i = 0; i < value_count; ++i) values[i] = (float)(((i / 128U) * 37U + (i % 128U) * 11U) % 65521U) / 65521.0F; } } static bool publish(Lardon3DAppState *state, uint64_t image_id, const char *kind, Lardon3DFeatureDescriptorType type, uint32_t count, unsigned char salt, Lardon3DProjectDbFeatureSet *set) { Lardon3DExtractedFeatures features; fill_features(&features, count, type, salt); unsigned char fingerprint[32]; memset(fingerprint, salt, sizeof(fingerprint)); uint32_t dimension = type == LARDON3D_FEATURE_DESCRIPTOR_U8 ? 32 : 128; Lardon3DFeatureStoreResult result = lardon3d_feature_store_publish_v2( state, image_id, 0, kind, 1, fingerprint, type, dimension, 0, &features, set); free(features.keypoints); free(features.descriptors); return result == LARDON3D_FEATURE_STORE_OK; } static bool benchmark_case(const char *kind, Lardon3DMatcherKind matcher_kind, Lardon3DFeatureDescriptorType type, uint32_t count) { char root_template[] = "/tmp/lardon3d-matcher-bench-XXXXXX"; char *root = mkdtemp(root_template); if (!root) return false; std::string db_path = std::string(root) + "/project.db"; Lardon3DProjectDb *db = nullptr; char error[256]; if (lardon3d_project_db_open(db_path.c_str(), &db, error) != LARDON3D_PROJECT_DB_OK) return false; Lardon3DProjectDbScanSet scanset; Lardon3DProjectDbImage image_a, image_b; Lardon3DAppState state; lardon3d_app_state_init(&state); state.project_loaded = true; state.project_db = db; (void)snprintf(state.project_path, sizeof(state.project_path), "%s", root); bool ok = lardon3d_project_db_create_scanset(db, "benchmark", &scanset) == LARDON3D_PROJECT_DB_OK && register_image(db, scanset.scanset_id, 1, &image_a) && register_image(db, scanset.scanset_id, 2, &image_b); Lardon3DProjectDbFeatureSet set_a, set_b; ok = ok && publish(&state, image_a.image_id, kind, type, count, 3, &set_a) && publish(&state, image_b.image_id, kind, type, count, 4, &set_b); Lardon3DProjectDbCandidatePair pair; ok = ok && lardon3d_project_db_create_candidate_pair( db, image_a.image_id, image_b.image_id, 1, &pair) == LARDON3D_PROJECT_DB_OK; std::vector samples; Lardon3DMatcherParams params = {matcher_kind, lardon3d_matcher_default_ratio(matcher_kind)}; for (int repetition = 0; ok && repetition < 3; ++repetition) { Lardon3DMatcherStats stats; params.ratio_threshold = lardon3d_matcher_default_ratio(matcher_kind) - (float)repetition * 0.01F; Lardon3DProjectDbMatchResult result; ok = lardon3d_matcher_match_and_publish_profiled( root, db, &pair, &set_a, &set_b, ¶ms, &result, &stats) == LARDON3D_MATCHER_OK; samples.push_back({stats.feature_open_ns, stats.descriptor_read_ns, stats.knn_ns, stats.filter_ns, stats.canonicalize_ns, stats.serialize_ns, stats.sha256_ns, stats.publication_ns, stats.database_ns, stats.total_ns}); } if (ok) { std::vector values; auto field = [&](auto getter) { values.clear(); for (const Sample &sample : samples) values.push_back(getter(sample)); return (double)median(values) / 1000000.0; }; printf("%s,%u,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f\n", kind, count, field([](const Sample &s) { return s.open_ns; }), field([](const Sample &s) { return s.read_ns; }), field([](const Sample &s) { return s.knn_ns; }), field([](const Sample &s) { return s.filter_ns; }), field([](const Sample &s) { return s.canonicalize_ns; }), field([](const Sample &s) { return s.serialize_ns; }), field([](const Sample &s) { return s.sha_ns; }), field([](const Sample &s) { return s.publish_ns; }), field([](const Sample &s) { return s.database_ns; }), field([](const Sample &s) { return s.total_ns; })); std::vector reuse_totals; for (int repetition = 0; repetition < 5; ++repetition) { Lardon3DMatcherStats reuse_stats; Lardon3DProjectDbMatchResult reused; ok = ok && lardon3d_matcher_match_and_publish_profiled( root, db, &pair, &set_a, &set_b, ¶ms, &reused, &reuse_stats) == LARDON3D_MATCHER_OK; reuse_totals.push_back(reuse_stats.total_ns); } printf("%s-reuse,%u,0,0,0,0,0,0,0,0,0,%.3f\n", kind, count, (double)median(reuse_totals) / 1000000.0); } lardon3d_project_db_close(db); std::filesystem::remove_all(root); return ok; } int main(void) { printf("kind,count,open_ms,read_ms,knn_ms,filter_ms,canonicalize_ms,serialize_ms," "sha_ms,publish_ms,db_ms,total_ms\n"); const uint32_t sizes[] = {64, 256, 1024, 4096, 8192}; for (uint32_t count : sizes) { if (!benchmark_case("orb", LARDON3D_MATCHER_ORB_BF, LARDON3D_FEATURE_DESCRIPTOR_U8, count) || !benchmark_case("sift", LARDON3D_MATCHER_SIFT_BF, LARDON3D_FEATURE_DESCRIPTOR_F32, count) || !benchmark_case("rootsift", LARDON3D_MATCHER_ROOTSIFT_BF, LARDON3D_FEATURE_DESCRIPTOR_F32, count)) return EXIT_FAILURE; } return EXIT_SUCCESS; }