Вопрос Первый
Есть ли элегантное решение на С++, чтобы предотвратить необходимость объявления сложных переменных объекта, которые используются только в цикле вне цикла по причинам эффективности?
Подробное объяснение
Коллега поднял интересный момент. к нашей политике кода, которая заявляет (перефразирует): всегда используйте минимальную область для переменных и объявляйте переменную при первой инициализации.
Пример кодирования Пример:
// [A] DO THIS
void f() {
...
for (int i=0; i!=n; ++i) {
const double x = calculate_x(i);
set_squares(i, x*x);
}
...
}
// [B] DON'T do this:
void f() {
int i;
int n;
double x;
...
for (i=0; i!=n; ++i) {
x = calculate_x(i);
set_squares(i, x*x);
}
...
}
Это хорошо и хорошо, и в этом нет ничего плохого, пока вы не перейдете от примитивных типов к объектам. (для определенного типа интерфейса)
Пример:
// [C]
void fs() {
...
for (int i=0; i!=n; ++i) {
string s;
get_text(i, s); // void get_text(int, string&);
to_lower(s);
set_lower_text(i, s);
}
...
}
Здесь строка s будет разрушена, она освободит память каждый цикл цикла, а затем каждый цикл функции get_text
должен будет вновь выделить память для s-буфера.
Было бы более эффективно писать:
// [D]
string s;
for (int i=0; i!=n; ++i) {
get_text(i, s); // void get_text(int, string&);
to_lower(s);
set_lower_text(i, s);
}
так как теперь выделенная память в буфере s будет сохранена между циклами цикла, и очень вероятно, что мы сэкономим на распределениях.
Отказ от ответственности: Обратите внимание: Поскольку это циклы, и мы говорим о распределении памяти, я не считаю преждевременную оптимизацию думать об этом проблема в целом. Конечно, есть случаи и циклы, где накладные расходы не имеют значения; но n
имеет тенденцию ворчания, которая будет больше, чем Dev ожидает изначально, и код имеет тенденцию к ворчанию в контексте, где производительность имеет значение.
В любом случае, теперь более эффективным способом для "общей" конструкции цикла является нарушение локальности кода и объявление сложных объектов неуместными "на всякий случай". Это делает меня довольно непростым.
Обратите внимание, что я пишу его так:
// [E]
void fs() {
...
{
string s;
for (int i=0; i!=n; ++i) {
get_text(i, s); // void get_text(int, string&);
to_lower(s);
set_lower_text(i, s);
}
}
...
}
не является решением, так как читаемость еще больше!
Вдумчивая далее, интерфейс функции get_text
в любом случае не является идиоматическим, так как вчера все параметры были такими же, а "хороший" интерфейс возвращался по значению:
// [F]
for (int i=0; i!=n; ++i) {
string s = get_text(i); // string get_text(int);
to_lower(s);
set_lower_text(i, s);
}
Здесь мы не платим double за распределение памяти, так как очень вероятно, что s
будет построено через RVO из возвращаемого значения, поэтому для [F] мы выплачиваем то же самое накладные расходы, что и в [C], В отличие от случая [C], мы не можем оптимизировать этот вариант интерфейса.
Таким образом, нижняя строка кажется, что использование минимальной области (может) ухудшить производительность и использование чистых интерфейсов Я по крайней мере считаю возврат по значению намного более чистым, чем этот параметр ref ref stuff предотвратит возможности оптимизации - по крайней мере, в общем случае.
Проблема заключается не столько в том, что иногда приходится отказываться от чистого кода для эффективности, проблема в том, что как только Devs начнет находить такие особые случаи, весь Руководство по кодированию (см. [A], [B]) теряет полномочия.
Теперь вопрос будет выглядеть следующим образом: см. первый абзац