Информация об изменениях

Сообщение Re[7]: Разработка на чистом C от 31.10.2016 16:16

Изменено 31.10.2016 16:17 Pavel Dvorkin

Здравствуйте, _NN_, Вы писали:

_NN>Здравствуйте, Pavel Dvorkin, Вы писали:


_NN>Ок более приближённый тест функция верхнего уровня не оптимизируется , скажем это функция обратного вызова.


<skipped>

Что-то я этот код плохо понимаю

1. CallThrowFunction нигде не вызывается и никому не нужна
2. TestErrorCode вызывает CallErrorCodeFunction, но не анализирует результат. Поэтому имеем следующее. Вот цикл



    for (int i = 0; i < count; i++)
    {
        result = TestErrorCode(result, count);
01081030  mov         ecx,esi  
01081032  call        TestErrorCode (01081000h)  
01081037  mov         esi,eax  
01081039  dec         edx  
0108103A  jne         wmain+20h (01081030h)  
    }


Все верно, TestErrorCode вызывется count раз

А теперь смотрим код TestErrorCode

Упс!

--- c:\users\dvorkin\documents\visual studio 2013\projects\consoleapplication8\consoleapplication8.cpp 
    CallErrorCodeFunction(count);
    return result + 1;
01081000  lea         eax,[ecx+1]  
}
01081003  ret


Так что ты просто время пустого цикла померил.

А теперь смотрим второй цикл

    for (int i = 0; i < count; i++)
    {
        result = TestThrow(result, count);
01081060  mov         ecx,esi  
01081062  call        TestErrorCode (01081000h)  
01081067  mov         esi,eax  
01081069  dec         edx  
0108106A  jne         wmain+50h (01081060h)  
    }


Вот тебе и раз. Компилятор почему-то решил, что здесь можно TestErrorCode вызвать вместо TestThrow. Но это не ошибка компилятора — если в TestThrow вставить printf, вызывается, как положено, TestThrow. Видимо, оптимизатор решил, что можно и ту вызвать, все равно. Может быть, потому что ErrorCodeFunction и ThrowFunction совпадают.

А пока что ты еще раз время пустого цикла померил. Времена совпадают

Кстати, если убрать __declspec(noinline), то оба времени равны 0, так как компилятор вообще оба цикла выбрасывает вместе со всем содержимым.

Непростая штука это оптимизатор, очень непростая

Ну а если по существу... Конечно, в зависимости от 1) как часто выполняется throw и 2) как соотносится количество команд на проверку кода возврата с количество команд в самой функции — соотношение может варьироваться в широких пределах.
Если сама функция содержит десяток команд, то дополнительные команды проверки кода возврата могут повлиять на скорость работы. Если же функция читает что-то с диска или из сети, то время для проверки кода возврата есть бесконечно малое по сравнению с временем работы функции.
Если throw происходит один раз на 100500 вызовов — его время есть бесконечно малое по сравнению с суммарным временем работы этих 100500 вызовов. Если каждые 5 раз — то может быть и сравнимо.

Так что истина конкретна.

А я всего лишь хотел показать, что собственно throw — try- catch достаточно затратная операция. А дальше — зависит от того, на фоне каких других затрат.
Re[7]: Разработка на чистом C
Здравствуйте, _NN_, Вы писали:

_NN>Здравствуйте, Pavel Dvorkin, Вы писали:


_NN>Ок более приближённый тест функция верхнего уровня не оптимизируется , скажем это функция обратного вызова.


<skipped>

Что-то я этот код плохо понимаю

1. CallThrowFunction нигде не вызывается и никому не нужна
2. TestErrorCode вызывает CallErrorCodeFunction, но не анализирует результат. Поэтому имеем следующее. Вот цикл



    for (int i = 0; i < count; i++)
    {
        result = TestErrorCode(result, count);
01081030  mov         ecx,esi  
01081032  call        TestErrorCode (01081000h)  
01081037  mov         esi,eax  
01081039  dec         edx  
0108103A  jne         wmain+20h (01081030h)  
    }


Все верно, TestErrorCode вызывется count раз

А теперь смотрим код TestErrorCode

Упс!

--- c:\users\dvorkin\documents\visual studio 2013\projects\consoleapplication8\consoleapplication8.cpp 
    CallErrorCodeFunction(count);
    return result + 1;
01081000  lea         eax,[ecx+1]  
}
01081003  ret


Так что ты просто время почти пустого цикла померил.

А теперь смотрим второй цикл

    for (int i = 0; i < count; i++)
    {
        result = TestThrow(result, count);
01081060  mov         ecx,esi  
01081062  call        TestErrorCode (01081000h)  
01081067  mov         esi,eax  
01081069  dec         edx  
0108106A  jne         wmain+50h (01081060h)  
    }


Вот тебе и раз. Компилятор почему-то решил, что здесь можно TestErrorCode вызвать вместо TestThrow. Но это не ошибка компилятора — если в TestThrow вставить printf, вызывается, как положено, TestThrow. Видимо, оптимизатор решил, что можно и ту вызвать, все равно. Может быть, потому что ErrorCodeFunction и ThrowFunction совпадают.

А пока что ты еще раз время почти пустого цикла померил. Времена совпадают

Кстати, если убрать __declspec(noinline), то оба времени равны 0, так как компилятор вообще оба цикла выбрасывает вместе со всем содержимым.

Непростая штука это оптимизатор, очень непростая

Ну а если по существу... Конечно, в зависимости от 1) как часто выполняется throw и 2) как соотносится количество команд на проверку кода возврата с количество команд в самой функции — соотношение может варьироваться в широких пределах.
Если сама функция содержит десяток команд, то дополнительные команды проверки кода возврата могут повлиять на скорость работы. Если же функция читает что-то с диска или из сети, то время для проверки кода возврата есть бесконечно малое по сравнению с временем работы функции.
Если throw происходит один раз на 100500 вызовов — его время есть бесконечно малое по сравнению с суммарным временем работы этих 100500 вызовов. Если каждые 5 раз — то может быть и сравнимо.

Так что истина конкретна.

А я всего лишь хотел показать, что собственно throw — try- catch достаточно затратная операция. А дальше — зависит от того, на фоне каких других затрат.