From 7fa25005e3a1ecb40870e90d653e3f13b3671d0b Mon Sep 17 00:00:00 2001 From: "k.koide" Date: Sun, 22 Jun 2025 19:27:45 +0900 Subject: [PATCH 1/4] offload GPU memory --- include/glim/mapping/global_mapping.hpp | 7 ++- src/glim/mapping/global_mapping.cpp | 58 +++++++++++++++++++++++-- src/glim/odometry/callbacks.cpp | 2 +- 3 files changed, 62 insertions(+), 5 deletions(-) diff --git a/include/glim/mapping/global_mapping.hpp b/include/glim/mapping/global_mapping.hpp index 2e59e4a3..895749d7 100644 --- a/include/glim/mapping/global_mapping.hpp +++ b/include/glim/mapping/global_mapping.hpp @@ -35,6 +35,8 @@ struct GlobalMappingParams { bool enable_between_factors; std::string between_registration_type; + size_t gpu_memory_offload_mb; // in MB + std::string registration_error_factor_type; double submap_voxel_resolution; double submap_voxel_resolution_max; @@ -84,6 +86,7 @@ class GlobalMapping : public GlobalMappingBase { std::shared_ptr create_matching_cost_factors(int current) const; void update_submaps(); + void offload_gpu_memory(); gtsam_points::ISAM2ResultExt update_isam2(const gtsam::NonlinearFactorGraph& new_factors, const gtsam::Values& new_values); void recover_graph() override; @@ -96,9 +99,11 @@ class GlobalMapping : public GlobalMappingBase { std::mt19937 mt; int session_id; + std::uint64_t point_bytes_gpu; + std::unique_ptr imu_integration; std::any stream_buffer_roundrobin; - + std::vector submaps; std::vector subsampled_submaps; diff --git a/src/glim/mapping/global_mapping.cpp b/src/glim/mapping/global_mapping.cpp index 6fe63b1a..738c535f 100644 --- a/src/glim/mapping/global_mapping.cpp +++ b/src/glim/mapping/global_mapping.cpp @@ -55,6 +55,8 @@ GlobalMappingParams::GlobalMappingParams() { enable_between_factors = config.param("global_mapping", "create_between_factors", false); between_registration_type = config.param("global_mapping", "between_registration_type", "GICP"); + gpu_memory_offload_mb = config.param("global_mapping", "gpu_memory_offload_mb", 64); + registration_error_factor_type = config.param("global_mapping", "registration_error_factor_type", "VGICP"); submap_voxel_resolution = config.param("global_mapping", "submap_voxel_resolution", 1.0); submap_voxel_resolution_max = config.param("global_mapping", "submap_voxel_resolution_max", submap_voxel_resolution); @@ -87,6 +89,7 @@ GlobalMapping::GlobalMapping(const GlobalMappingParams& params) : params(params) #endif session_id = 0; + point_bytes_gpu = 0; imu_integration.reset(new IMUIntegration); new_values.reset(new gtsam::Values); @@ -229,6 +232,8 @@ void GlobalMapping::insert_submap(const SubMap::Ptr& submap) { update_submaps(); Callbacks::on_update_submaps(submaps); + + offload_gpu_memory(); } void GlobalMapping::insert_submap(int current, const SubMap::Ptr& submap) { @@ -280,6 +285,9 @@ void GlobalMapping::insert_submap(int current, const SubMap::Ptr& submap) { submaps.push_back(submap); subsampled_submaps.push_back(subsampled_submap); + + point_bytes_gpu += subsampled_submap->memory_usage_gpu(); + logger->info("Points={:.1f}MB", point_bytes_gpu / (1024.0 * 1024.0)); } void GlobalMapping::find_overlapping_submaps(double min_overlap) { @@ -332,7 +340,9 @@ void GlobalMapping::find_overlapping_submaps(double min_overlap) { const auto& stream = stream_buffer.first; const auto& buffer = stream_buffer.second; for (const auto& voxelmap : submaps[i]->voxelmaps) { - new_factors->emplace_shared(X(i), X(j), voxelmap, subsampled_submaps[j], stream, buffer); + auto f = gtsam::make_shared(X(i), X(j), voxelmap, subsampled_submaps[j], stream, buffer); + f->set_enable_offloading(params.gpu_memory_offload_mb > 0); + new_factors->add(f); } } #endif @@ -463,7 +473,9 @@ std::shared_ptr GlobalMapping::create_matching_cost const auto& stream = stream_buffer.first; const auto& buffer = stream_buffer.second; for (const auto& voxelmap : submaps[i]->voxelmaps) { - factors->emplace_shared(X(i), X(current), voxelmap, subsampled_submaps[current], stream, buffer); + auto f = gtsam::make_shared(X(i), X(current), voxelmap, subsampled_submaps[current], stream, buffer); + f->set_enable_offloading(params.gpu_memory_offload_mb > 0); + factors->add(f); } } #endif @@ -489,6 +501,45 @@ void GlobalMapping::update_submaps() { } } +void GlobalMapping::offload_gpu_memory(){ + const size_t thresh = params.gpu_memory_offload_mb * (1024ull * 1024ull); + + if (point_bytes_gpu < thresh) { + return; + } + + logger->info("offload GPU memory ({}MB)", point_bytes_gpu / (1024.0 * 1024.0)); + + std::vector points_on_gpu; + for (const auto& points : subsampled_submaps) { + if (points->memory_usage_gpu() == 0) { + continue; + } + + auto points_gpu = std::dynamic_pointer_cast(points); + if (!points_gpu) { + logger->warn("subsampled submap is not a PointCloudGPU!!"); + continue; + } + + points_on_gpu.push_back(std::const_pointer_cast(points_gpu)); + } + + std::sort(points_on_gpu.begin(), points_on_gpu.end(), [](const gtsam_points::PointCloudGPU::Ptr& a, const gtsam_points::PointCloudGPU::Ptr& b) { + return a->last_accessed_time() > b->last_accessed_time(); + }); + + for (auto& points_gpu : points_on_gpu) { + if (point_bytes_gpu < thresh) { + break; + } + + size_t bytes = points_gpu->memory_usage_gpu(); + points_gpu->offload_gpu(); + point_bytes_gpu -= bytes; + } +} + gtsam_points::ISAM2ResultExt GlobalMapping::update_isam2(const gtsam::NonlinearFactorGraph& new_factors, const gtsam::Values& new_values) { gtsam_points::ISAM2ResultExt result; @@ -822,7 +873,8 @@ bool GlobalMapping::load(const std::string& path) { const auto& buffer = stream_buffer.second; for (const auto& voxelmap : submaps[first]->voxelmaps) { - graph.emplace_shared(X(first), X(second), voxelmap, subsampled_submaps[second], stream, buffer); + auto f = gtsam::make_shared(X(first), X(second), voxelmap, subsampled_submaps[second], stream, buffer); + graph.add(f); } #else logger->warn("GPU is enabled but gtsam_points was built without CUDA!!"); diff --git a/src/glim/odometry/callbacks.cpp b/src/glim/odometry/callbacks.cpp index bd2843aa..b4053ccb 100644 --- a/src/glim/odometry/callbacks.cpp +++ b/src/glim/odometry/callbacks.cpp @@ -1,6 +1,6 @@ #include -#include +#include namespace glim { From 9cec497f6de2b2040b4170896a838a819f0c0408 Mon Sep 17 00:00:00 2001 From: "k.koide" Date: Tue, 24 Jun 2025 19:52:07 +0900 Subject: [PATCH 2/4] offloadable --- include/glim/mapping/global_mapping.hpp | 4 +- src/glim/mapping/global_mapping.cpp | 57 +++++++++++++++---------- 2 files changed, 36 insertions(+), 25 deletions(-) diff --git a/include/glim/mapping/global_mapping.hpp b/include/glim/mapping/global_mapping.hpp index 895749d7..b81f078c 100644 --- a/include/glim/mapping/global_mapping.hpp +++ b/include/glim/mapping/global_mapping.hpp @@ -99,11 +99,11 @@ class GlobalMapping : public GlobalMappingBase { std::mt19937 mt; int session_id; - std::uint64_t point_bytes_gpu; + size_t submap_bytes_gpu; std::unique_ptr imu_integration; std::any stream_buffer_roundrobin; - + std::vector submaps; std::vector subsampled_submaps; diff --git a/src/glim/mapping/global_mapping.cpp b/src/glim/mapping/global_mapping.cpp index 738c535f..005b3e1a 100644 --- a/src/glim/mapping/global_mapping.cpp +++ b/src/glim/mapping/global_mapping.cpp @@ -89,7 +89,7 @@ GlobalMapping::GlobalMapping(const GlobalMappingParams& params) : params(params) #endif session_id = 0; - point_bytes_gpu = 0; + submap_bytes_gpu = 0; imu_integration.reset(new IMUIntegration); new_values.reset(new gtsam::Values); @@ -270,6 +270,8 @@ void GlobalMapping::insert_submap(int current, const SubMap::Ptr& submap) { auto voxelmap = std::make_shared(resolution); voxelmap->insert(*submap->frame); submap->voxelmaps.push_back(voxelmap); + + submap_bytes_gpu += voxelmap->memory_usage_gpu(); } } #endif @@ -283,11 +285,15 @@ void GlobalMapping::insert_submap(int current, const SubMap::Ptr& submap) { } } +#ifdef GTSAM_POINTS_USE_CUDA + auto points_gpu = std::dynamic_pointer_cast(submap->frame); + if (points_gpu) { + submap_bytes_gpu += points_gpu->memory_usage_gpu(); + } +#endif + submaps.push_back(submap); subsampled_submaps.push_back(subsampled_submap); - - point_bytes_gpu += subsampled_submap->memory_usage_gpu(); - logger->info("Points={:.1f}MB", point_bytes_gpu / (1024.0 * 1024.0)); } void GlobalMapping::find_overlapping_submaps(double min_overlap) { @@ -501,42 +507,47 @@ void GlobalMapping::update_submaps() { } } -void GlobalMapping::offload_gpu_memory(){ +void GlobalMapping::offload_gpu_memory() { const size_t thresh = params.gpu_memory_offload_mb * (1024ull * 1024ull); - if (point_bytes_gpu < thresh) { + if (submap_bytes_gpu < thresh) { return; } - logger->info("offload GPU memory ({}MB)", point_bytes_gpu / (1024.0 * 1024.0)); + logger->info("offload GPU memory ({:.2f}MB)", submap_bytes_gpu / (1024.0 * 1024.0)); - std::vector points_on_gpu; - for (const auto& points : subsampled_submaps) { - if (points->memory_usage_gpu() == 0) { - continue; + std::vector offloadables; + for (auto& submap : submaps) { + auto points_gpu = std::dynamic_pointer_cast(submap->frame); + if (points_gpu && points_gpu->loaded_on_gpu()) { + offloadables.emplace_back(points_gpu); } - auto points_gpu = std::dynamic_pointer_cast(points); - if (!points_gpu) { - logger->warn("subsampled submap is not a PointCloudGPU!!"); - continue; + for (auto& voxelmap : submap->voxelmaps) { + auto voxelmap_gpu = std::dynamic_pointer_cast(voxelmap); + if (voxelmap_gpu && voxelmap_gpu->loaded_on_gpu()) { + offloadables.emplace_back(voxelmap_gpu); + } } + } - points_on_gpu.push_back(std::const_pointer_cast(points_gpu)); + for (int i = 0; i < offloadables.size(); i++) { + logger->info("{} : {} bytes, last accessed at {}", i, offloadables[i]->memory_usage_gpu(), offloadables[i]->last_accessed_time()); } - std::sort(points_on_gpu.begin(), points_on_gpu.end(), [](const gtsam_points::PointCloudGPU::Ptr& a, const gtsam_points::PointCloudGPU::Ptr& b) { - return a->last_accessed_time() > b->last_accessed_time(); + std::sort(offloadables.begin(), offloadables.end(), [](const gtsam_points::OffloadableGPU::Ptr& a, const gtsam_points::OffloadableGPU::Ptr& b) { + return a->last_accessed_time() < b->last_accessed_time(); }); - for (auto& points_gpu : points_on_gpu) { - if (point_bytes_gpu < thresh) { + for (auto& offloadable : offloadables) { + if (submap_bytes_gpu < thresh) { break; } - size_t bytes = points_gpu->memory_usage_gpu(); - points_gpu->offload_gpu(); - point_bytes_gpu -= bytes; + size_t bytes = offloadable->memory_usage_gpu(); + offloadable->offload_gpu(); + submap_bytes_gpu -= bytes; + logger->info("offloaded {} bytes", bytes); } } From 1cadc83d2240deb93c2aabecfadd35019839c4ef Mon Sep 17 00:00:00 2001 From: "k.koide" Date: Wed, 25 Jun 2025 15:54:37 +0900 Subject: [PATCH 3/4] refine offloading and update visualization --- config/config_global_mapping_gpu.json | 11 ++++- include/glim/mapping/global_mapping.hpp | 1 + include/glim/viewer/standard_viewer.hpp | 5 +-- include/glim/viewer/standard_viewer_mem.hpp | 7 ++- src/glim/mapping/global_mapping.cpp | 23 +++++----- src/glim/viewer/standard_viewer.cpp | 47 +++++++++++---------- src/glim/viewer/standard_viewer_mem.cpp | 43 +++++++++++++++++++ 7 files changed, 99 insertions(+), 38 deletions(-) diff --git a/config/config_global_mapping_gpu.json b/config/config_global_mapping_gpu.json index 4562b3c9..bc88a0f2 100644 --- a/config/config_global_mapping_gpu.json +++ b/config/config_global_mapping_gpu.json @@ -22,6 +22,13 @@ // use_isam2_dogleg : If true, use dogleg optimizer (robust but slow) // isam2_relinearize_skip : Relinearization is performed every $isam2_relinearize_skip optimization calls // isam2_relinearize_thresh : Relinearization is performed only when linear delta gets larger than this + // + // --- Memory settings --- + // gpu_memory_offload_mb : Threshold for GPU memory offloading in MB (0 = disable offloading). + // : If GPU memory usage exceeds this value, old submap points and voxelmaps are offloaded to CPU memory. + // : Note that the current implementation doesn't support offloading of factors and graphics data, and thus + // : the actual GPU memory usage will be much larger than this value. + // : (Setting this to a half or quarter of the total GPU memory would be a good choice). */ "global_mapping": { "so_name": "libglobal_mapping.so", @@ -46,6 +53,8 @@ // Optimizer settings "use_isam2_dogleg": false, "isam2_relinearize_skip": 1, - "isam2_relinearize_thresh": 0.1 + "isam2_relinearize_thresh": 0.1, + // Memory settings + "gpu_memory_offload_mb": 2048 } } \ No newline at end of file diff --git a/include/glim/mapping/global_mapping.hpp b/include/glim/mapping/global_mapping.hpp index b81f078c..93e5e798 100644 --- a/include/glim/mapping/global_mapping.hpp +++ b/include/glim/mapping/global_mapping.hpp @@ -102,6 +102,7 @@ class GlobalMapping : public GlobalMappingBase { size_t submap_bytes_gpu; std::unique_ptr imu_integration; + std::any main_stream; std::any stream_buffer_roundrobin; std::vector submaps; diff --git a/include/glim/viewer/standard_viewer.hpp b/include/glim/viewer/standard_viewer.hpp index aa889bb7..5f3c8c2b 100644 --- a/include/glim/viewer/standard_viewer.hpp +++ b/include/glim/viewer/standard_viewer.hpp @@ -27,7 +27,7 @@ namespace glim { class TrajectoryManager; struct EstimationFrame; -struct SubMapMemoryStats; +struct SubMap; struct FactorMemoryStats; class StandardViewer : public ExtensionModule { @@ -89,8 +89,7 @@ class StandardViewer : public ExtensionModule { float min_overlap; bool show_memory_stats; - int submap_memstats_count; - std::vector submap_memstats; + std::vector> submaps; int global_factor_stats_count; std::vector global_factor_memstats; diff --git a/include/glim/viewer/standard_viewer_mem.hpp b/include/glim/viewer/standard_viewer_mem.hpp index 1351c3a9..025bef02 100644 --- a/include/glim/viewer/standard_viewer_mem.hpp +++ b/include/glim/viewer/standard_viewer_mem.hpp @@ -19,9 +19,12 @@ struct SubMapMemoryStats { size_t voxelmap_cpu_bytes; ///< CPU memory usage for voxel map size_t frame_gpu_bytes; ///< GPU memory usage size_t voxelmap_gpu_bytes; ///< GPU memory usage for voxel map + size_t frame_gpu_net_bytes; ///< GPU memory usage for the frame + size_t voxelmap_gpu_net_bytes; ///< GPU memory usage for the voxel map - size_t odom_cpu_bytes; ///< Total CPU memory usage for all frames - size_t odom_gpu_bytes; ///< Total GPU memory usage for all frames + size_t odom_cpu_bytes; ///< Total CPU memory usage for all frames + size_t odom_gpu_bytes; ///< Total GPU memory usage for all frames + size_t odom_gpu_net_bytes; ///< Total GPU memory usage for all frames (networked) size_t num_custom_data; ///< Number of custom data entries in the submap }; diff --git a/src/glim/mapping/global_mapping.cpp b/src/glim/mapping/global_mapping.cpp index 005b3e1a..6d39e59f 100644 --- a/src/glim/mapping/global_mapping.cpp +++ b/src/glim/mapping/global_mapping.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -55,7 +56,7 @@ GlobalMappingParams::GlobalMappingParams() { enable_between_factors = config.param("global_mapping", "create_between_factors", false); between_registration_type = config.param("global_mapping", "between_registration_type", "GICP"); - gpu_memory_offload_mb = config.param("global_mapping", "gpu_memory_offload_mb", 64); + gpu_memory_offload_mb = config.param("global_mapping", "gpu_memory_offload_mb", 0); registration_error_factor_type = config.param("global_mapping", "registration_error_factor_type", "VGICP"); submap_voxel_resolution = config.param("global_mapping", "submap_voxel_resolution", 1.0); @@ -110,7 +111,8 @@ GlobalMapping::GlobalMapping(const GlobalMappingParams& params) : params(params) } #ifdef GTSAM_POINTS_USE_CUDA - stream_buffer_roundrobin = std::make_shared(64); + main_stream = std::make_shared(); + stream_buffer_roundrobin = std::make_shared(4); #endif #ifdef GTSAM_USE_TBB @@ -508,13 +510,14 @@ void GlobalMapping::update_submaps() { } void GlobalMapping::offload_gpu_memory() { +#ifdef GTSAM_POINTS_USE_CUDA const size_t thresh = params.gpu_memory_offload_mb * (1024ull * 1024ull); - if (submap_bytes_gpu < thresh) { + if (params.gpu_memory_offload_mb == 0 || submap_bytes_gpu < thresh) { return; } - logger->info("offload GPU memory ({:.2f}MB)", submap_bytes_gpu / (1024.0 * 1024.0)); + logger->debug("offload GPU memory ({:.2f}MB)", submap_bytes_gpu / (1024.0 * 1024.0)); std::vector offloadables; for (auto& submap : submaps) { @@ -531,24 +534,24 @@ void GlobalMapping::offload_gpu_memory() { } } - for (int i = 0; i < offloadables.size(); i++) { - logger->info("{} : {} bytes, last accessed at {}", i, offloadables[i]->memory_usage_gpu(), offloadables[i]->last_accessed_time()); - } - std::sort(offloadables.begin(), offloadables.end(), [](const gtsam_points::OffloadableGPU::Ptr& a, const gtsam_points::OffloadableGPU::Ptr& b) { return a->last_accessed_time() < b->last_accessed_time(); }); + auto stream = std::any_cast>(main_stream); for (auto& offloadable : offloadables) { if (submap_bytes_gpu < thresh) { break; } size_t bytes = offloadable->memory_usage_gpu(); - offloadable->offload_gpu(); + offloadable->offload_gpu(*stream); submap_bytes_gpu -= bytes; - logger->info("offloaded {} bytes", bytes); + logger->debug("offloaded {} bytes", bytes); } + stream->sync(); + +#endif } gtsam_points::ISAM2ResultExt GlobalMapping::update_isam2(const gtsam::NonlinearFactorGraph& new_factors, const gtsam::Values& new_values) { diff --git a/src/glim/viewer/standard_viewer.cpp b/src/glim/viewer/standard_viewer.cpp index b7fcb533..e768b396 100644 --- a/src/glim/viewer/standard_viewer.cpp +++ b/src/glim/viewer/standard_viewer.cpp @@ -72,7 +72,6 @@ StandardViewer::StandardViewer() : logger(create_module_logger("viewer")) { min_overlap = 0.2f; show_memory_stats = false; - submap_memstats_count = 0; global_factor_stats_count = 0; total_gl_bytes = 0; @@ -529,6 +528,7 @@ void StandardViewer::set_callbacks() { const std::shared_ptr T_world_origin(new Eigen::Isometry3d(submap->T_world_origin)); invoke([this, submap, T_world_origin] { + submaps.emplace_back(submap); const double stamp_endpoint_R = submap->odom_frames.back()->stamp; const Eigen::Isometry3d T_world_endpoint_R = (*T_world_origin) * submap->T_origin_endpoint_R; trajectory->update_anchor(stamp_endpoint_R, T_world_endpoint_R); @@ -559,20 +559,9 @@ void StandardViewer::set_callbacks() { submap_poses[i] = submaps[i]->T_world_origin.cast(); } - std::vector mem_stats; - if (show_memory_stats) { - mem_stats.reserve(submaps.size() - submap_memstats_count); - for (int i = submap_memstats_count; i < submaps.size(); i++) { - mem_stats.emplace_back(*submaps[i]); - } - submap_memstats_count = submaps.size(); - } - - invoke([this, latest_submap, submap_ids, submap_poses, mem_stats] { + invoke([this, latest_submap, submap_ids, submap_poses] { auto viewer = guik::LightViewer::instance(); - submap_memstats.insert(submap_memstats.end(), mem_stats.begin(), mem_stats.end()); - std::vector submap_positions(submap_ids.size()); last_submap_z = submap_poses.back().translation().z(); @@ -910,18 +899,26 @@ void StandardViewer::drawable_selection() { size_t points_cpu = 0; size_t points_gpu = 0; + size_t points_gpu_net = 0; size_t voxelmap_cpu = 0; size_t voxelmap_gpu = 0; + size_t voxelmap_gpu_net = 0; size_t odom_cpu = 0; size_t odom_gpu = 0; + size_t odom_gpu_net = 0; + + for (const auto& s : submaps) { + const SubMapMemoryStats m(*s); - for (const auto& m : submap_memstats) { points_cpu += m.frame_cpu_bytes; points_gpu += m.frame_gpu_bytes; + points_gpu_net += m.frame_gpu_net_bytes; voxelmap_cpu += m.voxelmap_cpu_bytes; voxelmap_gpu += m.voxelmap_gpu_bytes; + voxelmap_gpu_net += m.voxelmap_gpu_net_bytes; odom_cpu += m.odom_cpu_bytes; odom_gpu += m.odom_gpu_bytes; + odom_gpu_net += m.odom_gpu_net_bytes; } size_t factors_cpu = 0; @@ -935,14 +932,17 @@ void StandardViewer::drawable_selection() { constexpr double mb = 1.0 / (1024.0 * 1024.0); const size_t total_cpu = points_cpu + voxelmap_cpu + odom_cpu + factors_cpu; const size_t total_gpu = points_gpu + voxelmap_gpu + odom_gpu + factors_gpu + total_gl_bytes; + const size_t total_gpu_net = points_gpu_net + voxelmap_gpu_net + odom_gpu_net + factors_gpu + total_gl_bytes; const double total_cpu_mb = total_cpu * mb; const double total_gpu_mb = total_gpu * mb; + const double total_gpu_net_mb = total_gpu_net * mb; ImGui::Text("Global mapping memory usage"); - if (ImGui::BeginTable("Global mapping memory usage", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) { - const auto show_item = [=](const char* name, size_t cpu, size_t gpu) { + if (ImGui::BeginTable("Global mapping memory usage", 4, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) { + const auto show_item = [=](const char* name, size_t cpu, size_t gpu, size_t gpu_net = 0) { const double cpu_mb = cpu * mb; const double gpu_mb = gpu * mb; + const double gpu_net_mb = gpu_net * mb; ImGui::TableNextRow(); ImGui::TableNextColumn(); @@ -951,19 +951,22 @@ void StandardViewer::drawable_selection() { ImGui::Text("%.2f MB / %.1f %%", cpu_mb, cpu_mb / total_cpu_mb * 100.0); ImGui::TableNextColumn(); ImGui::Text("%.2f MB / %.1f %%", gpu * mb, gpu_mb / total_gpu_mb * 100.0); + ImGui::TableNextColumn(); + ImGui::Text("%.2f MB / %.1f %%", gpu_net * mb, gpu_net_mb / total_gpu_mb * 100.0); }; ImGui::TableSetupColumn("Item"); ImGui::TableSetupColumn("CPU"); ImGui::TableSetupColumn("GPU"); + ImGui::TableSetupColumn("GPU (net)"); ImGui::TableHeadersRow(); - show_item("Total", total_cpu, total_gpu); - show_item("Points", points_cpu, points_gpu); - show_item("Voxelmap", voxelmap_cpu, voxelmap_gpu); - show_item("Odom frames", odom_cpu, odom_gpu); - show_item("Factors", factors_cpu, factors_gpu); - show_item("OpenGL", 0, total_gl_bytes); + show_item("Total", total_cpu, total_gpu, total_gpu_net); + show_item("Points", points_cpu, points_gpu, points_gpu_net); + show_item("Voxelmap", voxelmap_cpu, voxelmap_gpu, voxelmap_gpu_net); + show_item("Odom frames", odom_cpu, odom_gpu, odom_gpu_net); + show_item("Factors", factors_cpu, factors_gpu, factors_gpu); + show_item("OpenGL", 0, total_gl_bytes, total_gl_bytes); ImGui::EndTable(); } diff --git a/src/glim/viewer/standard_viewer_mem.cpp b/src/glim/viewer/standard_viewer_mem.cpp index ca242d80..755d999c 100644 --- a/src/glim/viewer/standard_viewer_mem.cpp +++ b/src/glim/viewer/standard_viewer_mem.cpp @@ -49,6 +49,18 @@ size_t get_mem_stats_cpu(const gtsam_points::PointCloud& points) { size_t get_mem_stats_gpu(const gtsam_points::PointCloud& points) { size_t bytes = 0; + bytes += points.has_times() ? sizeof(float) * points.size() : 0; + bytes += points.has_points() ? sizeof(Eigen::Vector3f) * points.size() : 0; + bytes += points.has_normals() ? sizeof(Eigen::Vector3f) * points.size() : 0; + bytes += points.has_covs() ? sizeof(Eigen::Matrix3f) * points.size() : 0; + bytes += points.has_intensities() ? sizeof(float) * points.size() : 0; + + return bytes; +} + +size_t get_net_mem_stats_gpu(const gtsam_points::PointCloud& points) { + size_t bytes = 0; + bytes += points.has_times_gpu() ? sizeof(float) * points.size() : 0; bytes += points.has_points_gpu() ? sizeof(Eigen::Vector3f) * points.size() : 0; bytes += points.has_normals_gpu() ? sizeof(Eigen::Vector3f) * points.size() : 0; @@ -80,6 +92,19 @@ size_t get_mem_stats_gpu(const gtsam_points::GaussianVoxelMap* voxelmap) { #endif } +size_t get_net_mem_stats_gpu(const gtsam_points::GaussianVoxelMap* voxelmap) { +#ifdef GTSAM_POINTS_USE_CUDA + const auto v = dynamic_cast(voxelmap); + if (!v || !v->buckets) { + return 0; + } + constexpr size_t voxel_size = sizeof(int) + sizeof(Eigen::Vector3f) + sizeof(Eigen::Matrix3f); + return v->voxelmap_info.num_voxels * voxel_size + v->voxelmap_info.num_buckets * sizeof(gtsam_points::VoxelBucket); +#else + return 0; // GPU voxel maps are not supported without CUDA +#endif +} + size_t get_mem_stats_cpu(const EstimationFrame& frame) { size_t bytes = 0; @@ -111,33 +136,51 @@ size_t get_mem_stats_gpu(const EstimationFrame& frame) { return bytes; } +size_t get_net_mem_stats_gpu(const EstimationFrame& frame) { + size_t bytes = 0; + + bytes += frame.frame ? get_net_mem_stats_gpu(*frame.frame) : 0; + for (const auto& voxelmap : frame.voxelmaps) { + bytes += get_net_mem_stats_gpu(voxelmap.get()); + } + + return bytes; +} + SubMapMemoryStats::SubMapMemoryStats() : id(0), frame_cpu_bytes(0), voxelmap_cpu_bytes(0), frame_gpu_bytes(0), voxelmap_gpu_bytes(0), + frame_gpu_net_bytes(0), + voxelmap_gpu_net_bytes(0), odom_cpu_bytes(0), odom_gpu_bytes(0), + odom_gpu_net_bytes(0), num_custom_data(0) {} SubMapMemoryStats::SubMapMemoryStats(const SubMap& submap) : SubMapMemoryStats() { id = submap.id; frame_cpu_bytes = submap.frame ? get_mem_stats_cpu(*submap.frame) : 0; frame_gpu_bytes = submap.frame ? get_mem_stats_gpu(*submap.frame) : 0; + frame_gpu_net_bytes = submap.frame ? get_net_mem_stats_gpu(*submap.frame) : 0; for (const auto& voxelmap : submap.voxelmaps) { voxelmap_cpu_bytes += get_mem_stats_cpu(voxelmap.get()); voxelmap_gpu_bytes += get_mem_stats_gpu(voxelmap.get()); + voxelmap_gpu_net_bytes += get_net_mem_stats_gpu(voxelmap.get()); } for (const auto& frame : submap.frames) { odom_cpu_bytes += frame ? get_mem_stats_cpu(*frame) : 0; odom_gpu_bytes += frame ? get_mem_stats_gpu(*frame) : 0; + odom_gpu_net_bytes += frame ? get_net_mem_stats_gpu(*frame) : 0; } for (const auto& odom : submap.odom_frames) { odom_cpu_bytes += odom ? get_mem_stats_cpu(*odom) : 0; odom_gpu_bytes += odom ? get_mem_stats_gpu(*odom) : 0; + odom_gpu_net_bytes += odom ? get_net_mem_stats_gpu(*odom) : 0; } num_custom_data = submap.custom_data.size(); From f2530d14be1fc62eddefbbc6fd9574beafcd82be Mon Sep 17 00:00:00 2001 From: "k.koide" Date: Fri, 4 Jul 2025 19:42:19 +0900 Subject: [PATCH 4/4] offload GPU factors --- include/glim/mapping/global_mapping.hpp | 6 ++- include/glim/viewer/standard_viewer.hpp | 8 ++-- src/glim/mapping/global_mapping.cpp | 57 ++++++++++++++++--------- src/glim/viewer/standard_viewer.cpp | 36 ++++++++-------- 4 files changed, 62 insertions(+), 45 deletions(-) diff --git a/include/glim/mapping/global_mapping.hpp b/include/glim/mapping/global_mapping.hpp index 93e5e798..7ff827d3 100644 --- a/include/glim/mapping/global_mapping.hpp +++ b/include/glim/mapping/global_mapping.hpp @@ -12,8 +12,9 @@ class NonlinearFactorGraph; namespace gtsam_points { class ISAM2Ext; -class StreamTempBufferRoundRobin; struct ISAM2ResultExt; +class StreamTempBufferRoundRobin; +class OffloadableGPU; } // namespace gtsam_points namespace glim { @@ -99,7 +100,8 @@ class GlobalMapping : public GlobalMappingBase { std::mt19937 mt; int session_id; - size_t submap_bytes_gpu; + size_t bytes_gpu; + std::vector> offloadables; std::unique_ptr imu_integration; std::any main_stream; diff --git a/include/glim/viewer/standard_viewer.hpp b/include/glim/viewer/standard_viewer.hpp index 5f3c8c2b..7257ebd3 100644 --- a/include/glim/viewer/standard_viewer.hpp +++ b/include/glim/viewer/standard_viewer.hpp @@ -22,6 +22,10 @@ namespace gtsam { class NonlinearFactor; } +namespace gtsam_points { +class OffloadableGPU; +} + namespace glim { class TrajectoryManager; @@ -90,9 +94,7 @@ class StandardViewer : public ExtensionModule { bool show_memory_stats; std::vector> submaps; - - int global_factor_stats_count; - std::vector global_factor_memstats; + std::vector> gpu_factors; size_t total_gl_bytes; diff --git a/src/glim/mapping/global_mapping.cpp b/src/glim/mapping/global_mapping.cpp index 6d39e59f..ea241458 100644 --- a/src/glim/mapping/global_mapping.cpp +++ b/src/glim/mapping/global_mapping.cpp @@ -90,7 +90,7 @@ GlobalMapping::GlobalMapping(const GlobalMappingParams& params) : params(params) #endif session_id = 0; - submap_bytes_gpu = 0; + bytes_gpu = 0; imu_integration.reset(new IMUIntegration); new_values.reset(new gtsam::Values); @@ -273,7 +273,8 @@ void GlobalMapping::insert_submap(int current, const SubMap::Ptr& submap) { voxelmap->insert(*submap->frame); submap->voxelmaps.push_back(voxelmap); - submap_bytes_gpu += voxelmap->memory_usage_gpu(); + offloadables.emplace_back(voxelmap); + bytes_gpu += voxelmap->memory_usage_gpu(); } } #endif @@ -288,9 +289,10 @@ void GlobalMapping::insert_submap(int current, const SubMap::Ptr& submap) { } #ifdef GTSAM_POINTS_USE_CUDA - auto points_gpu = std::dynamic_pointer_cast(submap->frame); + auto points_gpu = std::dynamic_pointer_cast(submap->frame); if (points_gpu) { - submap_bytes_gpu += points_gpu->memory_usage_gpu(); + offloadables.emplace_back(points_gpu); + bytes_gpu += points_gpu->memory_usage_gpu(); } #endif @@ -511,28 +513,22 @@ void GlobalMapping::update_submaps() { void GlobalMapping::offload_gpu_memory() { #ifdef GTSAM_POINTS_USE_CUDA - const size_t thresh = params.gpu_memory_offload_mb * (1024ull * 1024ull); + const size_t thresh_hi = params.gpu_memory_offload_mb * (1024ull * 1024ull); + const size_t thresh_lo = thresh_hi * 0.9; - if (params.gpu_memory_offload_mb == 0 || submap_bytes_gpu < thresh) { + if (params.gpu_memory_offload_mb == 0 || bytes_gpu < thresh_hi) { return; } - logger->debug("offload GPU memory ({:.2f}MB)", submap_bytes_gpu / (1024.0 * 1024.0)); + logger->info("offload GPU memory ({:.2f}MB)", bytes_gpu / (1024.0 * 1024.0)); - std::vector offloadables; - for (auto& submap : submaps) { - auto points_gpu = std::dynamic_pointer_cast(submap->frame); - if (points_gpu && points_gpu->loaded_on_gpu()) { - offloadables.emplace_back(points_gpu); - } - - for (auto& voxelmap : submap->voxelmaps) { - auto voxelmap_gpu = std::dynamic_pointer_cast(voxelmap); - if (voxelmap_gpu && voxelmap_gpu->loaded_on_gpu()) { - offloadables.emplace_back(voxelmap_gpu); - } + size_t new_bytes_gpu = 0; + for (const auto& offloadable : offloadables) { + if (offloadable->loaded_on_gpu()) { + new_bytes_gpu += offloadable->memory_usage_gpu(); } } + logger->info("total GPU memory usage: {:.2f}MB", new_bytes_gpu / (1024.0 * 1024.0)); std::sort(offloadables.begin(), offloadables.end(), [](const gtsam_points::OffloadableGPU::Ptr& a, const gtsam_points::OffloadableGPU::Ptr& b) { return a->last_accessed_time() < b->last_accessed_time(); @@ -540,17 +536,24 @@ void GlobalMapping::offload_gpu_memory() { auto stream = std::any_cast>(main_stream); for (auto& offloadable : offloadables) { - if (submap_bytes_gpu < thresh) { + if (!offloadable->loaded_on_gpu()) { + continue; + } + + if (new_bytes_gpu < thresh_lo) { break; } size_t bytes = offloadable->memory_usage_gpu(); offloadable->offload_gpu(*stream); - submap_bytes_gpu -= bytes; + new_bytes_gpu -= bytes; logger->debug("offloaded {} bytes", bytes); } stream->sync(); + logger->info("offloaded GPU memory ({:.2f}MB)", new_bytes_gpu / (1024.0 * 1024.0)); + bytes_gpu = new_bytes_gpu; + #endif } @@ -605,6 +608,18 @@ gtsam_points::ISAM2ResultExt GlobalMapping::update_isam2(const gtsam::NonlinearF return update_isam2(factors, values); } + for (auto& factor : new_factors) { + if (!factor) { + continue; + } + + auto offloadable = std::dynamic_pointer_cast(factor); + if (offloadable) { + offloadables.emplace_back(offloadable); + bytes_gpu += offloadable->memory_usage_gpu(); + } + } + return result; } diff --git a/src/glim/viewer/standard_viewer.cpp b/src/glim/viewer/standard_viewer.cpp index e768b396..845cd0a4 100644 --- a/src/glim/viewer/standard_viewer.cpp +++ b/src/glim/viewer/standard_viewer.cpp @@ -72,7 +72,6 @@ StandardViewer::StandardViewer() : logger(create_module_logger("viewer")) { min_overlap = 0.2f; show_memory_stats = false; - global_factor_stats_count = 0; total_gl_bytes = 0; points_alpha = config.param("standard_viewer", "points_alpha", 1.0); @@ -616,27 +615,27 @@ void StandardViewer::set_callbacks() { between_factors.push_back(std::make_pair(symbol0.index(), symbol1.index())); } - invoke([this, between_factors] { global_between_factors.insert(global_between_factors.end(), between_factors.begin(), between_factors.end()); }); + std::vector new_gpu_factors; + for (const auto& factor : new_factors) { + if (!factor) { + continue; + } + const auto gpu_factor = std::dynamic_pointer_cast(factor); + if (gpu_factor) { + new_gpu_factors.emplace_back(gpu_factor); + } + } + + invoke([this, between_factors, new_gpu_factors] { + global_between_factors.insert(global_between_factors.end(), between_factors.begin(), between_factors.end()); + gpu_factors.insert(gpu_factors.end(), new_gpu_factors.begin(), new_gpu_factors.end()); + }); }); // Smoother update result callback GlobalMappingCallbacks::on_smoother_update_result.add([this](gtsam_points::ISAM2Ext& isam2, const gtsam_points::ISAM2ResultExt& result) { logger->debug("--- iSAM2 update ({} values / {} factors) ---", result.num_values, result.num_factors); logger->debug(result.to_string()); - - if (show_memory_stats) { - std::vector mem_stats; - - for (int i = global_factor_stats_count; i < isam2.getFactorsUnsafe().size(); i++) { - FactorMemoryStats stats(isam2.getFactorsUnsafe()[i]); - if (stats.cpu_bytes || stats.gpu_bytes) { - mem_stats.emplace_back(stats); - } - } - global_factor_stats_count = isam2.getFactorsUnsafe().size(); - - invoke([this, mem_stats] { global_factor_memstats.insert(global_factor_memstats.end(), mem_stats.begin(), mem_stats.end()); }); - } }); } @@ -924,9 +923,8 @@ void StandardViewer::drawable_selection() { size_t factors_cpu = 0; size_t factors_gpu = 0; - for (const auto& m : global_factor_memstats) { - factors_cpu += m.cpu_bytes; - factors_gpu += m.gpu_bytes; + for (const auto& f : gpu_factors) { + factors_gpu += f->loaded_on_gpu() ? f->memory_usage_gpu() : 0; } constexpr double mb = 1.0 / (1024.0 * 1024.0);