>

在 HPC 集群上复现 RetinaSim

最近,我接手了一项颇具挑战性的任务:在一台高性能计算(HPC)集群上完整复现一篇名为《Physics-informed deep generative learning for quantitative assessment of the retina》的论文,其核心代码库是 RetinaSim。这项工作的目标不仅仅是简单地运行代码,而是要在一个与开发者原始环境可能截然不同的、受严格管理的计算环境中,完整地重现其复杂的软件栈和模拟流程。 我们的战场是一个典型的HPC集群,名为“Barkla2”,它运行着 Rocky Linux 9,并使用 Slurm 作为作业调度器。这意味着所有操作都必须通过命令行完成,并且任何耗时较长的计算任务都必须作为批处理作业提交,而不能直接在登录节点上运行。RetinaSim 项目本身是一个复杂的混合体,它融合了 Python 脚本作为主流程控制,同时依赖于一个用 C++ 编写的高性能流体动力学模拟器(Reanimate)和一个用 .NET(C#)编写的血管生成程序(RetinaGen)。这种异构的技术栈几乎注定了在新的环境中会遇到各种意想不到的编译和运行时问题。 我的第一步是获取代码并制定一个初步的 Slurm 脚本。代码通过 git clone 获取,其目录结构清晰地展示了各个子模块。初步分析 README 和 CMakeLists.txt 等文件后,我了解到编译 Reanimate 需要 CMake 和 C++ 编译器,而 RetinaGen 则需要 .NET SDK。Python 部分则依赖于一份 requirements.txt 文件。 基于这些信息,我编写了第一个版本的 Slurm 脚本。这个脚本的目标是按顺序完成所有准备工作:加载必要的环境模块(如 GCC 编译器、CMake、Python),创建 Python 虚拟环境并安装依赖,然后编译 C++ 和 .NET 子模块,最后尝试运行主 Python 脚本 main.py。这是一个标准的、看似直接的流程,然而,现实很快就给了我第一击。 第一次失败:找不到 CMakeLists.txt 当我提交第一个作业后,几秒钟内它就失败了。检查错误日志,我看到了一个熟悉而又基础的错误信息:CMake Error: The source directory "..." does not appear to contain CMakeLists.txt。这个错误的意思是,CMake 在我指定的目录中没有找到它的核心配置文件 CMakeLists.txt。 ...

十二月 28, 2025 · 4 分钟 · 663 字 · Tategoto Azarasi

Uol 2025 Wk2 题解

Recount 问题描述 最近的学校董事会选举竞争异常激烈:提案内容包括互换中小学的上学时间,一项备受争议的禁止在校穿着运动服的新着装规定,以及一项旨在提高房地产税以资助新橄榄球训练设施的提案,诸如此类的议题层出不穷。现在,距离投票站关闭已过去数小时,但获胜者仍未揭晓! 在绝望之中,选举官员们求助于您,请您编写一个程序来统计选票! 输入 输入包含一个单一的测试用例,即一张已投选票的列表。输入中的每一行都包含一位被投票的候选人的名字。一个名字可能由多个单词组成,以空格分隔。单词包含字母或连字符,但不含其他标点符号。列表中至少有2张选票。选票列表以包含字符 *** 的单行结束。这一行不应被计入票数。最多可以有100,000张有效选票。 输出 如果某位候选人获得了简单多数或绝对多数的选票(即,票数超过任何其他候选人),则输出这位候选人的名字!如果没有候选人获得简单多数,则输出:“ Runoff!”(别忘了包含感叹号!) 代码 #include <iostream> #include <string> #include <unordered_map> #include <algorithm> // using namespace std; // 为简洁起见,在单个文件中可以如此使用 namespace recount { int main(istream &cin, ostream &cout) { // 使用 unordered_map 存储每位候选人的票数。 // 键是候选人的名字(字符串),值是他们的票数(无符号长整型)。 std::unordered_map<std::string, unsigned long> m = std::unordered_map<std::string, unsigned long>(); std::string line; // 逐行读取选票,直到遇到哨兵值 "***"。 while(std::getline(cin, line)) { if(line == "***") { break; } // 为当前行所指的候选人增加票数。 // 如果候选人尚不存在于哈希表中,则会以计票数为1被添加进去。 m[line]++; } // 初始化一个字符串来存放获胜者的名字,以及一个变量用于存放最高票数。 std::string ans = "***"; // 使用哨兵值来检查是否存在平局。 unsigned long max_vote = 0; // 第一遍遍历:找出所有候选人中的最高票数。 for(const auto &[k, v]: m) { max_vote = std::max(max_vote, v); } // 第二遍遍历:找出获得最高票数的候选人。 for(const auto &[k, v]: m) { if(v == max_vote) { // 如果 'ans' 不再是哨兵值,意味着我们已经找到了一个获胜者。 // 此时再找到一个说明存在平局。 if(ans != "***") { cout << "Runoff!"; return 0; // 在打印平局结果后退出。 } // 这是找到的第一个获得最高票数的候选人。 ans = k; } } // 如果循环完成且 'ans' 只被更新过一次,则打印获胜者的名字。 cout << ans; return 0; } } 题解 这个问题要求我们处理一个投票列表,其中每一票都是一个代表候选人姓名的字符串。我们需要找出得票最多的候选人。如果只有一位候选人得票最高,我们就打印他/她的名字。如果有两位或更多的候选人并列获得最高票数,我们必须宣布“Runoff! ”。投票列表以一个特殊的字符串 *** 结尾。 ...

十月 1, 2025 · 6 分钟 · 1145 字 · Tategoto Azarasi

矩阵乘法性能测试:从三重循环到百 GFLOPS (AMD Ryzen AI + Radeon 平台实测)

深度对比11种矩阵乘法实现(从Naive到CPU SIMD、多核、BLAS及GPU加速如OpenCL/HIP/Vulkan)在AMD Ryzen AI + Radeon平台上的巨大性能差异与优化关键。

四月 19, 2025 · 14 分钟 · 2859 字 · Tategoto Azarasi

深入探索 Wasmtime:C++ 与 Rust Wasm 模块的双向通信与内存共享

一篇详细的技术指南,介绍如何使用 Wasmtime 运行时在 C++ 宿主应用程序与 Rust WebAssembly 模块之间实现复杂的双向通信、共享内存访问和结构体传递。

四月 6, 2025 · 15 分钟 · 3053 字 · Tategoto Azarasi