15

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

我相信使用 SSE 来实现这些循环应该会显着提高它们的性能,尤其是在对同一组数据执行许多操作的情况下,所以一旦数据最初被读入缓存,就不应该有任何缓存未命中停止它。但是,我不确定是否要这样做。

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

  • 我仍然需要支持一些没有或有限 SSE 支持的 CPU(例如 Intel Celeron)。是否有某种方法可以避免必须制作不同版本的程序,例如拥有某种“运行时链接器”,该链接器根据进程启动时运行它的 CPU 链接基本或 SSE 优化代码?

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

4

5 回答 5

7

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

  • 普通的旧 C 函数指针
  • 动态链接(通常依赖于 C 函数指针)
  • 如果您使用的是 C++,那么拥有不同的类来表示对不同架构的支持并使用虚函数可以极大地帮助您解决这个问题。

请注意,由于您将依赖间接函数调用,因此抽象不同操作的函数通常需要表示更高级别的功能,否则您可能会失去从调用开销中的优化指令中获得的任何收益(换句话说,不要t 抽象单个 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
于 2009-12-12T22:08:20.093 回答
6

关于这个主题的好读物:停止指令集战争

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

于 2009-12-12T19:39:24.200 回答
4

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

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

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

于 2009-12-12T19:59:48.997 回答
3

回答您的评论:

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

依靠。只要不执行 SSE 指令,它们就可以存在于二进制文件中。CPU对此没有任何问题。

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

因此,您最有可能在启用 SSE 的情况下分别编译一个或两个文件,并让它们包含您所有的 SSE 例程。然后将其与应用程序的其余部分链接,该部分是在没有 SSE 支持的情况下编译的。

于 2009-12-13T11:06:38.140 回答
1

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

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

您的应用程序听起来正是 OpenCL 旨在解决的问题。在 SSE 中编写替代函数当然会提高执行速度,但编写和调试工作量很大。

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

是的。SSE 内在函数基本上已由 Intel 标准化,因此相同的函数在 Windows、Linux 和 Mac 之间的工作方式相同(特别是在 Visual C++ 和 GNU g++ 中)。

我仍然需要支持一些没有或有限 SSE 支持的 CPU(例如 Intel Celeron)。是否有某种方法可以避免必须制作不同版本的程序,例如拥有某种“运行时链接器”,该链接器根据进程启动时运行它的 CPU 链接基本或 SSE 优化代码?

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

对于 OpenCL,没有必要这样做,因为代码是在运行时为给定架构生成的。

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

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

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

于 2009-12-14T10:53:32.457 回答