Chinese, Simplified 字幕

← cs344_unit5_17_l_转置代码测量内存使用量

埋め込みコードを取得する
2言語

Showing Revision 3 created 05/19/2013 by Michael Xiao.

  1. 所以,如果我们回顾一下针对所有这些内核的分析,
    我们就会看到
  2. 我们的每元素并行代码版本将达到每秒12.5 千兆字节。
  3. 我们的每行并行代码版本将获得约每秒1.8 千兆字节,
  4. 而我们的串行代码版本获得令人失望的每秒0.018 千兆字节。
  5. 这大约是信鸽的速度。或许,
    一个思考这个问题更好的方式,
  6. 不在于绝对数字,而是我们使用的特定 GPU 能够实现的百分比。
  7. 所以,如果我们要算出我们达到的百分比,
  8. 使用我们执行最好的内核,它大致是31 %的理论峰值带宽,
  9. 使用我们的每行内核时达到4.5%的理论峰值带宽,
    而我们的串行内核达到小于0.1%的理论峰值带宽。
  10. 所以,回到问题。为什么这个数字这么低?
  11. 嗯,我们可以做一个聪明的猜测,
    每当你看到非常低的DRAM 利用率,
  12. 非常低的百分比带宽,你第一个猜测始终是合并。
  13. 理解合并的一种方法是 GPU 始终访问全局内存,
  14. 每次以较大的数据块,
    32 个或 128 个字节来访问DRAM。
  15. 这意味着我们将需要最少
  16. 的总内存事务,如果warp中的线程访问连续相邻的内存位置。
  17. 所以,这是一个很好的合并示例。
  18. 每个线程在读取或写入一个相邻的内存位置。
  19. 而且很清楚,如果一个warp中的线程
    在读取或写入完全随机地址和内存,
  20. 然后你会得到较差的合并,对吗?
  21. 所以,如果这些访问散布在内存,
  22. 然后我们要读取的内存块总数
    可能和warp中的线程数一样多。
  23. 所以,一个随机访问模式显然会导致一个较差的合并。
  24. 所以,更为常见的访问模式被称为跨步,
    在这种模式下线程访问
  25. 的内存位置是它们的线程标识与一些步长乘积的函数。
  26. 所以,例如,线程 0 可能会访问位置 0,线程 1 访问位置 2,线程 2 位置 4、 线程 3 位置 6,等等。
  27. 在这种情况下,这将是 2步幅,
    因为在线程访问之间有两个元素,
  28. 跨步访问的范围从,好吧,
    就像在这个例子的 2 个元素的步幅,
  29. 我真的只增加了一倍的内存事务数。
  30. 所以我有点儿把我的合并质量
    一直减半到非常非常糟糕的地步,对吗?
  31. 所以,你可以想象,如果元素之间的跨距足够大,
  32. 然后在warp中的每个线程访问完全
    不同的 32 或 128 字节的内存块,
  33. 那么接下来你保证得到很糟的行为。
  34. 你必须要做的是保证将内存事务的数目最大化。
  35. 所以,让我们看看我们内核的代码。
    这里是我们从输入矩阵读取得地方,
  36. 和这实际上表现很不错。
  37. 每个线程读取内存中的一个值,
    这个值等于某个巨大的偏移;J乘以N加I。
  38. 如果你看看I,I其实是线程索引再加上一些偏移量。
  39. 所以,相邻的线程,你知道,那些相邻线程进入season X的线程
  40. 正在读入输入矩阵的相邻值。这正是我们想要的,
  41. 所以这是好的合并。另一方面,当我们写入输出矩阵,
  42. 相邻线程,具有相邻值I的线程,
    写入内存中被N分开的地方,对吗?
  43. N 就像 1024。所以,相邻线程正运行在
    相距1024个元素的内存位置。
  44. 这显然不好,这是很糟的合并。很糟,很糟、很糟、很糟。
  45. 事实上,这是我们问题的根源。