优化三角矩阵计算的 CUDA 内核的执行

2024-04-01

我正在开发我的第一个 Cuda 应用程序,并且我的内核“吞吐量低于预期”,这似乎是目前最大的瓶颈。

内核的任务是计算一个 N × N 大小的矩阵(DD)包含数据矩阵上所有元素之间的平方距离。数据矩阵(Y)的大小为 N × D(以支持多维数据)并存储为行优先。

Source:

__global__ void computeSquaredEuclideanDistance(const float * __restrict__ Y, float * __restrict__ DD, const int N, const int D) {
    int index = blockIdx.x * blockDim.x + threadIdx.x;
    int stride = blockDim.x * gridDim.x;

    for (int i = index; i < N * N; i += stride) {
        const int m = i / N;
        const int n = i % N;
        float tmp = 0;
        for (int d = 0; d < D; ++d) {
            const float Ynd = Y[d + D * n];
            const float Ymd = Y[d + D * m];
            const float Ydiff = Ynd - Ymd;
            tmp += Ydiff * Ydiff;
        }
        DD[n + N * m] = tmp;
    }
}

这是被调用的size_t blockSize = 256 and size_t numBlocks = (N*N + blockSize - 1)/blockSize.

我该如何优化这个内核?我最初的想法是,耗时的部分是在不利用某种共享内存的情况下读取数据,但是任何人都可以给我指导如何解决这个问题吗?

评论来自nvvc分析工具:

  • Latency analysis:
    • 计算利用率约为 40%
    • 内存(二级缓存)利用率约为 35%
  • Occupancy is not an issue
    • 主动扭曲为理论值 64 的 57.59
    • 理论入住率 100 的 90%

对于我的应用程序,典型值为:

  • 5k < N
  • D是 2 或 3

我通常会忽略这些类型的优化问题,因为在我看来,它们处于偏离主题的边缘。最糟糕的是,你没有提供MCVE https://stackoverflow.com/help/mcve因此任何试图回答这个问题的人都必须编写自己的所有支持代码来编译和基准测试您的内核。这类工作确实需要基准测试和代码分析。但是因为你的问题基本上是一个线性代数问题(而且我喜欢线性代数),所以我回答了它而不是关闭投票,因为它太广泛了……

我已经把这件事抛在脑后了。代码中立即出现了一些可以改进的内容,这些内容可能会对运行时间产生重大影响。

首先,内循环的行程计数是先验已知的。每当遇到类似情况时,请告知编译器。循环展开和代码重新排序是一项非常强大的编译器优化,NVIDIA 编译器在这方面非常擅长。如果将 D 移至模板参数中,则可以执行以下操作:

template<int D>
__device__ float esum(const float *x, const float *y)
{
    float val = 0.f;
#pragma unroll
    for(int i=0; i<D; i++) { 
        float diff = x[i] - y[i];
        val += diff * diff;
    }
    return val;
}

template<int D>
__global__ 
void vdistance0(const float * __restrict__ Y, float * __restrict__ DD, const int N)
{
    int index = blockIdx.x * blockDim.x + threadIdx.x;
    int stride = blockDim.x * gridDim.x;
    for (int i = index; i < N * N; i += stride) {
        const int m = i / N;
        const int n = i % N;
        DD[n + N * m] = esum<D>(Y + D * n, Y + D * m);
    }
}

template __global__ void vdistance0<2>(const float *, float *, const int);
template __global__ void vdistance0<3>(const float *, float *, const int);

编译器会内联esum并展开内部循环,然后它可以使用其重新排序启发式方法更好地交错负载和触发器以提高吞吐量。生成的代码也具有较低的寄存器占用空间。当我在 N=10000 且 D=2 的情况下运行此程序时,速度提高了约 35%(在配备 CUDA 9.1 的 GTX 970 上为 7.1 毫秒,而为 4.5 毫秒)。

但还有比这更明显的优化。您正在执行的计算将产生一个对称的输出矩阵。你只需要做(N*N)/2计算完整矩阵的操作,而不是N*N你在代码中所做的事情[技术上N(N/2 -1)因为对角线条目为零,但为了讨论的目的,让我们忘记对角线]。

