Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/xml/14.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 通过使用函数指针在游戏引擎数学库中使用SIMD~好主意?_C++_Function Pointers_Game Engine_Simd - Fatal编程技术网

C++ 通过使用函数指针在游戏引擎数学库中使用SIMD~好主意?

C++ 通过使用函数指针在游戏引擎数学库中使用SIMD~好主意?,c++,function-pointers,game-engine,simd,C++,Function Pointers,Game Engine,Simd,我从14岁起就开始阅读游戏引擎书籍(当时我一点也不懂:p) 几年后的今天,我想开始为我的游戏引擎编程数学基础。我一直在思考如何设计这个“图书馆”。(我的意思是“有组织的文件集”)每隔几年就会有新的SIMD指令集出现,我不希望它们被浪费掉。(如果我错了,请告诉我。) 我希望至少拥有以下属性: 使其能够检查它是否在运行时有SIMD,如果它有SIMD,则使用SIMD,如果没有,则使用正常的C++版本。(可能会有一些通话开销,这值得吗?) 如果编译时已经知道目标,则能够编译SIMD或普通C++。调用可

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

我希望至少拥有以下属性:

    使其能够检查它是否在运行时有SIMD,如果它有SIMD,则使用SIMD,如果没有,则使用正常的C++版本。(可能会有一些通话开销,这值得吗?)
  • 如果编译时已经知道目标,则能够编译SIMD或普通C++。调用可以内联,并适合于交叉优化,因为编译器知道使用SIMD或C++。
编辑-我想使源代码可移植,这样它也可以在其他设备上运行,然后是x86(-64)

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

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


Christian

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

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

例如,通过函数指针调用矩阵乘法(matrix multiply)所导致的双重间接寻址将不会很好。它不会内联琐碎的数学函数,而是在整个过程中引入函数调用,这些调用将被强制保存/恢复大量寄存器以启动(因为指针后面的代码在运行时才被知道)

在这一点上,没有双重间接寻址的非优化版本将大大优于带有函数指针的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
然后,您可能只需要将SSE和NEON包装在一个半ok包装器后面。当谈到AVX512和AVX2时,你可能会被搞砸

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

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

然后您就有可能生产出AVX2/AVX512版本。然而,使用这样组织的代码并不是世界上最容易的事情

您可能想看看函数。有许多直接映射到SIMD指令的编译器,如果目标平台不支持该特定指令,那么今天的编译器应该足够智能,能够模拟该指令。参见g.e..SSE2说明至少已有10年的历史。你确定你关心在旧机器上运行的游戏吗(假设我们不是在手机上玩游戏之类的)。@Matsbeterson我编辑了我的问题!我忘记了一个非常重要的目标,那就是它必须可移植到任何系统。@JoachimPileborg我认为他们模仿它很好,但我不希望我的代码是特定于编译器的,如果我使用VS2012的编译器内部函数,我的代码将是特定于编译器的。如果你使用SIMD扩展,你将有一些“不可移植”的代码。由于使用VS、GCC和Intel编译器,这些内部函数实际上是相当可移植的。显然,不是在ARM或PowerPC处理器上…因为数学库中的大多数函数都很小,您是否建议删除它的属性,以便在运行时推断它是否具有SIMD,并在可用时使用它?以及将使用SIMD的决策提升到更高的层次?我相信这样做会更有效,但代码膨胀可能是不可避免的。通常,为了从SIMD中获得任何真正的好处,每个例程都应该在一个相当大的数据集上运行,也就是说,不只是添加两个SIMD向量,而是添加两个1D或2D数组。这样,例程本身就可以获得很好的效率,而函数调度开销并不显著。看看Intel在其IPP库中如何处理此问题。我可以在Intels网站上找到IPP库,但您是否有指向该库内部的链接?您会建议我如何设计我的数学库?如果您阅读IPP的在线文档,它将讨论调度机制。至于设计,这是一个很大的主题,但本质上是我上面所说的:设计库函数,使它们在足够大的数据集上运行,例如,在图像处理的情况下,一行、一块或一块像素。理想情况下,您应该处理适合缓存的数据块,以便可以在同一数据仍在缓存中时对其组合多个函数调用。
struct Vec4SOA
{
  float x[BIG_NUM];
  float y[BIG_NUM];
  float z[BIG_NUM];
  float w[BIG_NUM];
};