@@ -57,17 +57,22 @@ inline const std::vector<float>& get_test_data() {
5757 return test_data;
5858}
5959
60- // Get current RSS (Resident Set Size) memory usage in bytes
6160size_t get_current_rss () {
6261 std::ifstream statm (" /proc/self/statm" );
6362 if (!statm.is_open ()) {
6463 return 0 ;
6564 }
6665 size_t vsize, rss;
6766 statm >> vsize >> rss;
68- return rss * sysconf (_SC_PAGESIZE);
67+ size_t page_size = sysconf (_SC_PAGESIZE);
68+ return rss * page_size;
6969}
7070
71+ struct UsageInfo {
72+ size_t file_size;
73+ size_t rss_increase;
74+ };
75+
7176} // namespace
7277
7378// Template function to write and read an index
@@ -130,6 +135,78 @@ void write_and_read_index(
130135 svs::runtime::v0::DynamicVamanaIndex::destroy (loaded);
131136}
132137
138+ // Helper that writes and reads and index of requested size
139+ // Reports memory usage
140+ UsageInfo run_save_and_load_test (const size_t target_mibytes) {
141+ // Generate requested MiB of test data
142+ constexpr size_t mem_test_d = 128 ;
143+ const size_t target_bytes = target_mibytes * 1024 * 1024 ;
144+ const size_t mem_test_n = target_bytes / (mem_test_d * sizeof (float ));
145+
146+ svs_test::prepare_temp_directory ();
147+ auto temp_dir = svs_test::temp_directory ();
148+ auto filename = temp_dir / " memory_test_index.bin" ;
149+
150+ {
151+ // Build Vamana FP32 index, scoped for memory cleanup
152+ auto large_test_data = create_test_data (mem_test_n, mem_test_d, 456 );
153+
154+ // Add data to index
155+ std::vector<size_t > labels (mem_test_n);
156+ std::iota (labels.begin (), labels.end (), 0 );
157+
158+ size_t mem_before = get_current_rss ();
159+ svs::runtime::v0::DynamicVamanaIndex* index = nullptr ;
160+ svs::runtime::v0::VamanaIndex::BuildParams build_params{64 };
161+ svs::runtime::v0::Status status = svs::runtime::v0::DynamicVamanaIndex::build (
162+ &index,
163+ mem_test_d,
164+ svs::runtime::v0::MetricType::L2,
165+ svs::runtime::v0::StorageKind::FP32,
166+ build_params
167+ );
168+ CATCH_REQUIRE (status.ok ());
169+ CATCH_REQUIRE (index != nullptr );
170+ status = index->add (mem_test_n, labels.data (), large_test_data.data ());
171+ CATCH_REQUIRE (status.ok ());
172+
173+ std::ofstream out (filename, std::ios::binary);
174+ CATCH_REQUIRE (out.is_open ());
175+ status = index->save (out);
176+ CATCH_REQUIRE (status.ok ());
177+ out.close ();
178+
179+ svs::runtime::v0::DynamicVamanaIndex::destroy (index);
180+ index = nullptr ;
181+ }
182+
183+ // Investigate the file size on disk
184+ size_t file_size = std::filesystem::file_size (filename);
185+
186+ // Load the index from disk
187+ std::ifstream in (filename, std::ios::binary);
188+ CATCH_REQUIRE (in.is_open ());
189+
190+ // Monitor RSS increase
191+ size_t rss_before = get_current_rss ();
192+
193+ svs::runtime::v0::DynamicVamanaIndex* loaded = nullptr ;
194+ auto status = svs::runtime::v0::DynamicVamanaIndex::load (
195+ &loaded, in, svs::runtime::v0::MetricType::L2, svs::runtime::v0::StorageKind::FP32
196+ );
197+ CATCH_REQUIRE (status.ok ());
198+ CATCH_REQUIRE (loaded != nullptr );
199+ in.close ();
200+
201+ size_t rss_delta = get_current_rss () - rss_before;
202+
203+ // Clean up
204+ svs::runtime::v0::DynamicVamanaIndex::destroy (loaded);
205+ loaded = nullptr ;
206+
207+ return {file_size, rss_delta};
208+ }
209+
133210CATCH_TEST_CASE (" WriteAndReadIndexSVS" , " [runtime]" ) {
134211 const auto & test_data = get_test_data ();
135212 auto build_func = [](svs::runtime::v0::DynamicVamanaIndex** index) {
@@ -377,94 +454,21 @@ CATCH_TEST_CASE("RangeSearchFunctional", "[runtime]") {
377454}
378455
379456CATCH_TEST_CASE (" MemoryUsageOnLoad" , " [runtime][memory]" ) {
380- // Generate some MiB of test data
381- constexpr size_t mem_test_d = 128 ;
382- constexpr size_t target_mibytes = 2 ;
383- constexpr size_t target_bytes = target_mibytes * 1024 * 1024 ;
384- constexpr size_t mem_test_n = target_bytes / (mem_test_d * sizeof (float ));
385-
386- std::cout << " Generating " << mem_test_n << " vectors of dimension " << mem_test_d
387- << " (approx " << (mem_test_n * mem_test_d * sizeof (float )) / (1024 * 1024 )
388- << " MiB)" << std::endl;
389-
390- // Save the index to a file
391- svs_test::prepare_temp_directory ();
392- auto temp_dir = svs_test::temp_directory ();
393- auto filename = temp_dir / " memory_test_index.bin" ;
394-
395- {
396- // Build Vamana FP32 index, scoped for memory cleanup
397- auto large_test_data = create_test_data (mem_test_n, mem_test_d, 456 );
398-
399- svs::runtime::v0::DynamicVamanaIndex* index = nullptr ;
400- svs::runtime::v0::VamanaIndex::BuildParams build_params{64 };
401- svs::runtime::v0::Status status = svs::runtime::v0::DynamicVamanaIndex::build (
402- &index,
403- mem_test_d,
404- svs::runtime::v0::MetricType::L2,
405- svs::runtime::v0::StorageKind::FP32,
406- build_params
407- );
408- CATCH_REQUIRE (status.ok ());
409- CATCH_REQUIRE (index != nullptr );
410-
411- // Add data to index
412- std::vector<size_t > labels (mem_test_n);
413- std::iota (labels.begin (), labels.end (), 0 );
414- status = index->add (mem_test_n, labels.data (), large_test_data.data ());
415- CATCH_REQUIRE (status.ok ());
416-
417- std::ofstream out (filename, std::ios::binary);
418- CATCH_REQUIRE (out.is_open ());
419- status = index->save (out);
420- CATCH_REQUIRE (status.ok ());
421- out.close ();
422-
423- // Clean up the first index
424- svs::runtime::v0::DynamicVamanaIndex::destroy (index);
425- index = nullptr ;
457+ CATCH_SECTION (" SmallIndex" ) {
458+ auto stats = run_save_and_load_test (10 );
459+ CATCH_REQUIRE (stats.file_size < 20 * 1024 * 1024 );
460+ CATCH_REQUIRE (stats.rss_increase < 1.2 * stats.file_size );
426461 }
427462
428- // Investigate the file size on disk
429- size_t file_size = std::filesystem::file_size (filename);
430- std::cout << " Index file size on disk: " << file_size / (1024 * 1024 ) << " MiB ("
431- << file_size << " bytes)" << std::endl;
432-
433- // Load the index from disk
434- std::ifstream in (filename, std::ios::binary);
435- CATCH_REQUIRE (in.is_open ());
436-
437- // Snapshot current memory usage before loading
438- size_t memory_before = get_current_rss ();
439- std::cout << " Memory before load: " << memory_before / (1024 * 1024 ) << " MiB"
440- << std::endl;
441-
442- svs::runtime::v0::DynamicVamanaIndex* loaded = nullptr ;
443- auto status = svs::runtime::v0::DynamicVamanaIndex::load (
444- &loaded, in, svs::runtime::v0::MetricType::L2, svs::runtime::v0::StorageKind::FP32
445- );
446- CATCH_REQUIRE (status.ok ());
447- CATCH_REQUIRE (loaded != nullptr );
448- in.close ();
449-
450- // Snapshot memory usage after loading
451- size_t memory_after = get_current_rss ();
452- std::cout << " Memory after load: " << memory_after / (1024 * 1024 ) << " MiB"
453- << std::endl;
454-
455- // Calculate memory increase
456- size_t memory_increase = memory_after - memory_before;
457- std::cout << " Memory increase: " << memory_increase / (1024 * 1024 ) << " MiB ("
458- << memory_increase << " bytes)" << std::endl;
459-
460- // Assert that no more than 140% of file size on disk were allocated
461- size_t max_allowed_memory = static_cast <size_t >(file_size * 1.4 );
462- std::cout << " Max allowed memory (140% of file size): "
463- << max_allowed_memory / (1024 * 1024 ) << " MiB (" << max_allowed_memory
464- << " bytes)" << std::endl;
465-
466- CATCH_REQUIRE (memory_increase <= max_allowed_memory);
463+ CATCH_SECTION (" MediumIndex" ) {
464+ auto stats = run_save_and_load_test (50 );
465+ CATCH_REQUIRE (stats.file_size < 100 * 1024 * 1024 );
466+ CATCH_REQUIRE (stats.rss_increase < 1.2 * stats.file_size );
467+ }
467468
468- // Clean up
469- svs::runtime::v0::DynamicVamanaIndex::destroy (loaded);
469+ CATCH_SECTION (" LargeIndex" ) {
470+ auto stats = run_save_and_load_test (200 );
471+ CATCH_REQUIRE (stats.file_size < 400 * 1024 * 1024 );
472+ CATCH_REQUIRE (stats.rss_increase < 1.2 * stats.file_size );
473+ }
470474}
0 commit comments