3

我从 14 岁开始就一直在阅读游戏引擎书籍(当时我什么都不懂:P) 几年后,我想开始为我的游戏引擎编写数学基础。我一直在思考如何设计这个“图书馆”。(我的意思是“有组织的文件集”)每隔几年就会出现新的 SIMD 指令集,我不希望它们浪费掉。(如果我错了,请告诉我。)

我想至少具有以下属性:

  • 使其能够在运行时检查它是否有 SIMD,如果有则使用 SIMD,如果没有则使用普通的 C++ 版本。(可能会有一些通话开销,这值得吗?)
  • 如果我们在编译时已经知道目标,则使其能够为 SIMD 或普通 C++ 编译。这些调用可以内联并适合交叉优化,因为编译器知道是使用 SIMD 还是 C++。

编辑 - 我想让源代码可移植,这样它也可以在其他设备上运行,然后是 x86(-64)

所以我认为使用函数指针是一个很好的解决方案,我会在程序开始时将其设为静态并初始化。以及合适的函数(例如矩阵/向量的乘法)将调用。

您认为这种设计的优点和缺点是什么(哪个更重要?),甚至可以用上述两种特性来创建它吗?

基督教

4

2 回答 2

5

获得正确的粒度以决定调用哪个例程非常重要。如果您在太低的级别上执行此操作,那么函数调度开销就会成为一个问题,例如,如果通过某种函数指针调度机制而不是仅仅被内联调用,那么只有几条指令的小例程可能会变得非常低效。理想情况下,特定于体系结构的例程应该处理合理数量的数据,以便函数调度成本可以忽略不计,而不会因为为每个支持的体系结构编译额外的非体系结构特定代码而导致代码大量膨胀。

于 2013-05-10T09:02:44.023 回答
0

最简单的方法是编译你的游戏两次,一次启用 SIMD,一次不启用。创建一个执行 _may_i_use_cpu_feature 检查的小型启动器应用程序,然后运行正确的构建。

通过函数指针调用矩阵乘法(例如)引起的双重间接不会很好。它不会内联琐碎的数学函数,而是会在整个商店中引入函数调用,并且这些调用将被迫保存/恢复大量寄存器以启动(因为指针后面的代码要到运行时才能知道) .

那时,没有双重间接的非优化版本将大大优于带有函数指针的 SSE 版本。

至于支持多平台,这很容易,但也很麻烦。ARM neon 与 SSE4 非常相似,因此值得将指令封装在一些宏后面,但是 neon 也不同,非常烦人!

#if CPU_IS_INTEL

#include <immintrin.h>
typedef __m128 f128;

#define add4f _mm_add_ps

#else

#include <neon.h>
typedef float32x4 f128;

#define add4f vqadd_f32

#endif

从 Intel 开始,后来移植到 ARM 的主要问题是很多好东西都不存在。在 ARM 上可以进行洗牌,但这也很麻烦。ARM 上不存在除法、点积和 sqrt(只有倒数估计,您需要在其上进行自己的牛顿迭代)

如果您正在考虑这样的 SIMD:

struct Vec4 
{
  float x;
  float y;
  float z;
  float w;
};

然后,您也许可以将 SSE 和 NEON 包装在一个半正常的包装器后面。但是,当谈到 AVX512 和 AVX2 时,您可能会被搞砸。

但是,如果您正在考虑使用数组结构格式的 SIMD:

struct Vec4SOA
{
  float x[BIG_NUM];
  float y[BIG_NUM];
  float z[BIG_NUM];
  float w[BIG_NUM];
};

那么您就有机会制作出 AVX2/AVX512 版本。然而,使用这样组织的代码并不是世界上最简单的事情。

于 2017-06-20T09:26:38.360 回答