【问题标题】:How to select the most powerful OpenCL device?如何选择最强大的 OpenCL 设备?
【发布时间】:2014-02-12 05:51:03
【问题描述】:

我的计算机同时具有 Intel GPU 和 NVIDIA GPU。后者功能更强大,是我执行繁重任务时的首选设备。我需要一种以编程方式确定要使用哪个设备的方法。

我知道很难知道哪种设备最适合特定任务。我需要的是(以编程方式)使用下面列出的变量进行合格的猜测

您如何对这两种设备进行排名?Intel HD Graphics 4400 位于左侧,GeForce GT 750M 位于右侧。

GlobalMemoryCacheLineSize               64 vs 128
GlobalMemoryCacheSize              2097152 vs 32768
GlobalMemorySize                1837105152 vs 4294967296
HostUnifiedMemory                     true vs false
Image2DMaxHeight                     16384 vs 32768
Image2DMaxWidth                      16384 vs 32768
Image3DMaxDepth                       2048 vs 4096
Image3DMaxHeight                      2048 vs 4096
Image3DMaxWidth                       2048 vs 4096
LocalMemorySize                      65536 vs 49152
MaxClockFrequency                      400 vs 1085
MaxComputeUnits                         20 vs 2
MaxConstantArguments                     8 vs 9
MaxMemoryAllocationSize          459276288 vs 1073741824
MaxParameterSize                      1024 vs 4352
MaxReadImageArguments                  128 vs 256
MaxSamplers                             16 vs 32
MaxWorkGroupSize                       512 vs 1024
MaxWorkItemSizes           [512, 512, 512] vs [1024, 1024, 64]
MaxWriteImageArguments                   8 vs 16
MemoryBaseAddressAlignment            1024 vs 4096
OpenCLCVersion                         1.2 vs 1.1
ProfilingTimerResolution                80 vs 1000
VendorId                             32902 vs 4318

显然,还有数百种其他设备需要考虑。我需要一个通用公式!

【问题讨论】:

  • 我会选择VendorId 最高的那个。说真的:如果你不知道你必须执行的任务,你怎么能猜测?它可能是高度并行的(然后更多的单元可能更好)或原始的粗略计算(然后更高的时钟频率或更大的缓存可能更好)。举个例子……
  • 通用公式对您没有帮助,因为公式必须猜测您希望执行的特定任务,因为某些规格适合不同的任务。
  • 在这种情况下,我们需要更多的单元。这些任务是高度并行的。但是,正如您在上面看到的,NVIDIA 错误地报告了 2 个计算单元......
  • 至于 正常 CPU 线程数并不是并行任务必须考虑的唯一因素(如果您不能信任报告的值,那么您就没有希望猜测使用它们)。让我再挑选几件要考虑的事情:缓存(每个任务使用多少本地数据?);内存(与 CPU 共享?与并行任务相比有多少并发访问?);指令集(我们是否需要一些特定的东西来提高速度,即使其他参数不太好?);杂项(我们是否有一些特定要求,例如 something 的大小?)。
  • 简而言之:您无法以可靠的方式猜测,因为因素太多且相关。编写一个小测试(尽可能与您的任务相似)并使用 许多卡片 运行它,并使用足够大的 统计数据可能 从一组未知的参数中推断出一个索引。

标签: performance opencl gpgpu


【解决方案1】:

为什么要猜?在您当天的硬件上动态选择:获取您希望在“最佳”GPU 上运行的代码,并在每个可用 GPU 上以少量样本数据运行它。以先完成者为准:将其用于其余计算。

【讨论】:

  • 你提到的方式通常发生在研究中,输出数据是唯一重要的事情。我认为这不是软件工程的可能方式。
【解决方案2】:

到目前为止,我喜欢所有的解决方案。如果自动选择最佳设备很重要,那么就是这样做的(根据您的使用需求加权值并取最高分)。

另外一种更简单的方法是只使用 第一个 GPU 设备,但也可以让用户查看兼容设备列表并进行更改(立即或在下一次运行)。

这种选择是合理的,因为大多数系统只有一个 GPU。

【讨论】:

  • 是的,这就是他们在大多数在线示例中所做的。但是,我有两台 相同的 计算机,并且它们上的 first 设备不一样。不确定这是怎么可能的,但也许某些驱动程序是以其他顺序或类似顺序安装的。这就是我首先提出这个问题的原因。我根本不能依赖设备的顺序。
  • 是的,驱动安装顺序和其他因素会影响平台和设备顺序。如果计算机和设备的集合是已知的,只需搜索已知的设备 ID。或者,像建议的其他答案一样,选择一个或多个参数(或其产品),对它们进行加权,然后取最高分。对于“最佳 GPU”,我只需将 CL_DEVICE_MAX_COMPUTE_UNITS 乘以 CL_DEVICE_MAX_CLOCK_FREQUENCY,就像 Adriano 建议的那样。没有对实际内核进行基准测试,这是一个很好的猜测。
【解决方案3】:

正如@Adriano 所指出的,有很多事情需要考虑......太多的事情。 但我能想到几件事情(以及可以做的更简单的事情)来帮助你(而不是完全解决你的问题):

OCL 版本

首先,您需要哪个版本的 OCL(与性能无关)。但是如果你使用 OCL 1.2 的某些特性...问题就解决了

内存或计算限制

您通常可以(粗略地)将您的算法归为以下两个类别之一:内存受限或计算受限。如果它受内存限制(主机和设备之间有大量传输)可能,最有趣的信息将是具有主机统一内存的设备。如果不是,最强大的处理器很可能会更有趣。

粗略基准