因此,采用不同的方法并使用一个块来计算上三角输出矩阵的每一行,然后您可以执行以下操作:

struct udiag
{
    float *p;
    int m;

    __device__ __host__ udiag(float *_p, int _m) : p(_p), m(_m) {};
    __device__ __host__ float* get_row(int i) { return p + (i * (i + 1)) / 2; };
};


template<int D>
__global__ 
void vdistance2(const float * __restrict__ Y, float * __restrict__ DD, const int N)
{
     int rowid = blockIdx.x;
     int colid = threadIdx.x;
     udiag m(DD, N);

     for(; rowid < N; rowid += gridDim.x) {
         float* p = m.get_row(rowid);
         const float* y = Y + D * rowid;
         for(int i=colid; i < (N-rowid); i += blockDim.x) {
             p[i] = esum<D>(y, y + D * i);
         }
    }
}
template __global__ void vdistance2<2>(const float *, float *, const int);
template __global__ void vdistance2<3>(const float *, float *, const int);

这使用一个小辅助类来封装上三角输出矩阵的寻址方案所需的三角形数。这样做可以节省大量的内存和内存带宽,并减少计算的总 FLOP 计数。如果您之后需要做其他事情,BLAS(和 CUBLAS)支持上三角矩阵或下三角矩阵的计算。使用它们。当我运行这个程序时,我获得了大约 75% 的加速(7.1 毫秒,而同一 GTX 970 上为 1.6 毫秒)。

巨大的免责声明:您在这里看到的所有代码都是在 45 分钟午休期间编写的,并且一直如此very轻微测试。我绝对不声称这个答案中的任何内容实际上是正确的。我已经确认它可以编译,并且当我运行它来获取分析数据时不会产生运行时错误。这就对了。买者自负等等。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

优化三角矩阵计算的 CUDA 内核的执行 的相关文章

