很less执行和几乎空,如果语句大幅降低性能的C + +

编者的澄清:当这是最初发布,有两个问题:

  • 如果看起来无关紧要的话,testing性能下降三倍
  • 完成testing所需的时间似乎随机变化

第二个问题已经解决了:随机性只发生在debugging器下运行。

这个问题的其余部分应该被理解为关于上面的第一个要点,并且在VC ++ 2010 Express的发布模式下运行,并且优化“最大化速度”和“优先快速代码”。

评论部分还有一些评论在谈论第二点,但现在可以忽略它们。


我有一个模拟,如果我添加一个简单的if语句到运行实际仿真的while循环中,性能下降大约三倍(我在while循环中运行了大量计算,太阳能的n-body重力系统除了其他东西)即使if语句几乎从未执行:

if (time - cb_last_orbital_update > 5000000) { cb_last_orbital_update = time; } 

timecb_last_orbital_update都是doubletypes的,并且在主函数的开头定义,if语句也是这样。 通常我也要在那里运行计算,但是如果我删除它们也没有什么区别。 if语句如上所述对性能有相同的影响。

可变time是模拟时间,它在开始时增加了0.001个步骤,所以在第一次执行if语句之前需要很长时间(我还包括打印消息以查看它是否正在执行,但是它不是,或者至less只有当它应该)。 无论如何,即使在仿真的第一分钟内,性能也会下降3倍,但是还没有执行一次。 如果我注释掉这一行

 cb_last_orbital_update = time; 

那么它又跑得快了,所以这不是检查

 time - cb_last_orbital_update > 5000000 

无论如何,这绝对是将当前模拟时间写入该variables的简单行为。

另外,如果我将当前时间写入另一个variables而不是cb_last_orbital_update ,则性能不会下降。 所以这可能是一个问题,分配一个新的值,用于检查是否应该执行“if”的variables? 这些都是黑暗中的镜头。

免责声明:我是相当新的编程,并为所有的文字感到抱歉。

我正在使用Visual C ++ 2010 Express,停用stdafx.h预编译头文件function也没有任何区别。

编辑:程序的基本结构。 请注意,在while循环( time += time_interval; )的末尾除了time改变之外, 此外, cb_last_orbital_update只有三次发生:声明/初始化,再加上导致问题的if语句中的两次。

 int main(void) { ... double time = 0; double time_interval = 0.001; double cb_last_orbital_update = 0; F_Rocket_Preset(time, time_interval, ...); while(conditions) { Rocket[active].Stage[Rocket[active].r_stage].F_Update_Stage_Performance(time, time_interval, ...); Rocket[active].F_Calculate_Aerodynamic_Variables(time); Rocket[active].F_Calculate_Gravitational_Forces(cb_mu, cb_pos_d, time); Rocket[active].F_Update_Rotation(time, time_interval, ...); Rocket[active].F_Update_Position_Velocity(time_interval, time, ...); Rocket[active].F_Calculate_Orbital_Elements(cb_mu); F_Update_Celestial_Bodies(time, time_interval, ...); if (time - cb_last_orbital_update > 5000000.0) { cb_last_orbital_update = time; } Rocket[active].F_Check_Apoapsis(time, time_interval); Rocket[active].F_Status_Check(time, ...); Rocket[active].F_Update_Mass (time_interval, time); Rocket[active].F_Staging_Check (time, time_interval); time += time_interval; if (time > 3.1536E8) { std::cout << "\n\nBreak main loop! Sim Time: " << time << std::endl; break; } } ... } 

编辑2:

这是汇编代码的区别。 左边是线条的快速代码

 cb_last_orbital_update = time; 

在右边慢行代码的行。

编辑4:

所以,我发现一个解决方法似乎迄今为止工作得很好:

 int cb_orbit_update_counter = 1; // before while loop if(time - cb_orbit_update_counter * 5E6 > 0) { cb_orbit_update_counter++; } 

编辑5:

尽pipe该解决方法确实起作用,但它只能与使用__declspec(noinline)结合使用。 我只是从函数声明中删除了这些,看看是否改变了任何东西,而且确实如此。

编辑6:对不起,这变得困惑。 我把这个函数中删除__declspec(noinline)函数追踪到了性能较低的罪魁祸首,这个函数是在if中执行的:

 __declspec(noinline) std::string F_Get_Body_Name(int r_body) { switch (r_body) { case 0: { return ("the Sun"); } case 1: { return ("Mercury"); } case 2: { return ("Venus"); } case 3: { return ("Earth"); } case 4: { return ("Mars"); } case 5: { return ("Jupiter"); } case 6: { return ("Saturn"); } case 7: { return ("Uranus"); } case 8: { return ("Neptune"); } case 9: { return ("Pluto"); } case 10: { return ("Ceres"); } case 11: { return ("the Moon"); } default: { return ("unnamed body"); } } } 

