CUDA Cpp正电子发射断层扫描仪校准和图像重建—蒙特卡洛3D伊辛模型

要点

  1. GPU对比CPU计算正弦和:使用单CPU、使用OpenMP库和CUDA
  2. CUDA并行计算:3D网格运行内核:线程块,线程线性处理3D数组,并行归约,共享内存,矩阵乘法/平铺矩阵乘法,基本线性代数子程序
  3. 平铺分区,矢量加载,warp级内在函数和子 warp,线程发散和同步,联合组
  4. 使用 2D 和 3D 模板,迭代求解偏微分方程和图像处理
  5. 使用 GPU 纹理硬件执行快速插值,图像配准
  6. 蒙特卡洛模拟3D伊辛模型
  7. CUDA 流
  8. CUDA正电子发射断层扫描仪校准和图像重建
  9. GPU扩展

矩阵乘法示例

假设我们有两个矩阵, A A A B B B。假设 A A A是一个 n × m n \times m n×m矩阵,这意味着它有 n n n行和 m m m列。还假设 B B B m × w m \times w m×w 矩阵。乘法 A ∗ B A * B AB(与 B ∗ A B * A BA不同)的结果是一个 n × w n \times w n×w矩阵,我们称之为 M M M。也就是说,结果矩阵的行数等于第一个矩阵 A A A 的行数和第二个矩阵 B B B​ 的列数。

为什么会发生这种情况以及它是如何运作的?这里两个问题的答案是相同的。我们以 M M M 的单元格 1,1(第一行,第一列)为例。运算 M = A ∗ B M=A * B M=AB 后其中的数字是 A A A 第 1 行中的数字与 B B B 第 1 列中的数字的所有逐元素乘法之和。也就是说,在 M M M 的单元格 i , j i, j i,j 中,我们得到了 A A A 中第 i i i 行和 B B B 中第 j j j​ 列中所有数字的逐元素乘法之和。

下图直观地解释了这个想法:

现在应该很清楚为什么矩阵-矩阵乘法是并行计算的一个很好的例子了。我们必须计算 C C C 中的每个元素,并且每个元素彼此独立,因此我们可以有效地并行化。

我们将看到实现这一目标的不同方法。我们的目标是在本文中添加新概念,最终形成一个 2D 内核,它使用共享内存来有效地优化操作。

网格和块

当我们使用指令 <<< >>> 调用内核时,我们自动定义一个 dim3 类型变量,定义每个网格的块数和每个块的线程数。

事实上,网格和块分别是块和线程的 3D 数组。当我们在调用内核之前定义它们时,这一点很明显,如下所示:

dim3 blocksPerGrid(512, 1, 1)
dim3 threadsPerBlock(512, 1, 1)
kernel<<<blocksPerGrid, threadsPerBlock>>>()

当我们现在处理矩阵时,我们想要指定第二个维度(同样,我们可以省略第三个维度)。这对于使线程正常工作非常有用,有时甚至是必不可少的。

事实上,通过这种方式,我们可以按照前面示例中所遵循的相同方式来引用 x x x y y y​ 轴。 我们看一下代码:

 int row = blockIdx.y * blockDim.y + threadIdx.y;int col = blockIdx.x * blockDim.x + threadIdx.x;

正如您所看到的,它们的代码相似。 在 CUDA 中,blockIdx、blockDim 和 threadIdx 是具有成员 x、y 和 z 的内置函数。 它们在 C 中被索引为法线向量,因此介于 0 和最大数减 1 之间。例如,如果我们的网格维度为blocksPerGrid = (512, 1, 1),则 blockIdx.x 的范围将在 0 到 511 之间。

使用多维块意味着您必须小心地在所有维度之间分配这个数量的线程。 在 1D 块中,x 轴最多可以设置 1024 个线程,但在 2D 块中,如果将 y 的大小设置为 2,则 x 不能超过 512! 例如,允许使用dim3threadsPerBlock(1024, 1, 1),以及dim3threadsPerBlock(512, 2, 1),但不允许使用dim3threadsPerBlock(256, 3, 2)。

内核

现在我们已经有了执行任务所需的所有信息,让我们看一下内核代码。为了简单起见,我们将在示例中使用方阵 N × N N \times N N×N 矩阵。

