Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/129.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++ 如何正确地对[templated]C++;程序_C++_Optimization_Benchmarking - Fatal编程技术网

C++ 如何正确地对[templated]C++;程序

C++ 如何正确地对[templated]C++;程序,c++,optimization,benchmarking,C++,Optimization,Benchmarking,我现在需要优化C++代码。我正在写一个分子模拟库,我需要添加一个新功能。我以前已经尝试过添加这个特性,但后来我使用了嵌套循环中调用的虚拟函数。我对此感觉不好,第一次实施证明这是个坏主意。但是,这对于测试概念来说是可以的 现在我需要这个特性尽可能快(没有汇编代码或GPU计算,这仍然是C++,比更少的可读性)。 现在我对模板和类策略有了更多的了解(来自Alexandrescu的优秀著作),我认为编译时代码生成可能是解决方案 然而,在将其实现到库中之前,我需要对设计进行测试。问题是关于测试这个新特性

我现在需要优化C++代码。我正在写一个分子模拟库,我需要添加一个新功能。我以前已经尝试过添加这个特性,但后来我使用了嵌套循环中调用的虚拟函数。我对此感觉不好,第一次实施证明这是个坏主意。但是,这对于测试概念来说是可以的

现在我需要这个特性尽可能快(没有汇编代码或GPU计算,这仍然是C++,比更少的可读性)。 现在我对模板和类策略有了更多的了解(来自Alexandrescu的优秀著作),我认为编译时代码生成可能是解决方案

然而,在将其实现到库中之前,我需要对设计进行测试。问题是关于测试这个新特性效率的最佳方法

显然,我需要打开优化,因为如果没有这个g++(可能还有其他编译器),将在目标代码中保留一些不必要的操作。我还需要在基准测试中大量使用新特性,因为1e-3秒的增量可以区分好的设计和坏的设计(在实际程序中,此特性将被称为百万次)

问题是,G++在优化时有时“太聪明”,如果考虑到计算结果从未被使用,则可以去掉整个循环。在查看输出汇编代码时,我已经看过一次了

如果我向stdout添加一些打印,那么编译器将被迫在循环中进行计算,但我可能主要会对iostream实现进行基准测试

那么,如何对从库中提取的一个小特征进行校正基准测试呢? 相关问题:在一个小装置上进行这种体外试验是正确的方法还是我需要整个环境

谢谢你的建议


似乎有几种策略,从允许微调的编译器特定选项到应该与每个编译器一起工作的更通用的解决方案,如
volatile
extern

我想我会尝试所有这些。 非常感谢您的回答

除非你有一个非常激进的编译器(可能发生),否则我建议你计算一个校验和(只需将所有结果相加)并输出校验和


除此之外,您可能希望在运行任何基准测试之前查看生成的汇编代码,以便可以直观地验证任何循环是否正在实际运行。

编译器只允许消除不可能发生的代码分支。只要它不能排除应该执行分支,它就不会消除它。只要某个地方存在某种数据依赖关系,代码就会存在并运行。编译器在估计程序的哪些方面不会运行时不是很聪明,也不要尝试运行,因为这是一个NP问题,几乎不可计算。它们有一些简单的检查,比如if(0),但仅此而已

我的拙见是,您可能在前面遇到了一些其他问题,例如C/C++计算布尔表达式的方式

但是不管怎样,因为这是一个速度测试,你可以自己检查是否调用了一些东西——一次不运行,然后另一次运行一个返回值测试。或正在递增的静态变量。在测试结束时,打印出生成的数字。结果将是相同的

回答你关于体外试验的问题:是的,这样做。如果你的应用程序非常耗时,那么就这么做。另一方面,您的描述暗示了一个不同的问题:如果您的增量在1e-3秒的时间范围内,那么这听起来像是一个计算复杂性的问题,因为所讨论的方法必须非常频繁地调用(对于少数运行,1e-3秒是可以忽略的)

您正在建模的问题域听起来非常复杂,数据集可能非常庞大。这样的事情总是很有趣的。不过,首先要确保您拥有绝对正确的数据结构和算法,然后再进行所需的微优化所以,我要说的是,先看看整个上下文。;-)


