У меня есть простая функция с внутренним циклом - она масштабирует входное значение, ищет выходное значение в таблице поиска и копирует его в пункт назначения. (ftol_ambient - это трюк, который я скопировал из Интернета для быстрого преобразования float в int).
for (i = 0;  i < iCount;  ++i)
{
    iScaled = ftol_ambient(*pSource * PRECISION3);
    if (iScaled <= 0)
        *pDestination = 0;
    else if (iScaled >= PRECISION3)
        *pDestination = 255;
    else
    {
        iSRGB = FloatToSRGBTable3[iScaled];
        *pDestination = iSRGB;
    }
    pSource++;
    pDestination++;
}
Теперь моя таблица поиска является конечной, а поплавки бесконечны, поэтому есть возможность ошибок "один за другим". Я создал копию функции с некоторым кодом для обработки этого случая. Обратите внимание, что единственное отличие состоит в добавлении 2 строк кода - пожалуйста, игнорируйте уродливое кастинг-литье.
for (i = 0;  i < iCount;  ++i)
{
    iScaled = ftol_ambient(*pSource * PRECISION3);
    if (iScaled <= 0)
        *pDestination = 0;
    else if (iScaled >= PRECISION3)
        *pDestination = 255;
    else
    {
        iSRGB = FloatToSRGBTable3[iScaled];
        if (((int *)SRGBCeiling)[iSRGB] <= *((int *)pSource))
            ++iSRGB;
        *pDestination = (unsigned char) iSRGB;
    }
    pSource++;
    pDestination++;
}
Вот странная часть. Я тестирую обе версии с идентичным вводом 100000 элементов, повторяющихся 100 раз. На моем Athlon 64 1.8 ГГц (32-разрядный режим) первая функция занимает 0.231 секунды, а вторая (более длинная) функция занимает 0.185 секунды. Обе функции смежны в одном исходном файле, поэтому нет возможности использовать разные настройки компилятора. Я запускал тесты много раз, меняя порядок, в котором они запускаются, и тайминги примерно одинаковы каждый раз.
Я знаю, что в современных процессорах много загадок, но как это возможно?
Здесь для сравнения приведены соответствующие ассемблерные выходы компилятора Microsoft VС++ 6.
; 173  :    for (i = 0;  i < iCount;  ++i)
$L4455:
; 174  :    {
; 175  :        iScaled = ftol_ambient(*pSource * PRECISION3);
    fld DWORD PTR [esi]
    fmul    DWORD PTR [email protected]@400b8000000000000000
    fstp    QWORD PTR $T5011[ebp]
; 170  :    int i;
; 171  :    int iScaled;
; 172  :    unsigned int iSRGB;
    fld QWORD PTR $T5011[ebp]
; 173  :    for (i = 0;  i < iCount;  ++i)
    fistp   DWORD PTR _i$5009[ebp]
; 176  :        if (iScaled <= 0)
    mov edx, DWORD PTR _i$5009[ebp]
    test    edx, edx
    jg  SHORT $L4458
; 177  :            *pDestination = 0;
    mov BYTE PTR [ecx], 0
; 178  :        else if (iScaled >= PRECISION3)
    jmp SHORT $L4461
$L4458:
    cmp edx, 4096               ; 00001000H
    jl  SHORT $L4460
; 179  :            *pDestination = 255;
    mov BYTE PTR [ecx], 255         ; 000000ffH
; 180  :        else
    jmp SHORT $L4461
$L4460:
; 181  :        {
; 182  :            iSRGB = FloatToSRGBTable3[iScaled];
; 183  :            *pDestination = (unsigned char) iSRGB;
    mov dl, BYTE PTR _FloatToSRGBTable3[edx]
    mov BYTE PTR [ecx], dl
$L4461:
; 184  :        }
; 185  :        pSource++;
    add esi, 4
; 186  :        pDestination++;
    inc ecx
    dec edi
    jne SHORT $L4455
$L4472:
; 199  :    {
; 200  :        iScaled = ftol_ambient(*pSource * PRECISION3);
    fld DWORD PTR [esi]
    fmul    DWORD PTR [email protected]@400b8000000000000000
    fstp    QWORD PTR $T4865[ebp]
; 195  :    int i;
; 196  :    int iScaled;
; 197  :    unsigned int iSRGB;
    fld QWORD PTR $T4865[ebp]
; 198  :    for (i = 0;  i < iCount;  ++i)
    fistp   DWORD PTR _i$4863[ebp]
; 201  :        if (iScaled <= 0)
    mov edx, DWORD PTR _i$4863[ebp]
    test    edx, edx
    jg  SHORT $L4475
; 202  :            *pDestination = 0;
    mov BYTE PTR [edi], 0
; 203  :        else if (iScaled >= PRECISION3)
    jmp SHORT $L4478
$L4475:
    cmp edx, 4096               ; 00001000H
    jl  SHORT $L4477
; 204  :            *pDestination = 255;
    mov BYTE PTR [edi], 255         ; 000000ffH
; 205  :        else
    jmp SHORT $L4478
$L4477:
; 206  :        {
; 207  :            iSRGB = FloatToSRGBTable3[iScaled];
    xor ecx, ecx
    mov cl, BYTE PTR _FloatToSRGBTable3[edx]
; 208  :            if (((int *)SRGBCeiling)[iSRGB] <= *((int *)pSource))
    mov edx, DWORD PTR _SRGBCeiling[ecx*4]
    cmp edx, DWORD PTR [esi]
    jg  SHORT $L4481
; 209  :                ++iSRGB;
    inc ecx
$L4481:
; 210  :            *pDestination = (unsigned char) iSRGB;
    mov BYTE PTR [edi], cl
$L4478:
; 211  :        }
; 212  :        pSource++;
    add esi, 4
; 213  :        pDestination++;
    inc edi
    dec eax
    jne SHORT $L4472
Изменить: Попытка проверить гипотезу Нильса Пипенбринка, я добавил пару строк до и внутри цикла первой функции:
int one = 1;
int two = 2;
        if (one == two)
            ++iSRGB;
Время работы первой функции теперь составляет 0,152 секунды. Интересно.
Редактировать 2: Нильс отметил, что сравнение будет оптимизировано из сборки релиза, и это действительно так. Изменения в коде сборки очень тонкие, я отправлю его здесь, чтобы узнать, содержит ли он какие-либо подсказки. На данный момент мне интересно, выравнивает ли он код?
; 175  :    for (i = 0;  i < iCount;  ++i)
$L4457:
; 176  :    {
; 177  :        iScaled = ftol_ambient(*pSource * PRECISION3);
    fld DWORD PTR [edi]
    fmul    DWORD PTR [email protected]@400b8000000000000000
    fstp    QWORD PTR $T5014[ebp]
; 170  :    int i;
; 171  :    int iScaled;
; 172  :    int one = 1;
    fld QWORD PTR $T5014[ebp]
; 173  :    int two = 2;
    fistp   DWORD PTR _i$5012[ebp]
; 178  :        if (iScaled <= 0)
    mov esi, DWORD PTR _i$5012[ebp]
    test    esi, esi
    jg  SHORT $L4460
; 179  :            *pDestination = 0;
    mov BYTE PTR [edx], 0
; 180  :        else if (iScaled >= PRECISION3)
    jmp SHORT $L4463
$L4460:
    cmp esi, 4096               ; 00001000H
    jl  SHORT $L4462
; 181  :            *pDestination = 255;
    mov BYTE PTR [edx], 255         ; 000000ffH
; 182  :        else
    jmp SHORT $L4463
$L4462:
; 183  :        {
; 184  :            iSRGB = FloatToSRGBTable3[iScaled];
    xor ecx, ecx
    mov cl, BYTE PTR _FloatToSRGBTable3[esi]
; 185  :            if (one == two)
; 186  :                ++iSRGB;
; 187  :            *pDestination = (unsigned char) iSRGB;
    mov BYTE PTR [edx], cl
$L4463:
; 188  :        }
; 189  :        pSource++;
    add edi, 4
; 190  :        pDestination++;
    inc edx
    dec eax
    jne SHORT $L4457