if现在也不只是增加计数器:

 if(time - cb_orbit_update_counter * 1E7 > 0) { F_Update_Orbital_Elements_Of_Celestial_Bodies(args); std::cout << F_Get_Body_Name(3) << " SMA: " << cb_sma[3] << "\tPos Earth: " << cb_pos_d[3][0] << " / " << cb_pos_d[3][1] << " / " << cb_pos_d[3][2] << "\tAlt: " << sqrt(pow(cb_pos_d[3][0] - cb_pos_d[0][0],2) + pow(cb_pos_d[3][1] - cb_pos_d[0][1],2) + pow(cb_pos_d[3][2] - cb_pos_d[0][2],2)) << std::endl; std::cout << "Time: " << time << "\tcb_o_h[3]: " << cb_o_h[3] << std::endl; cb_orbit_update_counter++; } 

我从函数F_Get_Body_Name删除__declspec(noinline) ,代码变慢。 同样,如果我删除这个函数的执行或再次添加__declspec(noinline) ,代码运行得更快。 所有其他函数仍然有__declspec(noinline)

编辑7:所以我改变了开关function

 const std::string cb_names[] = {"the Sun","Mercury","Venus","Earth","Mars","Jupiter","Saturn","Uranus","Neptune","Pluto","Ceres","the Moon","unnamed body"}; // global definition const int cb_number = 12; // global definition std::string F_Get_Body_Name(int r_body) { if (r_body >= 0 && r_body < cb_number) { return (cb_names[r_body]); } else { return (cb_names[cb_number]); } } 

也使代码的另一部分更加苗条。 现在程序运行速度很快,没有任何__declspec(noinline) 。 正如ElderBug所build议的那样,CPU指令caching的问题,代码变得太大了?

我把我的钱放在英特尔的分支预测器上。 http://en.wikipedia.org/wiki/Branch_predictor

处理器假定(time-cb_last_orbital_update> 5000000)大部分时间都是假的,并相应地加载执行stream水线。

一旦条件(时间 – cb_last_orbital_update> 5000000)成真。 错误预测的延迟正在打击你。 你可能会松动10到20个周期。

 if (time - cb_last_orbital_update > 5000000) { cb_last_orbital_update = time; } 

发生了一些你并不期待的事情。

一个候选人是一些未初始化的variables挂在某处,根据您正在运行的确切代码,这些variables具有不同的值。 例如,你可能有未初始化的内存,有时是非规格化的浮点数,有时不是。

我认为应该清楚,你的代码不会做你期望的。 因此,请尝试debugging您的代码,编译启用所有警告,确保您使用相同的编译器选项(优化与非优化可以很容易地成为一个因素10)。 检查你是否得到相同的结果。

特别是当你说“它跑得更快(虽然这并不总是工作,但我看不到一个模式),也曾经改变5000000到5E6一次,但它只运行快,重新编译导致性能下降再一次没有改变任何东西,有一次它只是在重新编译两次后才运行得更慢。 它看起来很可能是你正在使用不同的编译器选项。

我会尝试另一个猜测。 这是假设的,主要是由于编译器。

我的猜测是,你使用了大量的浮点计算,并且在你的main中引入和使用double值会使编译器耗尽XMM寄存器(浮点SSE寄存器)。 这迫使编译器使用内存而不是寄存器,并导致内存和寄存器之间的大量交换,从而大大降低了性能。 这将会发生,主要是因为计算函数内联,因为函数调用是保存寄存器。

解决办法是将__declspec(noinline)添加到所有的计算函数声明中。

我build议使用Microsoft Profile Guided Optimizer – 如果编译器对这个特定的分支做出错误的假设,它将会有所帮助,并且很可能会提高代码的其他部分的速度。

解决方法,请尝试2:

代码现在看起来像这样:

 int cb_orbit_update_counter = 1; // before while loop if(time - cb_orbit_update_counter * 5E6 > 0) { cb_orbit_update_counter++; } 

到目前为止,它运行速度很快,加上代码正在执行的时候,据我所知。 再次只有一个解决方法,但如果这certificate能够四处工作,那么我很满意。

经过一些更多的testing,看起来不错。

我的猜测是,这是因为variablescb_last_orbital_update否则是只读的,所以当你在if分配variables时,会破坏编译器对只读variables的一些优化(例如,现在可能它存储在内存中而不是寄存器)。

要尝试的东西(尽pipe这可能还是不行 )是通过cb_last_orbital_updatetime来创build第三个variables,取决于条件是否为真,然后使用该variables。 据推测,编译器现在将这个variables作为一个常量来处理,但我不确定。