Является ли встроенный язык ассемблера медленнее, чем собственный код C ++? - PullRequest
171 голосов
/ 07 марта 2012

Я попытался сравнить производительность встроенного языка ассемблера и кода C ++, поэтому я написал функцию, которая добавляет два массива размером 2000 для 100000 раз. Вот код:

#define TIMES 100000
void calcuC(int *x,int *y,int length)
{
    for(int i = 0; i < TIMES; i++)
    {
        for(int j = 0; j < length; j++)
            x[j] += y[j];
    }
}


void calcuAsm(int *x,int *y,int lengthOfArray)
{
    __asm
    {
        mov edi,TIMES
        start:
        mov esi,0
        mov ecx,lengthOfArray
        label:
        mov edx,x
        push edx
        mov eax,DWORD PTR [edx + esi*4]
        mov edx,y
        mov ebx,DWORD PTR [edx + esi*4]
        add eax,ebx
        pop edx
        mov [edx + esi*4],eax
        inc esi
        loop label
        dec edi
        cmp edi,0
        jnz start
    };
}

Вот main():

int main() {
    bool errorOccured = false;
    setbuf(stdout,NULL);
    int *xC,*xAsm,*yC,*yAsm;
    xC = new int[2000];
    xAsm = new int[2000];
    yC = new int[2000];
    yAsm = new int[2000];
    for(int i = 0; i < 2000; i++)
    {
        xC[i] = 0;
        xAsm[i] = 0;
        yC[i] = i;
        yAsm[i] = i;
    }
    time_t start = clock();
    calcuC(xC,yC,2000);

    //    calcuAsm(xAsm,yAsm,2000);
    //    for(int i = 0; i < 2000; i++)
    //    {
    //        if(xC[i] != xAsm[i])
    //        {
    //            cout<<"xC["<<i<<"]="<<xC[i]<<" "<<"xAsm["<<i<<"]="<<xAsm[i]<<endl;
    //            errorOccured = true;
    //            break;
    //        }
    //    }
    //    if(errorOccured)
    //        cout<<"Error occurs!"<<endl;
    //    else
    //        cout<<"Works fine!"<<endl;

    time_t end = clock();

    //    cout<<"time = "<<(float)(end - start) / CLOCKS_PER_SEC<<"\n";

    cout<<"time = "<<end - start<<endl;
    return 0;
}

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

А вот и результат.

Функция версии сборки:

Debug   Release
---------------
732        668
733        680
659        672
667        675
684        694
Average:   677

Функция версии C ++:

Debug     Release
-----------------
1068      168
 999      166
1072      231
1002      166
1114      183
Average:  182

Код C ++ в режиме выпуска почти в 3,7 раза быстрее кода сборки. Почему?

Полагаю, что написанный мной ассемблерный код не так эффективен, как сгенерированный GCC. Обычному программисту, как я, трудно писать код быстрее, чем его противник, сгенерированный компилятором. Значит ли это, что я не должен доверять производительности написанного моими руками ассемблера, сосредоточиться на C ++ и забыть о языке ассемблера?

Ответы [ 22 ]

0 голосов
/ 19 ноября 2017

Компилятор c ++ после оптимизации на организационном уровне будет генерировать код, который будет использовать встроенные функции целевого процессора. HLL никогда не будет опережать или превосходить ассемблер по нескольким причинам; 1.) HLL будет скомпилирован и выведен с кодом Accessor, проверкой границ и, возможно, встроенным сборщиком мусора (ранее адресовавшим область действия в манере ООП), все требующие циклов (триггеры и флопсы). В наши дни HLL отлично справляется с работой (включая более новый C ++ и другие, такие как GO), но если они превосходят ассемблер (а именно ваш код), вам необходимо обратиться к документации по процессору - сравнения с неаккуратным кодом, безусловно, неубедительны, и компилируемые языки, такие как ассемблер, все решают вплоть до кода операции HLL абстрагирует детали и не устраняет их, иначе приложение не будет запущено, даже если оно будет распознано операционной системой хоста.

Большая часть кода на ассемблере (прежде всего объекты) выводится как «безголовая» для включения в другие исполняемые форматы с гораздо меньшей необходимой обработкой, следовательно, это будет намного быстрее, но гораздо более небезопасно; если исполняемый файл выводится ассемблером (NAsm, YAsm и т. д.), он все равно будет работать быстрее, пока полностью не совпадет с HLL-кодом по функциональности, тогда результаты могут быть точно взвешены.

Вызов объекта кода на основе ассемблера из HLL в любом формате неизбежно увеличит накладные расходы обработки, а также вызовы пространства памяти, использующие глобально распределенную память для переменных / постоянных типов данных (это относится как к LLL, так и к HLL). Помните, что в конечном выводе процессор в конечном итоге использует его как api и abi относительно аппаратного обеспечения (код операции), так и ассемблеры, и «компиляторы HLL» по существу / принципиально идентичны, за исключением единственного истинного исключения - читаемости (грамматика).

Консольное приложение Hello world в ассемблере, использующем FAsm, составляет 1,5 КБ (а в Windows это даже меньше, чем в FreeBSD и Linux) и превосходит все, что GCC может выбросить в свой лучший день; Причины - неявное заполнение с помощью nops, проверка доступа и проверка границ. Настоящая цель - чистые библиотеки HLL и оптимизируемый компилятор, который нацелен на процессор «хардкорным» способом, и большинство делает это в наши дни (наконец). GCC не лучше, чем YAsm - это практика кодирования и понимание разработчика, которые находятся под вопросом, и «оптимизация» приходит после изучения новичков и промежуточного обучения и опыта.

Компиляторы должны связывать и собирать для вывода в том же коде операции, что и ассемблер, потому что эти коды - это все, что ЦП, кроме (CISC или RISC [PIC тоже]). YAsm оптимизировал и значительно очистил ранние NAsm, что в конечном итоге ускорило весь вывод этого ассемблера, но даже тогда YAsm, как и NAsm, по-прежнему создает исполняемые файлы с внешними зависимостями для библиотек ОС от имени разработчика, поэтому пробег может варьироваться. В заключение, C ++ невероятно и гораздо более безопасен, чем ассемблер на 80+ процентов, особенно в коммерческом секторе ...

0 голосов
/ 08 марта 2012

Сборка может быть быстрее, если ваш компилятор генерирует много OO кодов поддержки.

Редактировать:

Для downvoters: OP пишет"Должен ли я ... сосредоточиться на C ++ и забыть о ассемблере?"и я поддерживаю мой ответ.Вы всегда должны следить за кодом, генерируемым ОО, особенно при использовании методов.Не забывать о языке ассемблера означает, что вы будете периодически просматривать сборку, которую генерирует ваш OO-код, который, я считаю, необходим для написания хорошо работающего программного обеспечения.

На самом деле, это относится ко всему компилируемому коду, а не только к OO.

...