Уточнение редактора:. Когда это было изначально опубликовано, возникли две проблемы:
- Эффективность тестирования снижается в три раза, если добавление, казалось бы, несущественного заявления
- Время, затраченное на завершение теста, по-видимому, меняется случайным образом
Вторая проблема решена: случайность возникает только при работе под отладчиком.
Остальная часть этого вопроса следует понимать как относящуюся к первой отметке выше, и в контексте запуска в режиме VС++ 2010 Express Release с оптимизацией "Максимизировать скорость" и "поддерживать быстрый код".
В разделе комментариев есть комментарии, в которых говорится о втором моменте, но теперь их можно игнорировать.
У меня есть симуляция, где, если я добавлю простой оператор if в цикл while, который запускает фактическое моделирование, производительность падает примерно в три раза (и я выполняю много вычислений в цикле while, n-body gravity для солнечной системы, помимо других вещей), хотя оператор if почти никогда не выполняется:
if (time - cb_last_orbital_update > 5000000)
{
cb_last_orbital_update = time;
}
с time
и cb_last_orbital_update
, являющимися как типом double
, так и определенными в начале основной функции, где этот оператор if тоже. Обычно есть вычисления, которые я хочу запустить там тоже, но не имеет значения, удалю их. Оператор if, как он выше, оказывает такое же влияние на производительность.
Переменная time
- это время моделирования, она увеличивается с шагом 0,001 в начале, поэтому требуется длительное время, пока оператор if не будет выполнен в первый раз (я также включил печать сообщения, чтобы узнать, выполняется, но это не так, или, по крайней мере, только тогда, когда это предполагается). Несмотря на это, производительность снижается в 3 раза даже в первые минуты симуляции, когда она еще не была выполнена. Если я прокомментирую строку
cb_last_orbital_update = time;
то он снова работает быстрее, поэтому это не проверка на
time - cb_last_orbital_update > 5000000
это определенно простой акт записи текущего времени моделирования в эту переменную.
Кроме того, если я пишу текущее время в другую переменную вместо cb_last_orbital_update
, производительность не уменьшается. Таким образом, это может быть проблемой при назначении нового значения переменной, которая используется для проверки выполнения "if"? Это все выстрелы в темноте.
Отказ от ответственности: я довольно новичок в программировании и извиняюсь за весь этот текст.
Я использую Visual С++ 2010 Express, дезактивация функции прекомпилированного заголовка stdafx.h
также не имеет значения.
EDIT: базовая структура программы. Обратите внимание, что нигде, кроме того, в конце цикла while (time += time_interval;
) не было изменено time
. Кроме того, cb_last_orbital_update
имеет только 3 вхождения: декларация/инициализация плюс два раза в выражении 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:
Хотя это обходное решение работает, оно работает только в сочетании с использованием __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++;
}
Я удаляю __declspec(noinline)
только из функции F_Get_Body_Name
, код становится медленнее. Аналогично, если я удалю выполнение этой функции или снова добавлю __declspec(noinline)
, код будет работать быстрее. Все остальные функции имеют __declspec(noinline)
.
ИЗМЕНИТЬ 7: Поэтому я изменил функцию переключателя на
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, проблема с кэшем команд процессора тогда/код становится слишком большим?