有趣的问题。我已经研究过这个问题,所以我的答案是基于一些参考资料和个人经验。
哪些类型的问题更适合常规多核,哪些类型更适合 GPGPU?
就像@Jared 提到的那样。 GPGPU 是为非常常规的吞吐量工作负载而构建的,例如图形、密集矩阵矩阵乘法、简单的 Photoshop 过滤器等。它们擅长容忍长延迟,因为它们天生就可以容忍纹理采样,即 1000 多个周期的操作。 GPU 内核有很多线程:当一个线程触发长延迟操作(例如内存访问)时,该线程将进入睡眠状态(其他线程继续工作),直到长延迟操作完成。这使得 GPU 能够保持其执行单元比传统内核更繁忙。
GPU 不擅长处理分支,因为 GPU 喜欢将“线程”(如果您不是 nVidia,则为 SIMD 通道)批处理成 warp 并将它们一起发送到管道以节省指令获取/解码能力。如果线程遇到分支,它们可能会发散,例如,8 线程 warp 中的 2 个线程可能会占用分支,而其他 6 个线程可能不会占用它。现在必须将经线分成大小为 2 和 6 的两个经线。如果您的核心有 8 个 SIMD 通道(这就是原始经线打包 8 个线程的原因),现在您的两个新形成的经线将低效运行。 2 线程 warp 将以 25% 的效率运行,6 线程 warp 将以 75% 的效率运行。你可以想象,如果一个 GPU 不断遇到嵌套分支,它的效率就会变得非常低。因此,GPU 不擅长处理分支,因此不应在 GPU 上运行带有分支的代码。
GPU 也不是协作线程。如果线程需要相互通信,那么 GPU 将无法正常工作,因为 GPU 上的同步没有得到很好的支持(但 nVidia 就在上面)。
因此,GPU 最糟糕的代码是并行性较低的代码或具有大量分支或同步的代码。
编程模型的主要区别是什么?
GPU 不支持中断和异常。对我来说这是最大的不同。除此之外,CUDA 与 C 没有太大区别。您可以编写一个 CUDA 程序,将代码发送到 GPU 并在那里运行。您在 CUDA 中访问内存的方式略有不同,但这又不是我们讨论的基础。
哪些关键的底层硬件差异导致编程模型有所不同?
我已经提到过它们。最大的是 GPU 的 SIMD 特性,它要求以非常规则的方式编写代码,没有分支和线程间通信。这就是为什么,例如,CUDA 限制代码中嵌套分支的数量的部分原因。
哪一个通常更容易使用?使用多少?
取决于您正在编码什么以及您的目标是什么。
易于矢量化的代码:CPU 更容易编码,但性能较低。 GPU 编码稍微难一些,但物超所值。
对于所有其他人来说,CPU 更容易,而且性能通常也更好。
从长远来看,为 GPU 实现高级并行库是否可行,例如 Microsoft 的任务并行库或 D 的 std.parallelism?
根据定义,任务并行需要线程通信并且也有分支。任务的想法是不同的线程做不同的事情。 GPU 是为许多做相同事情的线程而设计的。我不会为 GPU 构建任务并行库。
如果 GPU 计算如此高效,为什么 CPU 的设计不像 GPU?
世界上的许多问题都是分支和不规则的。 1000 个例子。图形搜索算法、操作系统、网络浏览器等。补充一点——甚至图形也像每一代一样变得越来越多分支和通用,因此 GPU 将变得越来越像 CPU。我并不是说它们会变得像 CPU 一样,但它们会变得更加可编程。正确的模型介于低功耗 CPU 和非常专业的 GPU 之间。