利用SSE和其他CPU扩展

时间:2009-12-12 19:30:30

标签: c++ gcc cross-platform visual-c++ simd

我的代码库中有几个地方,对于大型数据集,相同的操作会重复很多次。在某些情况下,处理这些需要相当长的时间。

我相信使用SSE来实现这些循环应该会显着提高它们的性能,特别是在对同一组数据执行许多操作的情况下,所以一旦数据最初被读入缓存,就不应该有任何缓存错过了它。但是我不确定要解决这个问题。

  • 是否有编译器和操作系统独立的方式编写代码以利用SSE指令?我喜欢VC ++内在函数,其中包括SSE操作,但我还没有找到任何交叉编译器解决方案。

  • 我仍然需要支持一些没有或限制SSE支持的CPU(例如Intel Celeron)。有没有办法避免不得不制作不同版本的程序,比如有一种“运行时链接器”链接在基本或SSE优化代码中,基于运行进程时运行它的CPU?

  • 其他CPU扩展怎么样,查看各种Intel和AMD CPU的指令集,其中有几个?

5 个答案:

答案 0 :(得分:7)

对于您的第二点,只要您可以将差异分离到不同的功能中,就有几种解决方案:

  • 普通的旧C函数指针
  • 动态链接(通常依赖于C函数指针)
  • 如果你正在使用C ++,那么代表不同架构的支持和使用虚函数的不同类可以极大地帮助你。

请注意,因为您依赖于间接函数调用,抽象不同操作的函数通常需要表示更高级别的功能,否则您可能会失去从调用开销中的优化指令获得的任何增益(在其他情况下)单词不抽象单个SSE操作 - 抽象你正在做的工作。

以下是使用函数指针的示例:

typedef int (*scale_func_ptr)( int scalar, int* pData, int count);


int non_sse_scale( int scalar, int* pData, int count)
{
    // do whatever work needs done, without SSE so it'll work on older CPUs

    return 0;
}

int sse_scale( int scalar, in pData, int count)
{
    // equivalent code, but uses SSE

    return 0;
}


// at initialization

scale_func_ptr scale_func = non_sse_scale;

if (useSSE) {
    scale_func = sse_scale;
}


// now, when you want to do the work:

scale_func( 12, theData_ptr, 512);  // this will call the routine that tailored to SSE 
                                    // if the CPU supports it, otherwise calls the non-SSE
                                    // version of the function

答案 1 :(得分:6)

关于这个问题的好读物:Stop the instruction set war

简短概述:抱歉,无法以简单且最兼容(英特尔与AMD)的方式解决您的问题。

答案 2 :(得分:4)

SSE内在函数与visual c ++,GCC和intel编译器一起使用。这些天使用它们没有问题。

请注意,您应始终保留不使用SSE的代码版本,并根据您的SSE实施不断检查它。

这不仅有助于调试,如果您想支持不支持所需SSE版本的CPU或架构,它也很有用。

答案 3 :(得分:3)

回答你的评论:

  

如此有效,只要我不尝试实际执行包含不受支持的指令的代码我就没事了,我可以使用“if(see2Supported){...} else {...}”来逃避型开关?

取决于。只要它们没有被执行,SSE指令就可以存在于二进制文件中。 CPU没有问题。

但是,如果在编译器中启用SSE支持,它很可能会为其SSE等效项(例如标量浮点运算符)交换许多“正常”指令,因此即使是常规非SSE的块也是如此代码会在不支持它的CPU上爆炸。

所以你需要做的就是单独编译或两个文件,启用SSE,并让它们包含你所有的SSE例程。然后将其与应用程序的其余部分链接,这是在没有SSE支持的情况下编译的。

答案 4 :(得分:1)

我强烈建议您查看OpenCL,而不是将替代SSE实现手动编码到您的标量代码中。它是一个供应商中立的便携式跨平台系统,适用于计算密集型应用程序(并且非常符合流行语!)。您可以在为向量化操作设计的C99子集中编写算法,这比手动编码SSE要容易得多。最重要的是,OpenCL将在运行时生成最佳实现,以在CPU上的上执行。所以基本上你会得到为你编写的SSE代码。

  

我的代码库中有几个地方,对于大型数据集,相同的操作会重复很多次。在某些情况下,处理这些需要相当长的时间。

您的应用程序听起来就像是OpenCL旨在解决的问题。在SSE中编写替代函数肯定会提高执行速度,但编写和调试是一项繁重的工作。

  

是否有编译器和操作系统独立的方式编写代码以利用SSE指令?我喜欢VC ++内在函数,其中包括SSE操作,但我还没有找到任何交叉编译器解决方案。

是。 SSE内在函数基本上已被英特尔标准化,因此Windows,Linux和Mac之间的相同功能(特别是Visual C ++和GNU g ++)也是如此。

  

我仍然需要支持一些没有或有限SSE支持的CPU(例如Intel Celeron)。有没有办法避免不得不制作不同版本的程序,比如有一种“运行时链接器”链接在基本或SSE优化代码中,基于运行进程时运行它的CPU?

你可以这样做(例如使用dlopen()),但这是一个非常复杂的解决方案。更简单的是(在C中)定义一个函数接口并通过函数指针调用优化函数的适当版本,或者在C ++中使用不同的实现类,具体取决于检测到的CPU。

使用OpenCL没有必要这样做,因为代码是在运行时为给定的体系结构生成的。

  

其他CPU扩展怎么样,查看各种Intel和AMD CPU的指令集会显示其中的一些?

在SSE指令集中,有很多种口味。当某些指令不存在时,在SSE的不同子集中编码相同的算法可能非常困难。我建议(至少在开始时)选择最低支持级别,例如SSE2,然后回退到旧机器上的标量实现。

这也是单元/回归测试的理想情况,这对于确保不同的实现产生相同的结果非常重要。拥有输入数据和已知良好输出数据的测试套件,并通过两个版本的处理功能运行相同的数据。您可能需要进行传递的精确测试(例如,结果和正确答案之间的差异epsilon低于1e6)。这将极大地有助于调试,如果您为测试框架构建高分辨率时序,则可以同时比较性能改进。