Эффективность программирования на С ++ - PullRequest
3 голосов
/ 24 апреля 2011

Вот некоторый код, который я скопировал из Microsoft Developer Network
http://msdn.microsoft.com/en-us/library/dd162487(v=VS.85).aspx

LRESULT APIENTRY WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) 
{

**PAINTSTRUCT ps;  
HDC hdc;**

switch (message) 
{ 
    case WM_PAINT: 
        hdc = BeginPaint(hwnd, &ps); 
        TextOut(hdc, 0, 0, "Hello, Windows!", 15);
        EndPaint(hwnd, &ps); 
        return 0L; 

    // Process other messages.   
} 
} 

Возможно, я ошибаюсь, но я думал, что каждый раз, когда компилятор запускает оператор, подобный любому из них:

int var1  
double var2  
char var3[]  
PAINTSTRUCT ps  
HDC hdc  

компьютер создаст новую переменную.По крайней мере, было бы логично подумать, потому что это то, что вы пишете, когда хотите создать новую переменную, верно?

Я также всегда думал, что если бы у вас был такой блок кода:

for(int i = 0; i < 100; i++)
    int sum = i;

компьютер создаст 100 различных переменных с одинаковым именем sum и некоторым значением, которое содержится в i

В приведенном выше примере кода будет вызвана функция WndProcМного-много раз в течение программы, однако две переменные, которые создает функция, называемые "ps" и "hdc", будут использоваться только в некоторых случаях, когда функция выполняется.

Так будет и компьютерсоздание множества отдельных дополнительных переменных PAINTSTRUCT и HDC, которые он никогда не будет использовать?
Не будет ли, по крайней мере, несколько более эффективным объявление ps и hdc после case WM_PAINT: как это?

case WM_PAINT:
{ 
     **PAINTSTRUCT ps;  
     HDC hdc;**

     hdc = BeginPaint(hwnd, &ps); 
     TextOut(hdc, 0, 0, "Hello, Windows!", 15);
     EndPaint(hwnd, &ps); 
 }
 return 0L; 

Ответы [ 5 ]

2 голосов
/ 24 апреля 2011

Возможно, я ошибаюсь, но я думал, что каждый раз, когда компилятор запускает оператор, подобный любому из следующих:

int var1  
double var2  
char var3[]  
PAINTSTRUCT ps  
HDC hdc  

компьютер создает новую переменную.1007 *

Эти переменные соответствуют объему памяти во время выполнения.Поскольку они являются локальными, они, вероятно, живут в стеке, и выделение пространства для этих локальных элементов так же просто, как перемещение указателя стека.Это невероятно быстро.

Я также всегда думал, что если у вас есть такой блок кода: for (int i = 0; i <100; i ++) int sum = i; </p>

компьютер создаст 100 различных переменных с одним и тем же именем «сумма» и некоторым значением, которое содержится в «i»

Вероятно, что здесь произойдет, это то же место в стекебудет повторно использоваться для каждой итерации цикла.

Не будет ли, по крайней мере, немного более эффективным объявить "ps" и "hdc" после case WM_PAINT: как это?

Нет, они местныевероятно, размещены в стеке, и их пространство зарезервировано, как только метод будет введен независимо от того, где вы объявили их в коде.

Наконец, беспокойство по поводу производительности здесь в лучшем случае является микрооптимизацией.Сосредоточьтесь на значении вашего кода.Позвольте компилятору максимально эффективно преобразовать его в исполняемый код.Вы можете начать беспокоиться только в том случае, если у вас есть проблемы с производительностью, которые не соответствуют ожидаемым требованиям к производительности вашего клиента.

1 голос
/ 24 апреля 2011

компьютер создаст новую переменную

Это только внешне верно. Технически процессор фактически перемещает указатель стека на определенную длину. Если объявленные переменные не имеют конструкторов, накладные расходы для этого действия практически равны нулю.

Так будет ли компьютер создавать множество отдельных дополнительных переменных PAINTSTRUCT и HDC, которые он никогда не будет использовать?

Нет, это не так, когда функция возвращает все пространство стека, выделенное для этих переменных, освобождается и ничего не теряется.