但很可能选择将您的应用程序放在哪个类别中并不容易。 在这种情况下,你可以做一个小基准。粗略地说,这个基准测试将在虚拟计算上测试不同大小的数据(如果您的应用程序必须处理该数据),这些虚拟计算或多或少地匹配您的应用程序所需的计算量(在您完成内核开发后由您估计)。您可以记录数据量如此之大以至于它取消了功能最强大但通过 PCIe 连接的设备的点。

GPU 占用率

在 GPU 上编程时另一个非常重要的事情是 GPU 占用率。越高越好。 NVIDIA 提供了一个Excel file,它根据一些输入计算占用率。基于这些概念,您可以或多或少地重新计算两个 GPU 的占用率(其他供应商很可能需要进行一些调整)并选择最高的一个。
当然,您需要知道这些输入的值。其中一些是基于您的代码,因此您可以在动手之前计算它们。其中一些与 GPU 的规格有关。您可以像以前一样查询其中一些,对于其他一些,您可能需要在谷歌搜索后对某些文件中的值进行硬编码(但至少您不需要手头有这些 GPU 来测试它们)。最后但同样重要的是,不要忘记 OCL 提供了 clGetKernelWorkGroupInfo(),它可以为您提供一些信息,例如特定内核所需的本地或私有内存量。
关于本地内存的信息,请注意标准中的注释:

如果是本地内存大小,对于内核的任何指针参数 使用 __local 地址限定符声明,未指定,其 大小假定为 0。

因此,这意味着如果您必须首先从主机端动态计算大小,则此信息可能无用。一种解决方法可能是使用内核在 JIT 中编译的事实。这里的想法是在调用clBuildProgram() 时使用预处理器选项-D,正如我解释的here。这会给你类似的东西:

#define SIZE

    __mykernel(args){
       local myLocalMem[SIZE];
       ....
    }

如果更容易:

毕竟废话。我猜您会担心这一点,因为您可能想在不知道他们拥有什么硬件的情况下将您的应用程序发送给某些用户。使用虚拟生成的数据简单地运行您的应用程序以测量哪个设备性能更好并将其简单地记录在配置文件中会非常不方便(在安装时或通过向他们提供命令或按钮之后)吗?

或者也许:

有时,根据您的具体问题(可能不会涉及许多同步),您不必选择。有时,您可以简单地在两个设备之间拆分工作并同时使用...

【讨论】:

    【解决方案4】:

    你不能有一个简单的公式来根据这些参数计算索引。

    说明

    首先让我假设您可以信任收集到的数据,当然如果您为MaxComputeUnits 读取 2,但实际上它是 80,那么您无能为力(除非您拥有自己的卡片数据库及其所有规格)。

    如果你不知道你必须执行的任务,你怎么能猜到?它可能是高度并行的(然后更多的单元可能更好)或原始的粗略计算(然后更高的时钟频率或更大的缓存可能更好)。至于 正常 CPU 线程数并不是并行任务必须考虑的唯一因素。仅提及您必须考虑的几件事:

    • 缓存:每个任务使用多少本地数据?
    • 内存:与 CPU 共享?与并行任务相比有多少并发访问?
    • 指令集:您是否需要特定的东西来提高速度,即使其他参数不太好?
    • 杂项:您是否有一些特定要求,例如必须支持的东西的大小以及后备方法会使一切变得非常缓慢?

    简而言之:您无法以可靠的方式计算索引,因为因素太多并且它们之间存在很强的相关性(例如,高并行性可能会因缓存小或内存访问速度慢而变慢,但特定指令,如果支持,即使所有其他参数都很差,也可以为您提供出色的性能)。

    一种可能的解决方案

    如果您需要原始比较,您甚至可以简单地进行MaxComputeUnits * MaxClockFrequency(对于许多应用程序来说甚至可能就足够了),但如果您需要更准确的索引,那么不要认为这将是一件容易的事,您'会得到一个通用公式,如(a + b / 2)^2,它不是,结果将非常具体到你必须完成的任务。

    编写一个小测试(尽可能与您的任务相似,查看this post on SO)并使用许多卡片运行它,并使用足够大的统计数据 您可以从一组未知参数中推断索引。算法可能会变得非常复杂,并且有大量关于这个主题的文献,所以我什至不会在这里重复它们。我将从Wikipedia article 开始,作为其他更具体论文的摘要。如果你需要一个你必须做什么的例子,你可以阅读Exploring the Multiple-GPU Design Space

    请记住,添加到研究中的变量越多,结果质量越不稳定,使用的参数越少,结果越准确。为了更好地支持外推:

    • 在您收集到足够的数据后,您应该首先select and reduce variables 对其中的一部分进行一些预分析,只包括对您的基准测试结果影响更大的因素(例如,MaxGroupSize 可能不那么相关)。这个阶段非常重要,应该使用统计工具做出决策(例如,您可以计算 p 值)。
    • 某些参数可能具有很大的可变性(内存大小、单位数),但使用较少的值(例如 [0..5) 单位、[5..10) 单位、[10..* ) 单位)。然后,您应该对数据进行分区(观察它们的分布)。不同的分区可能会导致非常不同的结果,因此您应该尝试不同的组合。

    还有很多其他的事情需要考虑,一本关于数据挖掘的好书会帮助你写在这里超过 1000 字。

    【讨论】:

    • 优秀的答案!在某些情况下,建议的原始比较可能会派上用场。
    猜你喜欢
    • 1970-01-01
    • 2022-01-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-13
    • 1970-01-01
    • 1970-01-01
    • 2013-09-22
    相关资源
    最近更新 更多