gsl_complex与std::complex性能比较

3
我正在编写一个程序,该程序需要进行大量复杂的加法和乘法操作。我想知道是否应该使用gsl_complex还是std::complex
我似乎找不到在线比较GSL复杂算术与std::complex的优劣的页面。初步的谷歌搜索也没有帮助我找到GSL复杂的基准测试页面。
我编写了一个20行程序,生成两个随机的复数数组(1e7个),然后使用<ctime>中的clock()检查了加法和乘法所需的时间。使用此方法(没有编译器优化),我了解到gsl_complex_addgsl_complex_mul的速度几乎是std::complex<double>+*的两倍。但我以前从未做过这种事情,所以这是检查哪个更快的方式吗?
任何链接或建议都将有所帮助。谢谢!
编辑:

好的,我再次使用了 -O3 标志进行尝试,现在结果非常不同!std::complex<float>::operator+gsl_complex_add 快两倍以上,而 gsl_complex_mul 大约比 std::complex <float> :: operator * 快1.25倍。如果我使用 double,gsl_complex_add 大约比 std::complex <double> :: operator + 快30%,而 std::complex <double> :: operator * 大约比 gsl_complex_mul 快10%。我只需要浮点精度,但我听说 double 更快(并且内存对我来说不是问题)!所以现在我真的很困惑!


这是否进行了优化? - Mats Petersson
无论结果如何,你的基准测试几乎肯定是错误的。进行基准测试很困难;-) - user395760
@delnan 好的,也许我并不是真正意义上的想要进行基准测试!那么我该如何确定我应该使用什么?这个检查是否足够好? - Praveen
@MatsPetersson 我现在已经添加了优化运行的结果。但我无法判断该选择哪一个。 - Praveen
1
你能分享一下你的基准测试代码吗? - pyCthon
显示剩余3条评论
2个回答

4
打开优化选项。
任何你链接的库或者函数都会被编译时用优化(除非开发者名字是Kermit、Swedish Chef、Miss Peggy(项目经理)和Cookie Monster(测试人员),也就是说,开发团队是一群木偶)。由于std::complex使用了模板,所以它会根据你给出的编译器设置进行编译,因此代码不会被优化。所以你的问题实际上是“为什么在使用优化编译的函数X比没有使用优化编译的函数Y更快,即使它们做同样的事情?”这个问题的答案应该很明显:“优化几乎总是有用的!”(如果优化大多数时候都不起作用,编译器开发者将会轻松得多)
编辑:我的上面的观点刚刚得到了证实。请注意,由于模板可以内联代码,因此它通常比外部库更有效率(因为编译器可以直接将指令插入流中,而不是调用另一个函数)。
至于float与double,唯一比double慢的时候是只有double硬件可用,并增加了两个函数来在float和double之间缩短和延长。我不知道是否有这样的硬件。double有更多的位,所以它应该需要更长的时间。
编辑2:
在选择“一种解决方案而不是另一种解决方案”时,有很多因素。性能是其中之一(在某些情况下,是最重要的因素,在其他情况下则不是)。其他方面包括“易用性”,“可用性”,“适合项目”等等。
如果只考虑性能,有时可以运行简单的基准测试来确定一种解决方案比另一种更好或更差,但对于复杂的库[不是“实数和虚数”类型的复数,而是“复杂”的库],有时会有针对大量数据进行优化的方法,如果您使用不太复杂的解决方案,则“大数据”将无法达到相同的性能,因为没有花费足够的精力来解决“大数据”类型的问题。因此,如果您有一个“简单”的基准测试,可以在少量数据上进行一些基本计算,并且实际上您将运行一些更大的数据集,则小型基准测试可能不反映现实。
除非我们可以访问您的数据集,知道您正在执行哪些计算(也就是说,几乎拥有您的代码),并且具有使用两个“软件包”运行它的经验,否则我或任何其他人都无法告诉您哪个解决方案在您的系统上与您的数据集上提供最佳性能。
而且,对于其他标准(“易用性”等),这些更多地基于“个人意见”,因此首先不适合SO问题。

那些信息确实有帮助。但是,正如我目前在问题中所指出的那样,它仍然没有给我一个令人信服的理由来选择其中之一。我将尝试进行更多测试并进行编辑。 - Praveen
“帮我选择一个解决方案”是一种基于个人观点的回答,因为几乎从来没有一个真正的技术性答案可以对某个特定的解决方案说“是”或“否”。你将不得不根据你认为最好的解决方案做出选择,而不是向SO提问。 - Mats Petersson
我明白了。我想我希望答案像“在Python中,我应该使用NumPy向量操作还是for循环来对两个大数组求和?”这样清晰明了,给出一个粗略的类比。我会采纳你的建议。 - Praveen
@Praveen:我已经在我的回答中添加了更多内容。我仍然认为我们无法对“我应该选择哪个?”这种类型的问题给出一个好的答案。像“在使用基准测试时使用优化”这样的技术性问题,是绝对可以回答的。但是“哪个对我来说更好”不是其中之一——因为即使您向我们发送了一个完整的基准测试,并准确地表示了您可能使用的数据,我的计算机的性能可能比您计划使用的计算机更好或更差,从而扭曲了结果。 - Mats Petersson
所以,我最终测试了一个包含4500个元素的数组(这是我程序中使用的数字),并计算出程序进行1e5次运行时加法和乘法所需的平均时间。我打开了-O3和-ffast-math,因为我不太关心精度。结果发现,std::complex<float> 和基于gsl_complex_add和gsl_complex_mul的自定义加法和乘法函数的gsl_complex_float表现大致相同。因此,我选择std::complex以便实现简单。感谢所有的帮助! - Praveen
一个注意事项:PowerPC CPU 是硬件的一个例子,其中(非向量)浮点运算是以双精度完成的,但结果可以舍入为单精度。当单精度数据被加载到浮点寄存器中时,它会被扩展为双精度。 - p_a_c

1
这个答案不仅取决于优化标志,还取决于编译GSL库和你的特定代码使用的编译器。例如:如果您使用gcc编译gsl和icc编译程序,则可能会看到(显着的)差异(我已经用std::pow与gsl_pow做了这个测试)。此外,“./configure”生成的标准makefile不会使用激进的浮点数优化(例如,它不在gcc中包括fast-math标志),因为当存在这些优化时,一些GSL例程(例如微分方程求解器)无法通过其严格的精度测试。
GSL的一个重要特点是库的模块化。如果您不需要双精度精度,则可以单独使用激进的浮点数优化编译“gsl_complex.h”、“gsl_complex_math.h”和“math.c”(但是您需要删除math.c中的“#include ”行)。另一种策略是使用激进的浮点数优化编译整个库的单独版本,并测试精度是否对您的特定问题没有影响(这是我的最喜欢的方法)。
编辑:我忘记提到“gsl_complex.h”还具有“gsl_complex”的浮点版本。
typedef struct
  {
    float dat[2];
  }
gsl_complex_float;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接