出于好奇,您计算的问题是什么?

您可以控制编译的优化-O1、-O2等只是一组开关的别名

从手册页

       -O2 turns on all optimization flags specified by -O.  It also turns
       on the following optimization flags: -fthread-jumps -falign-func‐
       tions  -falign-jumps -falign-loops  -falign-labels -fcaller-saves
       -fcrossjumping -fcse-follow-jumps  -fcse-skip-blocks
       -fdelete-null-pointer-checks -fexpensive-optimizations -fgcse
       -fgcse-lm -foptimize-sibling-calls -fpeephole2 -fregmove -fre‐
       order-blocks  -freorder-functions -frerun-cse-after-loop
       -fsched-interblock  -fsched-spec -fschedule-insns  -fsched‐
       ule-insns2 -fstrict-aliasing -fstrict-overflow -ftree-pre
       -ftree-vrp
您可以调整并使用此命令来帮助您缩小要调查的选项范围

       ...
       Alternatively you can discover which binary optimizations are
       enabled by -O3 by using:

               gcc -c -Q -O3 --help=optimizers > /tmp/O3-opts
               gcc -c -Q -O2 --help=optimizers > /tmp/O2-opts
               diff /tmp/O2-opts /tmp/O3-opts Φ grep enabled

一旦找到了Cullret优化,您就不需要cout了。

如果这对您来说是可能的,您可以尝试将代码拆分为:

  • 要测试的库在所有优化都已打开的情况下编译
  • 一个测试程序,以友好方式链接库,并关闭优化

否则,您可能会为带有optimize属性的测试函数指定不同的优化级别(看起来您正在使用gcc…)(请参阅)

我不知道GCC是否有类似的功能,但在VC++中,您可以使用:

#pragma optimize

有选择地打开/关闭优化。如果GCC具有类似的功能,您可以使用完全优化进行构建,并在必要时将其关闭,以确保代码被调用。

您可以在单独的cpp文件中创建一个伪函数,该函数不做任何操作,但将计算结果的任何类型作为参数。然后,您可以使用计算结果调用该函数,强制gcc生成中间代码,唯一的惩罚是调用函数的成本(除非您经常调用函数,否则不会扭曲结果!)。

这只是一个不需要的优化的小例子:

#include <vector>
#include <iostream>

using namespace std;

int main()
{
double coords[500][3];

//perform a simple initialization of all coordinates:
for (int i=0; i<500; ++i)
 {
   coords[i][0] = 3.23;
   coords[i][1] = 1.345;
   coords[i][2] = 123.998;
 }


cout << "hello world !"<< endl;
return 0;
}
#包括
#包括
使用名称空间std;
int main()
{
双坐标[500]
#include <iostream>

// Mark coords as extern.
// Compiler is now NOT allowed to optimise away coords
// This it can not remove the loop where you initialise it.
// This is because the code could be used by another compilation unit
extern double coords[500][3];
double coords[500][3];

int main()
{

//perform a simple initialization of all coordinates:
for (int i=0; i<500; ++i)
 {
   coords[i][0] = 3.23;
   coords[i][1] = 1.345;
   coords[i][2] = 123.998;
 }


std::cout << "hello world !"<< std::endl;
return 0;
}
StartBenchmarking(); // ie, read a performance counter
for (int i=0; i<500; ++i)
 {
   coords[i][0] = 3.23;
   coords[i][1] = 1.345;
   coords[i][2] = 123.998;
 }
StopBenchmarking(); // what comes after this won't go into the timer

// this is just to force the compiler to use coords
double foo;
for (int j = 0 ; j < 500 ; ++j )
{
  foo += coords[j][0] + coords[j][1] + coords[j][2]; 
}
cout << foo;
void test1( volatile double *coords )
{
  //perform a simple initialization of all coordinates:
  for (int i=0; i<1500; i+=3)
  {
    coords[i+0] = 3.23;
    coords[i+1] = 1.345;
    coords[i+2] = 123.998;
  }
}
template<typename T> void sink(T const& t) {
   volatile T sinkhole = t;
}