From 27009b3d8718b67374bbc0fea8d3731ae284249d Mon Sep 17 00:00:00 2001 From: Dobromir Popov Date: Sat, 6 Sep 2025 11:41:40 +0300 Subject: [PATCH] rockm compile success --- rin/miner/Dockerfile.rocm-complete | 1 + rin/miner/Dockerfile.rocm-lightweight | 1 + rin/miner/Dockerfile.rocm-official | 1 + rin/miner/build-complete-system.sh | 1 + rin/miner/build-cpuminer-rocm.sh | 1 + rin/miner/build-cpuminer-simple.sh | 1 + rin/miner/build-rocm-complete.sh | 1 + rin/miner/build-rocm-lightweight.sh | 1 + rin/miner/build-rocm-official.sh | 1 + rin/miner/gpu/CMakeLists.txt | 1 + rin/miner/gpu/RinHash-hip/argon2d_device.cuh | 4 +- rin/miner/gpu/rinhash-gpu-miner.cpp | 60 ++++++++++++------- rin/miner/rinhash-gpu-miner | Bin 0 -> 27944 bytes 13 files changed, 49 insertions(+), 25 deletions(-) create mode 100644 rin/miner/rinhash-gpu-miner diff --git a/rin/miner/Dockerfile.rocm-complete b/rin/miner/Dockerfile.rocm-complete index f6e268c..c10332a 100644 --- a/rin/miner/Dockerfile.rocm-complete +++ b/rin/miner/Dockerfile.rocm-complete @@ -92,3 +92,4 @@ echo "Available algorithms:"\n\ # Default command CMD ["sh", "-c", "echo 'Build completed successfully! Binaries available in /output/' && ls -la /output/"] + diff --git a/rin/miner/Dockerfile.rocm-lightweight b/rin/miner/Dockerfile.rocm-lightweight index 8aae456..6279b67 100644 --- a/rin/miner/Dockerfile.rocm-lightweight +++ b/rin/miner/Dockerfile.rocm-lightweight @@ -92,3 +92,4 @@ ls -la *.so\n\ # Default command CMD ["sh", "-c", "echo 'ROCm GPU build completed successfully!' && ls -la /output/"] + diff --git a/rin/miner/Dockerfile.rocm-official b/rin/miner/Dockerfile.rocm-official index fd3726f..339160e 100644 --- a/rin/miner/Dockerfile.rocm-official +++ b/rin/miner/Dockerfile.rocm-official @@ -128,3 +128,4 @@ rinhash_cuda(input, input_len, output);\n\ # Default command CMD ["sh", "-c", "echo 'ROCm GPU build completed successfully!' && ls -la /output/ && echo '' && echo 'Integration guide available in INTEGRATION.md'"] + diff --git a/rin/miner/build-complete-system.sh b/rin/miner/build-complete-system.sh index 56e5ced..cc6c58a 100644 --- a/rin/miner/build-complete-system.sh +++ b/rin/miner/build-complete-system.sh @@ -293,3 +293,4 @@ echo " 3. For GPU acceleration, install ROCm runtime and use the libraries" echo "" echo "RinHash ROCm GPU build system completed successfully!" + diff --git a/rin/miner/build-cpuminer-rocm.sh b/rin/miner/build-cpuminer-rocm.sh index b2c0392..5c7bc05 100644 --- a/rin/miner/build-cpuminer-rocm.sh +++ b/rin/miner/build-cpuminer-rocm.sh @@ -200,3 +200,4 @@ else fi echo "cpuminer build completed successfully!" + diff --git a/rin/miner/build-cpuminer-simple.sh b/rin/miner/build-cpuminer-simple.sh index 6ea59cb..06410b0 100644 --- a/rin/miner/build-cpuminer-simple.sh +++ b/rin/miner/build-cpuminer-simple.sh @@ -223,3 +223,4 @@ else fi echo "cpuminer build completed successfully!" + diff --git a/rin/miner/build-rocm-complete.sh b/rin/miner/build-rocm-complete.sh index 19d5581..705f325 100644 --- a/rin/miner/build-rocm-complete.sh +++ b/rin/miner/build-rocm-complete.sh @@ -114,3 +114,4 @@ fi echo "ROCm GPU build completed successfully!" + diff --git a/rin/miner/build-rocm-lightweight.sh b/rin/miner/build-rocm-lightweight.sh index 18b0125..66bac41 100644 --- a/rin/miner/build-rocm-lightweight.sh +++ b/rin/miner/build-rocm-lightweight.sh @@ -111,3 +111,4 @@ fi echo "ROCm GPU build completed successfully!" + diff --git a/rin/miner/build-rocm-official.sh b/rin/miner/build-rocm-official.sh index 1d7a0ba..d12c122 100644 --- a/rin/miner/build-rocm-official.sh +++ b/rin/miner/build-rocm-official.sh @@ -146,3 +146,4 @@ echo "" echo "For more information about ROCm Docker containers, visit:" echo "https://github.com/ROCm/ROCm-docker" + diff --git a/rin/miner/gpu/CMakeLists.txt b/rin/miner/gpu/CMakeLists.txt index 5699618..64b140c 100644 --- a/rin/miner/gpu/CMakeLists.txt +++ b/rin/miner/gpu/CMakeLists.txt @@ -30,3 +30,4 @@ target_compile_options(rinhash-gpu-miner PRIVATE -O3 -march=native) # Install target install(TARGETS rinhash-gpu-miner DESTINATION bin) + diff --git a/rin/miner/gpu/RinHash-hip/argon2d_device.cuh b/rin/miner/gpu/RinHash-hip/argon2d_device.cuh index 206c2ed..635f5af 100644 --- a/rin/miner/gpu/RinHash-hip/argon2d_device.cuh +++ b/rin/miner/gpu/RinHash-hip/argon2d_device.cuh @@ -1,5 +1,5 @@ -#include -#include +#include +#include #include #include #include diff --git a/rin/miner/gpu/rinhash-gpu-miner.cpp b/rin/miner/gpu/rinhash-gpu-miner.cpp index ffe5452..097a42f 100644 --- a/rin/miner/gpu/rinhash-gpu-miner.cpp +++ b/rin/miner/gpu/rinhash-gpu-miner.cpp @@ -7,13 +7,14 @@ #include #include #include +#include // HIP/ROCm runtime check (using dlopen, no direct headers needed) // Forward declarations for GPU functions extern "C" { - void rinhash_cuda(const uint8_t* input, size_t input_len, uint8_t* output); - void rinhash_cuda_batch(const uint8_t* block_headers, size_t block_header_len, + void rinhash_hip(const uint8_t* input, size_t input_len, uint8_t* output); + void rinhash_hip_batch(const uint8_t* block_headers, size_t block_header_len, uint8_t* outputs, uint32_t num_blocks); void RinHash(const uint32_t* version, const uint32_t* prev_block, const uint32_t* merkle_root, const uint32_t* timestamp, @@ -26,8 +27,8 @@ private: bool gpu_available; // Function pointers for GPU operations - decltype(&rinhash_cuda) gpu_rinhash; - decltype(&rinhash_cuda_batch) gpu_rinhash_batch; + decltype(&rinhash_hip) gpu_rinhash; + decltype(&rinhash_hip_batch) gpu_rinhash_batch; decltype(&RinHash) gpu_RinHash; // Mining parameters @@ -61,30 +62,38 @@ public: } bool loadGPULibrary() { - // Try to load the GPU library - gpu_lib_handle = dlopen("./rocm-direct-output/gpu-libs/librinhash_hip.so", RTLD_LAZY); - if (!gpu_lib_handle) { - std::cerr << "Failed to load GPU library: " << dlerror() << std::endl; - std::cerr << "Make sure to run: sudo cp rocm-direct-output/gpu-libs/librinhash_hip.so /usr/local/lib/" << std::endl; - return false; - } + // Try to load the GPU library + std::cout << "Attempting to load GPU library..." << std::endl; + gpu_lib_handle = dlopen("./rocm-direct-output/gpu-libs/librinhash_hip.so", RTLD_LAZY); + if (!gpu_lib_handle) { + std::cerr << "Failed to load GPU library: " << dlerror() << std::endl; + std::cerr << "Make sure to run: sudo cp rocm-direct-output/gpu-libs/librinhash_hip.so /usr/local/lib/" << std::endl; + std::cerr << "Current working directory: " << std::filesystem::current_path() << std::endl; + return false; + } + std::cout << "GPU library loaded successfully!" << std::endl; - // Load function pointers - gpu_rinhash = (decltype(gpu_rinhash))dlsym(gpu_lib_handle, "rinhash_cuda"); - gpu_rinhash_batch = (decltype(gpu_rinhash_batch))dlsym(gpu_lib_handle, "rinhash_cuda_batch"); - gpu_RinHash = (decltype(gpu_RinHash))dlsym(gpu_lib_handle, "RinHash"); + // Load function pointers + std::cout << "Loading GPU functions..." << std::endl; + gpu_rinhash = (decltype(gpu_rinhash))dlsym(gpu_lib_handle, "rinhash_hip"); + gpu_rinhash_batch = (decltype(gpu_rinhash_batch))dlsym(gpu_lib_handle, "rinhash_hip_batch"); + gpu_RinHash = (decltype(gpu_RinHash))dlsym(gpu_lib_handle, "RinHash"); - if (!gpu_rinhash || !gpu_rinhash_batch || !gpu_RinHash) { - std::cerr << "Failed to load GPU functions: " << dlerror() << std::endl; - dlclose(gpu_lib_handle); - gpu_lib_handle = nullptr; - return false; - } + if (!gpu_rinhash) std::cerr << "Failed to load rinhash_hip" << std::endl; + if (!gpu_rinhash_batch) std::cerr << "Failed to load rinhash_hip_batch" << std::endl; + if (!gpu_RinHash) std::cerr << "Failed to load RinHash" << std::endl; + + if (!gpu_rinhash || !gpu_rinhash_batch || !gpu_RinHash) { + std::cerr << "Failed to load GPU functions: " << dlerror() << std::endl; + dlclose(gpu_lib_handle); + gpu_lib_handle = nullptr; + return false; + } + std::cout << "GPU functions loaded successfully!" << std::endl; // GPU availability will be verified by successful library loading // and function calls working properly - std::cout << "GPU library loaded successfully!" << std::endl; std::cout << "GPU functions ready for mining" << std::endl; gpu_available = true; @@ -149,7 +158,12 @@ public: } // Process batch on GPU - gpu_rinhash_batch(block_headers.data(), block_header_len, hashes.data(), num_nonces); + if (gpu_rinhash_batch) { + gpu_rinhash_batch(block_headers.data(), block_header_len, hashes.data(), num_nonces); + } else { + std::cerr << "GPU batch function not available" << std::endl; + return false; + } hashes_computed += num_nonces; // Check results diff --git a/rin/miner/rinhash-gpu-miner b/rin/miner/rinhash-gpu-miner new file mode 100644 index 0000000000000000000000000000000000000000..58dca7e34f58f5b5c89e391e926ce3b52fead164 GIT binary patch literal 27944 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 literal 0 HcmV?d00001