随机推荐

  • nginx conf 文件是什么语言?

    我想在我的 Nginx 配置文件中编写一些更复杂的条件 但我不确定语法 也找不到描述示例中基础知识之外可以做什么的文档 而且我似乎无法在Nginx 论坛或邮件列表 例如 我是否可以拥有一个unless健康 状况 所以我是 nginx 的新手
  • 如何以编程方式运行 NUnit

    我有一些引用 NUnit 的程序集并使用单个测试方法创建单个测试类 我能够获取该程序集的文件系统路径 例如 C test dll 我想以编程方式使用 NUnit 来针对该程序集运行 到目前为止我有 var runner new Simple
  • 我无法打开可变文件名

    有什么理由吗Dev C 不让我做file open file name variable 我不明白为什么它不允许我打开任何东西 除了像这样的硬编码名称file open abc txt 如何解决这个问题 不使用 Dev C 这基本上是我所拥
  • DecimalFormat 的奇数结果四舍五入

    提供一个浮点值37 35我得到一串37 3 这是我的代码 DecimalFormat format new DecimalFormat 0 format setRoundingMode RoundingMode HALF UP return
  • C# 剪贴板直接复制粘贴

    Hi 我想直接复制 粘贴 例如我只需单击一下按钮即可将粘贴到剪贴板 为此您可以编写示例代码 如果您想从文本框 在此示例中为 textBox1 复制 则需要以下内容 Clipboard SetText textBox1 Text To cop
  • 无法移出 ViewPort 窗格 - Selenium

    我正在尝试使网页自动化 http the internet herokuapp com exit intent http the internet herokuapp com exit intent 如果我们从视图窗格移向页面顶部 则会出现
  • 防止一个元素覆盖另一个元素[关闭]

    很难说出这里问的是什么 这个问题是含糊的 模糊的 不完整的 过于宽泛的或修辞性的 无法以目前的形式得到合理的回答 如需帮助澄清此问题以便重新打开 访问帮助中心 help reopen questions 我有一个固定的标题 当我滚动时 某些
  • 如何在.net中检索硬盘固件序列号?

    如何在 net 中检索硬盘固件序列号 我能够使用 WMI 调用检索 HDD 序列号 但无法找到任何方法来检索 HDD 固件序列号 提前感谢 使用 WMI 简单来说 这是一个低级函数 没有 理智的 更高级别的 API 会支持 读取或写入文件是
  • 什么是 F# 引号?

    F 中的 引号 是什么 它们的用途是什么 See http msdn microsoft com en us library dd233212 aspx http msdn microsoft com en us library dd233
  • Rails 5.2.0 与 Ruby 2.5.1 控制台 - `警告:``已经`初始化常量 FileUtils::VERSION

    我目前在新的 Rails 应用程序中遇到问题 更具体地说 轨道 5 2 0 Ruby 2 5 1p57 2018 03 29 修订版 63029 x86 64 darwin17 rvm 1 29 4 最新 作者 Michal Papis P
  • 将日期字符串解析为 java.util.Date 时出现非法模式字符“T”

    我有一个日期字符串 我想使用 java Date API 将其解析为正常日期 以下是我的代码 public static void main String args String date 2010 10 02T12 23 23Z Stri
  • 在 Mongoose 中间件方法之间共享数据 pre save 和 post save

    请参阅底部更新的示例代码 我在当前的 NodeJS 项目中使用 Mongoose 顺便说一句 这非常棒 并且我有一个 MDB 集合 它将在不同的集合中存储文档的更改 基本上是一个变更日志存储修改的内容 我试图实现的方法是创建一个存储文档的
  • 如何在C++中使用类对象作为函数参数

    我不确定如何拥有一个接收类对象作为参数的函数 有什么帮助吗 下面是一个例子 include
  • 在 MongoDB 中使用排序、限制,然后再次排序

    我正在使用 MongoDB 创建一个聊天应用程序 基本上我需要检索最新的 20 条聊天消息 然后按日期升序显示它们 为了获取最后 20 条聊天消息 我的代码如下所示 db messages find sort date 1 limit 20
  • 在 Linux 上以编程方式为 gdb 在 C 或 C++ 代码中设置断点

    如何以编程方式在 C 或 C 代码中设置适用于 Linux 上的 gdb 的断点 I e int main int argc char argv set breakpoint here int a 3 a In gdb gt print a
  • Mac 上 OpenGL 中的多线程视频渲染显示严重的闪烁问题

    我有一个视频播放器应用程序 并使用多个线程以保持用户交互仍然流畅 解码视频的线程最初只是将生成的帧作为 BGRA 写入 RAM 缓冲区 该缓冲区由 glTexSubImage2D 上传到 VRAM 对于普通视频来说效果足够好 但正如预期的那
  • 将 gtest 与 xcode 结合使用

    我正在尝试在我的计算机上设置一个 C 单元测试库 并认为 google 的 gtest 会很合适 我目前正在使用最新版本的 xcode 运行 mountain lion 我一直在尝试按照发现的说明进行操作here http code goo
  • iOS 13 SwiftUI:应用程序在真实设备上启动时崩溃

    在 SwiftUI 和 Apple 的新测试版 macOS 10 15 iOS 13 和 Xcode 11 发布后 我开始开发基于新技术的应用程序 考虑到所有涉及的软件和系统都处于测试阶段 开发进展顺利 然而 当尝试在真实设备 iPhone
  • 使用 System.Text.Json 反序列化匿名类型

    我正在更新 NET Core 3 x 的一些应用程序 作为其中的一部分 我正在尝试从Json NET到新的System Text Json类 使用 Json NET 我可以反序列化匿名类型 如下所示 var token JsonConver
  • 优化三角矩阵计算的 CUDA 内核的执行

    我正在开发我的第一个 Cuda 应用程序 并且我的内核 吞吐量低于预期 这似乎是目前最大的瓶颈 内核的任务是计算一个 N N 大小的矩阵 DD 包含数据矩阵上所有元素之间的平方距离 数据矩阵 Y 的大小为 N D 以支持多维数据 并存储为行