正如我们已经看到的,要做的第一件事是确定 x x x y y y 轴索引(即行号和列号):

__global__ void multiplication(float *A, float* B, float *C, int N){int ROW = blockIdx.y*blockDim.y+threadIdx.y;int COL = blockIdx.x*blockDim.x+threadIdx.x;

代码

矩阵类

#include <stdexcept>
#include <algorithm>
#include <cuda_runtime.h>template <class T>
class dev_array
{
// public functions
public:explicit dev_array(): start_(0),end_(0){}// constructorexplicit dev_array(size_t size){allocate(size);}// destructor~dev_array(){free();}// resize the vectorvoid resize(size_t size){free();allocate(size);}// get the size of the arraysize_t getSize() const{return end_ - start_;}// get dataconst T* getData() const{return start_;}T* getData(){return start_;}// setvoid set(const T* src, size_t size){size_t min = std::min(size, getSize());cudaError_t result = cudaMemcpy(start_, src, min * sizeof(T), cudaMemcpyHostToDevice);if (result != cudaSuccess){throw std::runtime_error("failed to copy to device memory");}}// getvoid get(T* dest, size_t size){size_t min = std::min(size, getSize());cudaError_t result = cudaMemcpy(dest, start_, min * sizeof(T), cudaMemcpyDeviceToHost);if (result != cudaSuccess){throw std::runtime_error("failed to copy to host memory");}}// private functions
private:// allocate memory on the devicevoid allocate(size_t size){cudaError_t result = cudaMalloc((void**)&start_, size * sizeof(T));if (result != cudaSuccess){start_ = end_ = 0;throw std::runtime_error("failed to allocate device memory");}end_ = start_ + size;}// free memory on the devicevoid free(){if (start_ != 0){cudaFree(start_);start_ = end_ = 0;}}T* start_;T* end_;
};#endif

矩阵乘法处理

#include <stdexcept>
#include <algorithm>
#include <cuda_runtime.h>template <class T>
class dev_array
{
// public functions
public:explicit dev_array(): start_(0),end_(0){}// constructorexplicit dev_array(size_t size){allocate(size);}// destructor~dev_array(){free();}// resize the vectorvoid resize(size_t size){free();allocate(size);}// get the size of the arraysize_t getSize() const{return end_ - start_;}// get dataconst T* getData() const{return start_;}T* getData(){return start_;}// setvoid set(const T* src, size_t size){size_t min = std::min(size, getSize());cudaError_t result = cudaMemcpy(start_, src, min * sizeof(T), cudaMemcpyHostToDevice);if (result != cudaSuccess){throw std::runtime_error("failed to copy to device memory");}}// getvoid get(T* dest, size_t size){size_t min = std::min(size, getSize());cudaError_t result = cudaMemcpy(dest, start_, min * sizeof(T), cudaMemcpyDeviceToHost);if (result != cudaSuccess){throw std::runtime_error("failed to copy to host memory");}}// private functions
private:// allocate memory on the devicevoid allocate(size_t size){cudaError_t result = cudaMalloc((void**)&start_, size * sizeof(T));if (result != cudaSuccess){start_ = end_ = 0;throw std::runtime_error("failed to allocate device memory");}end_ = start_ + size;}// free memory on the devicevoid free(){if (start_ != 0){cudaFree(start_);start_ = end_ = 0;}}T* start_;T* end_;
};#endif

内核头文件

内核CUDA处理

正电子发射断层扫描

临床正电子发射断层扫描的工作原理是,首先向受试者注射含有短寿命放射性同位素(例如 18F)的药物,该同位素会因发射正电子而衰变。 发射的正电子将与受试者组织中附近的电子湮灭,产生一对背靠背的伽马射线,这可以在周围的伽马探测器阵列中作为巧合事件进行检测。

PET 扫描仪中使用的典型探测器由光电倍增管观察的闪烁晶体组成。 晶体吸收伽马射线的能量并将其转换为可见光范围内的光子,进而在光电倍增管中产生信号。 重合事件中的两个探测器定义了一条响应线,衰变事件沿着这条响应线发生。 较旧的 PET 探测器的时间分辨率为几纳秒,足以找到真正湮没事件之间的重合,同时拒绝大多数其他背景来源。 然而,这个时间并不足以提供有关特定衰变事件发生在响应线上的位置的任何有用信息。 临床 PET 扫描可能涉及采集数十亿个 LOR,因此可以通过统计分析来重建受试者体内最可能的活动分布。 重建的活动通常显示为 3D 笛卡尔体素网格,其中 x − y x-y xy 平面位于扫描仪的横向平面中,z 轴与探测器形成的圆柱体的轴对齐。

一种流行的图像重建算法是最大似然期望最大化方法,如果每个响应线中观察到的衰变数量遵循基础放射性衰变过程的泊松分布,则该算法在理论上是最佳的。

最大似然期望最大化方法是迭代的,并使用如下方程式中所示的看似简单的公式:
a v n + 1 = a v n ∑ l ′ = 1 N l S v l ′ ∑ l = 1 N l m l S v l ∑ v ′ = 1 N v a v ′ n S v ′ l a_v^{n+1}=\frac{a_v^n}{\sum_{l^{\prime}=1}^{N_l} S_{v l^{\prime}}} \sum_{l=1}^{N_l} \quad \frac{m_l S_{v l}}{\sum_{v^{\prime}=1}^{N_v} a_{v^{\prime}}^n S_{v^{\prime} l}} avn+1=l=1NlSvlavnl=1Nlv=1NvavnSvlmlSvl
其中 a v n a_v^n avn 是迭代 $n 时体素 v v v 的估计活动,m_l$ 是响应线 l l l 中测量的衰减数, S v l S_{v l} Svl 是 PET 系统矩阵 (SM)。 SM 元素 S v l S_{v l} Svl 定义为在响应线 l l l 中检测到体素 v v v 衰减的概率。 迭代通常是通过为所有 a v 0 a_v^0 av0 分配相等的值来开始的。 总和限值 N l N_l Nl N v N_v Nv 是涉及的响应线和体素的总数。

伊辛模型

该模型在固态物理学中众所周知,涉及在任意维数的等间隔网格上排列的自旋二分之一粒子的演化。 在这里我们将讨论适用于实体的 3D 网格。 在每个网格点,我们放置一个自旋为 S 的对象,其中 S 可以为 ± 1 \pm 1 ±1。 每个自旋都受到其邻居的作用,使得平行自旋的能量低于反平行自旋,并且自旋网格位置 ( x , y , z ) (x, y, z) (x,y,z)​ 的最终能量可以取为:
E = J ( S x − 1 , y , z + S x + 1 , y , z + S x , y − 1 , z + S x , y + 1 , z + S x , y , z − 1 + S x , y , z + 1 ) S x , y , z E=J\left(S_{x-1, y, z}+S_{x+1, y, z}+S_{x, y-1, z}+S_{x, y+1, z}+S_{x, y, z-1}+S_{x, y, z+1}\right) S_{x, y, z} E=J(Sx1,y,z+Sx+1,y,z+Sx,y1,z+Sx,y+1,z+Sx,y,z1+Sx,y,z+1)Sx,y,z
此式是最简单的伊辛模型,我们只考虑最近邻居之间的相互作用。 常数 J J J 测量自旋-自旋相互作用的强度,在我们的模拟中我们将其设置为 1。 请注意,如果大多数相邻自旋与 S x , y , z S_{x, y, z} Sx,y,z 平行,则 E E E 将为正;如果它们大多反平行,则 E E E 将为负。 在模拟中,我们测试单个任意旋转并根据框中显示的标准翻转它。

蒙特卡洛

蒙特卡洛方法的科学应用一直很重要,并且随着计算能力的增长和可用性,其应用的重要性不断增长。 如今,它们已成为许多科学领域的重要工具。 简单来说,这些应用程序可以分为两类: (a) 积分,在其域中的随机点上对某些函数进行采样;(b) 模拟,使用随机数来模拟随机过程来研究某些物理系统或实验设备的行为。 好的随机数生成器是计算机在科学领域的许多应用的重要工具。

CUDA Cpp并行计算编程

参阅一:亚图跨际
参阅二:亚图跨际

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/665869.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Javaweb之SpringBootWeb案例之 @ConfigurationProperties的详细解析

4.3 ConfigurationProperties 讲解完了yml配置文件之后&#xff0c;最后再来介绍一个注解ConfigurationProperties。在介绍注解之前&#xff0c;我们先来看一个场景&#xff0c;分析下代码当中可能存在的问题&#xff1a; 我们在application.properties或者application.yml中配…

图论练习2

内容&#xff1a;路径计数DP&#xff0c;差分约束 最短路计数 题目大意 给一个个点条边的无向无权图&#xff0c;问从出发到其他每个点的最短路有多少条有自环和重边&#xff0c;对答案 解题思路 设边权为1&#xff0c;跑最短路 表示的路径数自环和重边不影…

WPS Office18.7软件日常更新

【应用名称】&#xff1a;WPS Office 【适用平台】&#xff1a;#Android 【软件标签】&#xff1a;#WPS 【应用版本】&#xff1a;18.6.1➡18.7 【应用大小】&#xff1a;160MB 【软件说明】&#xff1a;软件日常更新。WPS Office是使用人数最多的移动办公软件。独有手机阅读模…

正点原子--STM32定时器学习笔记(1)

这部分是笔者对基本定时器的理论知识进行学习与总结&#xff01;&#xff0c;主要记录自己在学习过程中遇到的重难点&#xff0c;其他一些基础点就一笔带过了&#xff01; 1. 定时器概述 1.1 软件定时原理 使用纯软件&#xff08;CPU死等&#xff09;的方式实现定时&#xf…

GetBuffer() 与 ReleaseBuffer() 使用详解

GetBuffer() 与 ReleaseBuffer() 使用详解 大家好&#xff0c;我是免费搭建查券返利机器人赚佣金就用微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01;今天&#xff0c;我们将深入研究在编程中常用的GetBuffer()与ReleaseBuff…

机器学习_15_贝叶斯算法

文章目录 1 贝叶斯定理相关公式2 朴素贝叶斯算法2.1 朴素贝叶斯算法推导2.2 朴素贝叶斯算法流程 3 高斯朴素贝叶斯4 伯努利朴素贝叶斯5 多项式朴素贝叶斯6 贝叶斯网络6.1 最简单的一个贝叶斯网络6.2 全连接贝叶斯网络6.3 “正常”贝叶斯网络6.4 实际贝叶斯网络&#xff1a;判断…

算法学习——华为机考题库5(HJ31 - HJ35)

算法学习——华为机考题库5&#xff08;HJ31 - HJ35&#xff09; HJ31 单词倒排 描述 对字符串中的所有单词进行倒排。 说明&#xff1a; 1、构成单词的字符只有26个大写或小写英文字母&#xff1b; 2、非构成单词的字符均视为单词间隔符&#xff1b; 3、要求倒排后的单…

LeAPI 后端接口开发 - 发布、下线接口

一、上线接口&#xff08;仅管理员&#xff09; 1. 校验请求参数 2. 判断&#xff08;测试&#xff09;接口是否可以调用 引入调用接口的客户端&#xff08;自己写的 SDK&#xff09;注入客户端实例调用接口 3. 修改数据库中接口的状态 /*** 上线&#xff08;发布&#xff…

机器视觉培训机构的保就业可信吗?就业不了退款是真的吗?

显然是不可能的。某些机器视觉培训机构为了取信于人&#xff0c;请来保险公司再出一份保单&#xff0c;对学员未来的就业薪资承保&#xff0c;如在某机器视觉培训机构培训后就业薪资低于12000元&#xff0c;由某保险公司理赔学员全部培训费用。这种取信于人的操作&#xff0c;我…

爬虫(二)

1.同步获取短视频 1.只要播放地址对Json数据解析&#xff0c;先把列表找出&#xff1a; 2.只想要所有的播放地址&#xff0c;通过列表表达式循环遍历这个列表拿到每个对象&#xff0c;再从一个个对象里面找到Video,再从Video里面找到播放地址(play_addr),再从播放地址找到播放…

动态内存管理 智能指针 shared_ptr、unique_ptr、weak_ptr + 定制删除器

动态内存管理常出现的两种问题&#xff1a; 1.忘记释放内存,造成内存泄漏 2.这块内存还有其他指针指向的情况下&#xff0c;就释放了它&#xff0c;会产生引用非法内存的指针&#xff0c;例如 如果类中有属性指向堆区&#xff0c;做赋值操作时会出现浅拷贝的问题 内存泄漏分…

在jetbrains IDEA/Pycharm/Android Studio中安装官方rust插件,开始rust编程

在idea插件市场搜索rust&#xff1a;JetBrains Marketplace &#xff0c;就可以找到rust插件&#xff1a; jetbrains官方rust插件地址&#xff1a;[Deprecated] Rust - IntelliJ IDEs Plugin | Marketplace 直接在idea中搜索rust好像是搜不到的&#xff1a; 需要在这个插件市场…

【数据结构】二叉树链式结构的实现

简单不先于复杂&#xff0c;而是在复杂之后。 文章目录 1. 二叉树链式结构的实现1.1 前置说明1.2 二叉树的遍历1.2.1 前序、中序以及后序遍历1.2.2 层序遍历 1.3 节点个数以及高度等1.4 二叉树基础oj练习1.5 二叉树的创建和销毁 1. 二叉树链式结构的实现 1.1 前置说明 在学习二…

Cambalache in Ubuntu

文章目录 前言apt install flatpak这很ok后记 前言 gtkmm4相比gtkmm3有很多改革, 代码也干净了许多, 但在windows上开发 有ui设计器那自然方便很多, 但glade又不支持gtkmm4, windows上装Cambalache很是困难. 各种问题都找不到答案.于是 我用VMware虚拟机Ubuntu20.xx安装Cambal…

macOS虚拟机安装全过程的详细教程

macOS虚拟机安装全过程的详细教程 一、安装虚拟机软件 选择软件&#xff1a;首先&#xff0c;你需要选择一个适合macOS的虚拟机软件。在本教程中&#xff0c;我们以VirtualBox为例。下载与安装&#xff1a;访问VirtualBox的官网&#xff0c;下载适用于macOS的安装包。运行安装…

【leetcode热题100】颜色分类

难度&#xff1a; 中等通过率&#xff1a; 40.7%题目链接&#xff1a;力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 题目描述 给定一个包含红色、白色和蓝色&#xff0c;一共 n 个元素的数组&#xff0c;原地对它们进行排序&#xff0c;使得相同颜…

高校建设AI算力平台方案探索

近年来&#xff0c;人工智能行业发展迅速&#xff0c;在自动驾驶、金融、医疗、教育等行业广泛应用。尤其是ChatGPT发布以后更是掀起了生成式AI的热潮&#xff0c;国内各大互联网厂商也相继发布自己的AI大模型。这也造成了大量的AI人才缺口&#xff0c;同时促进了高校的AI专业建…

CSP-202305-2-矩阵运算

CSP-202305-2-矩阵运算&#xff1a;题目链接 知识点一&#xff1a;申请矩阵 1.动态分配 // 申请 int** dynamicArray new int*[rows]; for (int i 0; i < rows; i) {dynamicArray[i] new int[cols]; }// 释放 for (int i 0; i < rows; i) {delete[] dynamicArray[…

有哪些流行的中文开源语言模型?

支持中文的流行开源语言模型有很多&#xff0c;这些模型在自然语言处理领域的中文任务上表现出色&#xff0c;包括文本分类、情感分析、机器翻译、问答系统等。以下是一些支持中文的流行开源语言模型&#xff1a; 1. **BERT-Base, Chinese**&#xff1a;Google发布的BERT模型的…

【Linux】【Shell】常用压缩和解压缩命令(超详细)

目录 1. 指令&#xff1a; 1.1 tar 1.2 gz、.tar.gz 1.3 .bz2、.tar.bz2、.bz 1.4 .z、.tar.z 1.5 .zip 1.6 .rar 1.7 lzop 2. 示例&#xff1a; 1. 指令&#xff1a; 快速压缩&#xff1a;XZ_DEFAULTS"-T0" tar cJvf xxxxx.tar.xz sourcefile&#xff08;压…