Не будет ли, по крайней мере, немного более эффективным объявить "ps" и "hdc" после case WM_PAINT: как это?

Маловероятно. Компилятор, скорее всего, увидит объявления и выделенное пространство для этих переменных в стеке, независимо от того, где вы разместите объявление для них. Опять же, это так до тех пор, пока у переменных нет конструкторов. Если они это сделают, то добавление их в регистр сохранит вызов для конструктора.

1 голос
/ 24 апреля 2011

Для примитивной локальной переменной - указателей, int, double, float и т. Д. - и массивов примитивов, объявление переменной действительно «выделяет» для нее место, но все, что на самом деле происходит, это то, что компилятор говорит: собираюсь вызвать это место в стеке, на три слова ниже указателя стека, i . " Никаких реальных вычислений не происходит - никакие инструкции не выполняются вообще! Поэтому объявление «суммы» внутри или вне цикла совершенно не имеет значения!

Однако для локально объявленного экземпляра класса все обстоит иначе. Объявление объекта означает, что конструктор будет вызываться при входе в блок, а деструктор будет вызываться при выходе из него, и что действительно приводит к выполнению инструкций - так что вам нужно быть осторожны.

1 голос
/ 24 апреля 2011

Вы правы, что компьютер будет объявлять много локальных переменных, но, к счастью, это чрезвычайно эффективно. При первом вызове функции компилятор обычно выводит код для создания локальных переменных в виде одной инструкции сборки, поэтому затраты на создание этих переменных незначительны. В вашем примере цикла компилятор обычно не создает и не уничтожает переменную несколько раз. Скорее, он создает его один раз, а затем использует пространство на каждой итерации цикла. Другими словами, переменная логически создается и уничтожается на каждой итерации, но в сгенерированном коде она фактически распределяется между несколькими итерациями.

Как правило, не беспокойтесь об эффективности ваших программ, если у вас нет особых причин полагать, что ваша производительность ниже, чем должна быть. Ваш приоритет должен состоять в том, чтобы писать чистый, элегантный код, а не оптимизированный код с первого раза, так как без реального запуска программы очень трудно понять, окажет ли ваша оптимизация полезный эффект на время выполнения программы. Обычно вы используете профилировщик, чтобы определить, какие участки кода медленны, а затем сосредоточить свои усилия на оптимизации.

0 голосов
/ 24 апреля 2011

Компилятор внесет существенные изменения в ваш код, если он будет иметь тот же результат.Особенно в сборке выпуска (без отладки).

Это означает, что если у вас неиспользуемый код, вы объявляете переменные слишком рано или у вас есть переменные, которые не используются вне циклов, компилятор может избавиться от них или переместить их.

/* If you never use these, the compiler will probably throw them out */
int var1;
double var2;
char var3[];
PAINTSTRUCT ps;
HDC hdc;

/* Nothing here effects other code, so the code will probably be removed */
for(int i = 0; i < 100000; ++i)
{
    int j = i * 7;
}

Если у вас нет действительно строгих требований к производительности, лучшее, что вы можете сделать, - это сделать ваш код более читабельным.Если переменная используется в меньшем объеме, разместите ее там, потому что ее легче читать, а не потому, что она может сделать вашу программу быстрее.

Компьютеры действительно хороши в выполнении кода, о котором вы говорите,где вы просто используете процессор и память.Места, где вы можете реально улучшить производительность, это когда вы работаете с относительно медленными устройствами, такими как сеть, или читаете с диска.В этих случаях имеют значение отдельные байты памяти.

Место, где оптимизация ЦП / памяти имеет наибольшее значение, находится в более сложных алгоритмах или при работе с большим количеством данных, таких как графика или алгоритм поиска..

Это не значит, что вы не должны думать об оптимизации - просто вы должны выбирать свои сражения.Оптимизируйте удобочитаемость вашего кода в большинстве программ и оптимизируйте производительность, когда у вас более сложный алгоритм или вы имеете дело с низкоуровневой графикой или сложными структурами / алгоритмами данных, и только тогда, когда вы протестировалиВаша программа немного вышла и обнаружила, что она работает медленно.

...