2010-01-06 2 views
2

Не то, чтобы я сейчас в этой ситуации, но меня интересует только ответ ...ASM-оптимизация потеряна после компиляции?

Предполагая, что у вас есть код, написанный на C/C++, и вы хотите его вручную оптимизировать, изменив его в ASM ,

Что произойдет, если вы измените код на C/C++ и перекомпилируете из источника. Конечно, оптимизация только что скомпилированного файла теряется. Как вы избегаете, чтобы эти оптимизации нужно переделывать каждый раз при компиляции проекта? Создаете ли вы отдельные исходные файлы для частей, которые необходимо оптимизировать, чтобы сделать их менее сложными? Или есть какой-то автоматический инструмент для этого ...? Думаете, вы не можете использовать сравнение/патча для этого ...

Пожалуйста, поделитесь своим опытом, благодаря

ответ

7

Пишут некоторые функции в отдельном файл ASM и вызова этих функции из вашего кода на C/C++. Или вы записываете встроенную сборку непосредственно в свой код C/C++.

Другими словами, вы могли бы начать с некоторого кода C/C++, чтобы получить некоторый базовый код ASM, но после того, как вы начнете тонкой настройки, вы удалите исходный C/C++ код и заменить ее с кодом ASM, используя один из этих двух методов.

+3

ну, эху, вы помещаете их под ifdef, чтобы их можно было использовать для проверки, если возникают проблемы, или внезапно придется переносить на другую арку. –

7

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

В MSVC:

// asm_overview.cpp 
// processor: x86 
void __declspec(naked) main() 
{ 
    // Naked functions must provide their own prolog... 
    __asm { 
     push ebp 
     mov ebp, esp 
     sub esp, __LOCAL_SIZE 
    } 

    // ... and epilog 
    __asm { 
     pop ebp 
     ret 
    } 
} 

В GCC:

__asm__ ("movl %eax, %ebx\n\t" 
      "movl $56, %esi\n\t" 
      "movl %ecx, $label(%edx,%ebx,$4)\n\t" 
      "movb %ah, (%ebx)"); 

Заметим также, что делает изменения ASM после компиляции и оптимизации является то, что только для тех, кто знает точно, что они делают. Мало того, что компилятор оптимизирует структуру таким образом, чтобы человек не мог (по крайней мере, не один без возможностей калькулятора освещения), он также выполняет гораздо более сложные анализы кода, который мы когда-либо могли бы сделать.

Доверьтесь своему компилятору. Это величайший инструмент, с которым вы когда-либо работали;).

+0

И для MSVC этот код компилируется в 64-разрядном режиме? :) – Dmitry

+0

Это скопируйте копию из MSDN - только для того, чтобы предоставить обзор синтаксиса внедрения, лично я использовал asm только в GCC;> –

+1

Вам действительно нужно делать эту отвратительную строку в GCC ?! –

1

Вы либо связываетесь с модулями, явно написанными на ассемблере, либо используете встроенный asm.

3

Если ваш компилятор поддерживает его, возможно, вы ищете что-то вроде inline assembly?

2

Извините, не строго ответ на ваш вопрос, но я считаю, что люди, делающие компилятор, намного лучше в asm, чем большинство из нас. Таким образом, я больше полагаюсь на компилятор, делающий «правильную вещь», вместо того, чтобы писать некоторый код asm в середине источника C++.

Еще один аргумент для меня не использовать хотя бы встроенный asm (хотя иногда мне нравится помещать __asm int 3; в код) заключается в том, что компилятор MS Visual Studio не поддерживает встроенный asm для 64-битных построений.

И последнее, вы пробовали использовать другой алгоритм для оптимизации, вместо того, чтобы предполагать, что gcc (который может использовать SSE_whatever_is_the_current_version оптимизации) создает код asm хуже, чем вы можете писать.

+1

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

+0

@ Tarydon: это приводит меня к другому вопросу: включает ли компиляция при всех оптимизациях (при условии, что GCC) * всегда * создает «рабочий» результат? Или есть варианты, чтобы очень внимательно посмотреть, прежде чем включать их? приветствует – Atmocreations

+0

@Ammocreations: Некоторые оптимизации, такие как переупорядочение инструкций для учета задержки, всегда должны быть безопасными (если, конечно, в компиляторе нет ошибки). Иногда переупорядочивание операций с плавающей запятой может изменить выход результирующего кода из-за арифметики с конечной точностью. В GCC вы можете включить некоторые из этих небезопасных математических оптимизаций с помощью -ffast-math. Самый «опасный», который я знаю, включает строгий псевдоним (-fstrict-aliasing), который может нарушить код, например,пытается получить доступ к битам float, отправив свой адрес в int *. – celion

4

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

Если есть определенная часть кода вы хотите вручную оптимизировать путем его реализации на ассемблере, у вас есть два варианта:

  1. Написать код как ASM код в первую очередь, и связать собранный файл ASM в ваш исполняемый файл. Сначала вы можете позволить компилятору сгенерировать сборку из C/C++, а затем изменить ее, если это экономит ваше время. Но с этого момента можно управлять этим кодом на уровне ASM.
  2. Используйте встроенную сборку в коде C/C++. Поскольку ручные оптимизированные части кода ASM обычно малы, это часто лучшая идея. Использование встроенного ASM очень просто в большинстве компиляторов. Ниже приведен простой snippet for GCC:

int main(void) 
{ 
     int foo = 10, bar = 15; 
     __asm__ __volatile__("addl %%ebx,%%eax" 
          :"=a"(foo) 
          :"a"(foo), "b"(bar) 
          ); 
     printf("foo+bar=%d\n", foo); 
     return 0; 
} 

Он демонстрирует красиво, как совместить код C с ASM, переменных обмена.

2

Еще одна веская причина для использования встроенной сборки: встроенная сборка gcc-стиля дает генератору кода кучу информации (clobbers, volatile и т. Д.), Которую генератор кода может использовать, чтобы сделать вашу сборку подходящей (и встроенной!) В ваш код на C/C++, не теряя возможности для оптимизации.


Edit:

Например, код C от другого ответа:

int main(void) 
{ 
    int foo = 10, bar = 15; 
    __asm__ __volatile__("addl %%ebx,%%eax" 
         :"=a"(foo) 
         :"a"(foo), "b"(bar) 
         ); 
    printf("foo+bar=%d\n", foo); 
    return 0; 
} 

производит:

.... 
movl $15, %ebx 
movl $10, %eax 
#APP 
addl %ebx,%eax 
#NO_APP 
movl %eax, 8(%esp) 
movl $.str, (%esp) 
movl %eax, 4(%esp) 
call printf 
.... 

Переменные F и бар хранится в регистрах и даже не хранится на стек.

Смежные вопросы