Refactor NSS and DNSS implementations for improved readability and modularity#7
Refactor NSS and DNSS implementations for improved readability and modularity#7eclipse0922 wants to merge 2 commits intomasterfrom
Conversation
…dularity - Rewrote legacy code into clean C++17 style. - Added missing method implementations and input validation. - Introduced options struct for configurable parameters in NSS and DNSS. - Implemented CUDA support for DNSS rotational feature computation. - Added new CMake configuration for building with or without CUDA. - Updated README to reflect changes and provide build instructions. - Added .gitignore to exclude build directories.
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
PR Description updated to latest commit (b7adea4) |
PR Reviewer Guide 🔍(Review updated until commit 4bb5106)Here are some key observations to aid the review process:
|
|
PR Description updated to latest commit (4bb5106) |
|
Persistent review updated to latest commit 4bb5106 |
|
/gemini review |
|
PR Description updated to latest commit (4bb5106) |
|
Persistent review updated to latest commit 4bb5106 |
PR Code Suggestions ✨Latest suggestions up to 4bb5106
Previous suggestionsSuggestions up to commit 4bb5106
Suggestions up to commit b7adea4
|
There was a problem hiding this comment.
Code Review
이 PR은 NSS 및 DNSS 구현을 현대적인 C++17 스타일로 리팩토링하여 코드 가독성, 모듈성 및 구성 가능성을 크게 향상시켰습니다. Options 구조체를 도입하고, CUDA 지원을 추가하고, 더 효율적인 알고리즘(예: lazy removal)을 사용한 점이 인상적입니다. 전반적으로 훌륭한 개선입니다.
몇 가지 추가적인 개선을 위해 코드의 재사용성과 유지보수성을 높일 수 있는 두 가지 제안을 남겼습니다. 하나는 NSS.h의 사용자 정의 fvec3에 대한 수학 헬퍼 함수를 인라인으로 제공하는 것이고, 다른 하나는 dnss_cuda.cu에서 RAII를 사용하여 CUDA 메모리 관리를 더 안전하게 만드는 것입니다.
| namespace glm | ||
| { | ||
| struct fvec3 | ||
| { | ||
| float x; | ||
| float y; | ||
| float z; | ||
|
|
||
| constexpr fvec3() | ||
| : x(0.0f), y(0.0f), z(0.0f) | ||
| { | ||
| } | ||
|
|
||
| constexpr explicit fvec3(float value) | ||
| : x(value), y(value), z(value) | ||
| { | ||
| } | ||
|
|
||
| constexpr fvec3(float x_value, float y_value, float z_value) | ||
| : x(x_value), y(y_value), z(z_value) | ||
| { | ||
| } | ||
|
|
||
| float &operator[](int index) | ||
| { | ||
| return (&x)[index]; | ||
| } | ||
|
|
||
| const float &operator[](int index) const | ||
| { | ||
| return (&x)[index]; | ||
| } | ||
|
|
||
| fvec3 &operator+=(const fvec3 &rhs) | ||
| { | ||
| x += rhs.x; | ||
| y += rhs.y; | ||
| z += rhs.z; | ||
| return *this; | ||
| } | ||
|
|
||
| fvec3 &operator-=(const fvec3 &rhs) | ||
| { | ||
| x -= rhs.x; | ||
| y -= rhs.y; | ||
| z -= rhs.z; | ||
| return *this; | ||
| } | ||
|
|
||
| fvec3 &operator*=(float scalar) | ||
| { | ||
| x *= scalar; | ||
| y *= scalar; | ||
| z *= scalar; | ||
| return *this; | ||
| } | ||
|
|
||
| fvec3 &operator/=(float scalar) | ||
| { | ||
| x /= scalar; | ||
| y /= scalar; | ||
| z /= scalar; | ||
| return *this; | ||
| } | ||
| }; | ||
|
|
||
| inline fvec3 operator+(fvec3 lhs, const fvec3 &rhs) | ||
| { | ||
| lhs += rhs; | ||
| return lhs; | ||
| } | ||
|
|
||
| inline fvec3 operator-(fvec3 lhs, const fvec3 &rhs) | ||
| { | ||
| lhs -= rhs; | ||
| return lhs; | ||
| } | ||
|
|
||
| inline fvec3 operator-(const fvec3 &value) | ||
| { | ||
| return fvec3(-value.x, -value.y, -value.z); | ||
| } | ||
|
|
||
| inline fvec3 operator*(fvec3 lhs, float scalar) | ||
| { | ||
| lhs *= scalar; | ||
| return lhs; | ||
| } | ||
|
|
||
| inline fvec3 operator*(float scalar, fvec3 rhs) | ||
| { | ||
| rhs *= scalar; | ||
| return rhs; | ||
| } | ||
|
|
||
| inline fvec3 operator/(fvec3 lhs, float scalar) | ||
| { | ||
| lhs /= scalar; | ||
| return lhs; | ||
| } | ||
| } // namespace glm |
There was a problem hiding this comment.
GLM에 대한 의존성을 줄이기 위해 자체 fvec3 구현을 제공하는 것은 좋은 접근입니다. 하지만 관련 수학 헬퍼 함수들(dotProduct, crossProduct, vectorLength, normalizeSafe)이 NSS.cpp의 익명 네임스페이스에 정의되어 있어 재사용이 어렵습니다.
이 함수들을 NSS.h 헤더에 inline 함수로 fvec3 구조체와 함께 정의하는 것을 고려해 보세요. 이렇게 하면 fvec3를 사용하는 다른 파일에서도 이 함수들을 재사용할 수 있어 코드 중복을 줄이고 모듈성을 높일 수 있습니다. glm 네임스페이스 안에 함께 정의하면 실제 GLM 라이브러리와의 일관성도 높일 수 있습니다.
| bool DNSSComputeRotationalFeaturesCuda( | ||
| const std::vector<glm::fvec3> &normalized_vertices, | ||
| const std::vector<glm::fvec3> &normals, | ||
| float theta_for_return_radians, | ||
| std::vector<glm::fvec3> *rotational_normals, | ||
| std::vector<float> *rotational_returns) | ||
| { | ||
| if (rotational_normals == nullptr || rotational_returns == nullptr) | ||
| { | ||
| return false; | ||
| } | ||
| if (normalized_vertices.size() != normals.size()) | ||
| { | ||
| return false; | ||
| } | ||
|
|
||
| const int point_count = static_cast<int>(normalized_vertices.size()); | ||
| rotational_normals->assign(normalized_vertices.size(), glm::fvec3(0.0f)); | ||
| rotational_returns->assign(normalized_vertices.size(), 0.0f); | ||
|
|
||
| if (point_count == 0) | ||
| { | ||
| return true; | ||
| } | ||
|
|
||
| std::vector<float> vertices_flat(static_cast<std::size_t>(point_count) * 3u); | ||
| std::vector<float> normals_flat(static_cast<std::size_t>(point_count) * 3u); | ||
| for (int i = 0; i < point_count; ++i) | ||
| { | ||
| vertices_flat[3 * i + 0] = normalized_vertices[static_cast<std::size_t>(i)].x; | ||
| vertices_flat[3 * i + 1] = normalized_vertices[static_cast<std::size_t>(i)].y; | ||
| vertices_flat[3 * i + 2] = normalized_vertices[static_cast<std::size_t>(i)].z; | ||
| normals_flat[3 * i + 0] = normals[static_cast<std::size_t>(i)].x; | ||
| normals_flat[3 * i + 1] = normals[static_cast<std::size_t>(i)].y; | ||
| normals_flat[3 * i + 2] = normals[static_cast<std::size_t>(i)].z; | ||
| } | ||
|
|
||
| float *d_vertices = nullptr; | ||
| float *d_normals = nullptr; | ||
| float *d_rot_normals = nullptr; | ||
| float *d_rot_returns = nullptr; | ||
|
|
||
| const std::size_t vector_bytes = vertices_flat.size() * sizeof(float); | ||
| const std::size_t return_bytes = rotational_returns->size() * sizeof(float); | ||
|
|
||
| if (!isCudaSuccess(cudaMalloc(&d_vertices, vector_bytes)) || | ||
| !isCudaSuccess(cudaMalloc(&d_normals, vector_bytes)) || | ||
| !isCudaSuccess(cudaMalloc(&d_rot_normals, vector_bytes)) || | ||
| !isCudaSuccess(cudaMalloc(&d_rot_returns, return_bytes))) | ||
| { | ||
| cudaFree(d_vertices); | ||
| cudaFree(d_normals); | ||
| cudaFree(d_rot_normals); | ||
| cudaFree(d_rot_returns); | ||
| return false; | ||
| } | ||
|
|
||
| if (!isCudaSuccess(cudaMemcpy(d_vertices, vertices_flat.data(), vector_bytes, cudaMemcpyHostToDevice)) || | ||
| !isCudaSuccess(cudaMemcpy(d_normals, normals_flat.data(), vector_bytes, cudaMemcpyHostToDevice))) | ||
| { | ||
| cudaFree(d_vertices); | ||
| cudaFree(d_normals); | ||
| cudaFree(d_rot_normals); | ||
| cudaFree(d_rot_returns); | ||
| return false; | ||
| } | ||
|
|
||
| const int threads = 256; | ||
| const int blocks = (point_count + threads - 1) / threads; | ||
| computeRotationalFeaturesKernel<<<blocks, threads>>>( | ||
| d_vertices, | ||
| d_normals, | ||
| theta_for_return_radians, | ||
| d_rot_normals, | ||
| d_rot_returns, | ||
| point_count); | ||
|
|
||
| if (!isCudaSuccess(cudaGetLastError()) || !isCudaSuccess(cudaDeviceSynchronize())) | ||
| { | ||
| cudaFree(d_vertices); | ||
| cudaFree(d_normals); | ||
| cudaFree(d_rot_normals); | ||
| cudaFree(d_rot_returns); | ||
| return false; | ||
| } | ||
|
|
||
| std::vector<float> rot_normals_flat(vertices_flat.size()); | ||
| std::vector<float> rot_returns_flat(rotational_returns->size()); | ||
|
|
||
| if (!isCudaSuccess(cudaMemcpy(rot_normals_flat.data(), d_rot_normals, vector_bytes, cudaMemcpyDeviceToHost)) || | ||
| !isCudaSuccess(cudaMemcpy(rot_returns_flat.data(), d_rot_returns, return_bytes, cudaMemcpyDeviceToHost))) | ||
| { | ||
| cudaFree(d_vertices); | ||
| cudaFree(d_normals); | ||
| cudaFree(d_rot_normals); | ||
| cudaFree(d_rot_returns); | ||
| return false; | ||
| } | ||
|
|
||
| cudaFree(d_vertices); | ||
| cudaFree(d_normals); | ||
| cudaFree(d_rot_normals); | ||
| cudaFree(d_rot_returns); | ||
|
|
||
| for (int i = 0; i < point_count; ++i) | ||
| { | ||
| (*rotational_normals)[static_cast<std::size_t>(i)] = glm::fvec3( | ||
| rot_normals_flat[3 * i + 0], | ||
| rot_normals_flat[3 * i + 1], | ||
| rot_normals_flat[3 * i + 2]); | ||
| (*rotational_returns)[static_cast<std::size_t>(i)] = rot_returns_flat[static_cast<std::size_t>(i)]; | ||
| } | ||
|
|
||
| return true; | ||
| } |
There was a problem hiding this comment.
현재 CUDA 메모리 관리는 수동으로 cudaFree를 호출하여 처리하고 있습니다. 이는 올바르게 동작하지만, 여러 에러 경로에서 cudaFree를 반복적으로 호출해야 하므로 코드가 길어지고 오류가 발생하기 쉽습니다. RAII(Resource Acquisition Is Initialization) 패턴을 사용하면 코드를 더 안전하고 간결하게 만들 수 있습니다. 예를 들어, std::unique_ptr와 커스텀 deleter를 사용하면 메모리 해제를 자동화할 수 있습니다.
이렇게 하면 함수가 어떤 경로로든 종료될 때 (예: 조기 반환 또는 예외) 메모리가 자동으로 해제되어 코드의 안정성과 유지보수성이 향상됩니다.
#include <memory>
struct CudaDeleter {
void operator()(void* p) const {
cudaFree(p);
}
};
// ...
float *d_vertices_raw = nullptr;
if (!isCudaSuccess(cudaMalloc((void**)&d_vertices_raw, vector_bytes))) { return false; }
std::unique_ptr<float, CudaDeleter> d_vertices(d_vertices_raw);
// ... 다른 포인터에 대해서도 동일하게 적용 ...
User description
PR Type
Enhancement, Bug fix, Documentation
Description
C++17 리팩토링: 레거시 코드를 모듈화되고 가독성 있는 구조로 재작성
NSS/DNSS에 옵션 구조체 추가 및 CUDA 지원 구현 (rotational feature 계산)
입력 유효성 검사 및 누락된 메서드(
setInputCloud) 구현README 및 CMakeLists.txt 업데이트로 빌드 및 사용 가이드 강화
Changes walkthrough 📝
NSS.cpp
NSS/DNSS 핵심 알고리즘 및 유틸리티 재작성NSS.cpp
sphericalBucketIndex,computeCenteredAndScaledVertices등 정적 유틸리티 함수 구현computeRotationalReturnValue수식을 명확하게 재작성하고 안정성 개선normalSpaceSampling및dualNormalSpaceSampling알고리즘을 레거시 병렬 코드 대신 효율적인버킷 기반 전략으로 교체
DNSSComputeRotationalFeaturesCuda선언 추가NSS.h
헤더 구조 및 옵션 인터페이스 개선NSS.h
Options구조체 추가로 설정 가능 파라미터화 (azimuth/z bins, random_seed,deterministic 등)
DNSS::Options에 CUDA 및 rotational bucket 초기화 옵션 추가dnss_cuda.cu
DNSS CUDA 가속 구현dnss_cuda.cu
CMakeLists.txt
CUDA 빌드 옵션 및 설정 추가CMakeLists.txt
pr-agent.yml
PR 리뷰 설정 조정.github/workflows/pr-agent.yml
README.md
문서화 및 빌드 가이드 강화README.md