Какие кодирующие методы Вы используете для оптимизации C программы? [закрытый]

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

Один из стандартных вопросов, которые я задал, был о методах оптимизации. Я был вполне удивлен, что у некоторых кандидатов не было ответов.

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

Ответы на оптимизацию для скорости и размера оба принятые.

31
задан Bill the Lizard 9 August 2012 в 03:11
поделиться

23 ответа

Первые вещи сначала - не оптимизируют слишком рано. Весьма распространено провести время, тщательно оптимизируя блок кода только, чтобы найти, что это не было узкое место, что Вы думали, что это было. Или, выражаясь иначе, "Перед созданием его быстро сделайте, это работать"

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

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

, Как только Вы решили, что необходимо оптимизировать код, какой бит Вы оптимизируете? Используйте профилировщика. Сосредоточьте свое внимание (сначала) на областях, которые чаще всего используются.

Поэтому, что можно сделать о тех областях?

  • минимизируют проверку условия. Проверка условий (например, завершение условий для циклов) время, что это не тратится на фактическую обработку. Проверка условия может быть минимизирована с методами как развертывание цикла.
  • В некоторой проверке условия обстоятельств может также быть устранен при помощи указателей функции. Например, при реализации конечного автомата, можно найти, что реализация обработчиков для отдельных государств как небольшие функции (с универсальным прототипом) и хранение "следующего состояния" путем хранения указателя функции следующего обработчика более эффективны, чем использование большого оператора переключения с кодом обработчика, реализованным в операторах отдельного случая. YMMV.
  • минимизируют вызовы функции. Вызовы функции обычно несут нагрузку сохранения контекста (например, запись локальных переменных, содержавшихся в регистрах к стеку, сохраняя указатель вершины стека), поэтому если Вы не должны звонить, это - сэкономленное время. Одна опция (если Вы оптимизируете для скорости и не пространства) состоит в том, чтобы использовать подставляемые функции.
  • , Если вызовы функции неизбежны, минимизируют данные, которые передаются функциям. Например, передающие указатели, вероятно, будут более эффективными, чем передающие структуры.
  • При оптимизации для скорости выбирают типы данных, которые являются собственным размером для платформы. Например, на процессоре на 32 бита, вероятно, будет более эффективно управлять значениями на 32 бита, чем 8 или 16 битовых значений. (примечание стороны - стоит проверить, что компилятор делает то, что Вы думаете, что это. У меня были ситуации, где я обнаружил, что мой компилятор настоял на том, чтобы делать арифметику на 16 битов на 8 битовых значениях со всем из к, и от преобразований для движения с ними)
  • Находят данные, которые могут быть предварительно вычислены, и или вычислить во время инициализации или (еще лучше) во время компиляции. Например, при реализации CRC можно или вычислить значения CRC на лету (использующий многочлен непосредственно), который является большим для размера (но ужасным для производительности), или можно генерировать таблицу всех временных значений - который является большим быстрым внедрением, в ущерб размеру.
  • Локализуют Ваши данные. При управлении блобом данных часто процессор может быть в состоянии ускорить вещи путем хранения всего этого в кэше. И Ваш компилятор может быть в состоянии использовать более короткие инструкции, которые подходят для более локализованных данных (например, инструкции, которые используют смещения на 8 битов вместо 32 битов)
  • В том же духе, локализуйте свои функции. По тем же причинам.
  • Разрабатывают предположения, что можно сделать об операциях, которые Вы выполняете и находите способы использования их. Например, на платформе на 8 битов, если единственная операция, что в Вы делаете на 32 битовых значениях, является инкрементом, можно найти, что можно добиться большего успеха, чем компилятор путем встраивания (или создания макроса) именно с этой целью, вместо того, чтобы использовать нормальную арифметическую операцию.
  • Избегают дорогих инструкций - подразделение является главным примером.
  • ключевое слово "регистра" может быть Вашим другом (хотя, надо надеяться, Ваш компилятор имеет довольно хорошую идею о Вашем использовании регистра). Если Вы собираетесь использовать "регистр", вероятно, что необходимо будет объявить локальные переменные, что Вы хотите редактора "регистра" сначала.
  • согласовываться с Вашими типами данных. Если Вы делаете арифметику на смеси типов данных (например, короткие замыкания и ints, удваивается и плавания), тогда, компилятор добавляет неявные преобразования типов для каждого несоответствия. Это - потраченные впустую циклы CPU, которые не могут быть необходимыми.

большинство упомянутых выше опций может использоваться в качестве части нормальной практики без любых вредных воздействий. Однако, если Вы действительно пытаетесь восполнить лучшую производительность: - занимаются расследованиями, где можно (безопасно) отключить проверку ошибок. Это не рекомендуется, но это сохранит Вас некоторое пространство и циклы. - Ручные части ремесла Вашего кода в ассемблере. Это, конечно, означает, что Ваш код больше не является портативным, но где это не проблема, можно найти сбережения здесь. Знайте хотя это, там потенциально время потерянные движущиеся данные в и из регистров, которые Вы имеете в Вашем распоряжении (т.е. удовлетворить использование регистра Вашего компилятора). Также знайте, что Ваш компилятор должен делать довольно хорошее задание самостоятельно. (конечно, существуют исключения)

37
ответ дан 27 November 2019 в 21:22
поделиться

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

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

, Например, у Вас есть строка к поиску и затем 50 возможным значениям. Таким образом вместо того, чтобы делать 50 strcmps, Вы добавляете все 50 строк к хеш-таблице и даете каждому уникальное число (только необходимо сделать это однажды). Тогда Вы поиск целевая строка в хеш-таблице и имеет один большой переключатель со всеми 50 случаями (или имейте указатели функций).

При поиске вещей с едиными наборами входа (как правила css), я использую быстрый код, чтобы отслеживать единственные возможные солитоны и затем выполнить итерации мысли те для нахождения соответствия. Как только у меня есть соответствие, я сохраняю результаты в хеш-таблицу (как кэш) и затем использую результаты кэша, если я получаю тот же самый входной набор позже.

Мои основные инструменты для более быстрого кода:

хеш-таблица - для быстрых поисков и для кэширования результатов

qsort - это - единственный вид, который я использую

bsp - для поиска вещей на основе области (карта, представляющая и т.д.)

0
ответ дан 27 November 2019 в 21:22
поделиться

Иногда необходимо решить, является ли это большим количеством пространства или большей скоростью, которая Вы после, который приведет почти к противоположной оптимизации. Например, чтобы получить все возможное от Вас пространство, Вы пакет структуры, например, пакет #pragma (1) и использование битовые поля в структурах. Для большей скорости Вы упаковываете, чтобы выровняться с предпочтением процессоров и избежать битовых полей.

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

2
ответ дан 27 November 2019 в 21:22
поделиться
  1. Измеряют уровень.
  2. Использование реалистические и нетривиальные сравнительные тесты. Помните, что "все быстро для маленького N" .
  3. Использование профилировщик для нахождения горячих точек.
  4. Сокращают количество динамических выделений памяти, доступов к диску, доступов базы данных, доступов к сети и переходов пользователя/ядра, потому что они часто имеют тенденцию быть горячими точками.
  5. Измеряют уровень.

, Кроме того, необходимо измерить уровень.

2
ответ дан 27 November 2019 в 21:22
поделиться

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

Первое правило в оптимизации скорости - , находят Ваш критический путь .
Обычно Вы будете находить, что этот путь не является таким длинным и не настолько сложным. Трудно сказать универсальным способом, как оптимизировать это, это, зависят от того, что является Вами выполнение и что находится в Вашем питании сделать. Например, Вы хотите, обычно избегают memcpy на критическом пути, поэтому когда-либо необходимо ли использовать DMA или оптимизировать ли, но что, если у Вас hw нет DMA? проверьте, является ли memcpy реализация лучшей, если не переписывают его.
не используют динамическое выделение вообще во встроенном, но если Вы по некоторым причинам делаете не, делают это в критическом пути.
Организуют Ваши приоритеты потока правильно, что, правильно реальный вопрос, и это - ясно конкретная система.
Мы используем очень простые инструменты для анализа узких мест, простой макрос, которые хранят метку времени и индекс. Немногие (2-3) выполнения в 90% случаев найдут, где Вы проводите свое время.
И последний обзор кода очень важный. В большей части случая мы избегаем, чтобы проблема производительности во время кода рассмотрела очень эффективный путь:)

2
ответ дан 27 November 2019 в 21:22
поделиться

Подставляемые функции! Вдохновленный профильными вентиляторами здесь я представил мое приложение и нашел небольшую функцию, которая делает некоторый bitshifting на кадрах MP3. Это делает приблизительно 90% всех вызовов функции в моем applcation, таким образом, я заставил его встроить и вуаля - программа теперь использует половину процессорного времени, которое это сделало прежде.

2
ответ дан 27 November 2019 в 21:22
поделиться

Собирающиеся профили выполнения кода получают Вас 50% пути там. Другие 50% имеют дело с анализированием этих докладов.

Далее при использовании GCC или VisualC ++ можно использовать "ведомую оптимизацию профиля", где компилятор возьмет информацию от предыдущего выполнения и перенесет инструкции сделать ЦП более счастливым.

2
ответ дан 27 November 2019 в 21:22
поделиться

В эти дни самые важные вещи в optimzation:

  • уважение кэша - пытается получить доступ к памяти в простых шаблонах и не разворачивает циклы только для забавы. Используйте массивы вместо структур данных с большим преследованием указателя, и это, вероятно, будет быстрее для небольших количеств данных. И ничего не делайте слишком большим.
  • задержка предотвращения - старается избегать подразделений и материала, это медленно, если другие вычисления сразу зависят от них. Доступы памяти, которые зависят от других доступов памяти (т.е., [b [c]]) плохи.
  • непредсказуемость предотвращения - много if/elses с непредсказуемыми условиями или условиями, которые представляют больше задержки, действительно испортит Вас. Существует много математических приемов без веток, которые полезны здесь, но они увеличивают задержку и только полезны, если Вам действительно нужны они. Иначе просто запишите простой код и не имейте сумасшедших условий цикла.

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

3
ответ дан 27 November 2019 в 21:22
поделиться

Трудный подвести итог...

  • Структуры данных:

    • Разделение структуры данных в зависимости от случая использования чрезвычайно важно. Распространено видеть структуру, которая содержит данные, к которым получают доступ на основе управления потоком. Эта ситуация может значительно понизить использование кэша.
    • Для принятия во внимание размера строки кэша и правил упреждающей выборки.
    • Для переупорядочения членов структуры для получения последовательного доступа к ним от кода
  • Алгоритмы:

    • Занимают время, чтобы думать о Вашей проблеме и найти корректный алгоритм.
    • Знают ограничения алгоритма, который Вы выбираете (radix-sort/quick-sort для 10 элементов, которые будут отсортированы, не мог бы быть лучшим выбором).
  • Низкий уровень:

    • Что касается последних процессоров не рекомендуется развернуть цикл, который имеет маленькое тело. Процессор обеспечивает свой собственный механизм обнаружения для этого и закоротит целый раздел его конвейера.
    • Доверие предварительное устройство выбора HW. Конечно, если Ваши структуры данных хорошо разработаны;)
    • Забота о Вашей строке кэша L2 промахи.
    • Попытка уменьшить как можно больше локальный рабочий набор Вашего приложения как процессоры склоняется к меньшим кэшам на ядра (C2D обладала 3 МБ за ядро макс., где iCore7 обеспечит макс. из 256 КБ за ядро + умирают, 8 МБ, совместно использованных ко всем ядрам для четырехъядерного.).

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

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

Это далеко от того, чтобы быть исчерпывающим, но должно обеспечить интересную основу.

3
ответ дан 27 November 2019 в 21:22
поделиться

Другая вещь, которая не была упомянута:

  • Знают Ваши требования: не оптимизируйте для ситуаций, которых никогда не будет вряд ли или происходить, концентрат на большей части удара для маркера
3
ответ дан 27 November 2019 в 21:22
поделиться

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

, Например, если это возможно, пишут

for (i=n; i!=0; --i) { ... }

вместо

for (i=0; i!=n; ++i) { ... }
4
ответ дан 27 November 2019 в 21:22
поделиться

Поскольку для моих приложений обычно не нужно много процессорного времени дизайном, я фокусирую на размере свои двоичные файлы на диске и в памяти. Что я делаю главным образом высматривает статически измеренные массивы и заменяет их динамично выделенной памятью, где это стоит дополнительного усилия free'ing память позже. Для сокращения размера двоичного файла я ищу большие массивы, которые инициализируются во время компиляции и помещают initializiation во время выполнения.

char buf[1024] = { 0, };
/* becomes: */
char buf[1024];
memset(buf, 0, sizeof(buf));

Это удалит 1 024 нулевых байта из двоичных файлов.DATA раздел и вместо этого создаст буфер на стеке во времени выполнения и заливке это с нулями.

РЕДАКТИРОВАНИЕ: О, да, и мне нравится кэшировать вещи. Это не C конкретный, но в зависимости от того, что Вы кэшируете, это может дать Вам огромное повышение производительности.

пз: сообщите нам, когда Ваш список закончен, мне очень любопытно.;)

4
ответ дан 27 November 2019 в 21:22
поделиться

Преждевременная оптимизация является корнем всего зла!;)

4
ответ дан 27 November 2019 в 21:22
поделиться

Избегайте использования "кучи". Используйте obstacks или средство выделения пула для идентичных размерных объектов. Поместите мелочи с коротким временем жизни на стек. alloca все еще существует.

4
ответ дан 27 November 2019 в 21:22
поделиться
  • Прежде всего, используйте лучший/быстрее алгоритм. Нет никакого смысла, оптимизируя код, который является медленным дизайном.
  • При оптимизации для скорости, обменяйте память на скорость: таблицы поиска предварительно вычисленных значений, двоичных деревьев, пишут более быструю пользовательскую реализацию системных вызовов...
  • , Когда торговая скорость для памяти: используйте сжатие в оперативной памяти
5
ответ дан 27 November 2019 в 21:22
поделиться

Для оптимизации низкого уровня:

  1. макросы START_TIMER/STOP_TIMER от ffmpeg (точность уровня тактовых сигналов для измерения любого кода).
  2. Oprofile, конечно, для профилирования.
  3. Огромные объемы кодированного рукой блока (просто делают туалет-l на/common/x86 каталоге x264, и затем помнить большую часть кода, являются шаблонными).
  4. Тщательное кодирование в целом; более короткий код обычно лучше.
  5. Умные алгоритмы низкого уровня, как 64-разрядный писатель потока битов я записал, что использует только сингл если и не еще.
  6. Явное объединение записи .
  7. Принимающие во внимание важные странные аспекты процессоров, как строка кэша Intel разделила случаи Нахождения выпуска .
  8. , где можно без потерь или почти без потерь сделать раннее завершение, где проверка раннего завершения стоит намного меньше, чем скорость, каждый получает от него.
  9. На самом деле встроенный блок для задач, которые намного больше подходят для единицы x86 SIMD, такой как средние вычисления (требует проверки времени компиляции на поддержку MMX).
8
ответ дан 27 November 2019 в 21:22
поделиться

наиболее распространенные методы, с которыми я встретился:

  • развертывание цикла
  • оптимизация цикла для лучшей упреждающей выборки кэша (т.е. делают операции N в циклах M вместо NxM исключительные операции)
  • данные, выравнивающиеся
  • , подставляемые функции
  • изготовили вручную asm отрывки

Что касается общих рекомендаций, большинством из них уже звучат:

  • выбирают лучшие алгоритмы
  • , профилировщик использования
  • не оптимизирует, если это не дает 20-30%-е повышение производительности
15
ответ дан 27 November 2019 в 21:22
поделиться

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

20
ответ дан 27 November 2019 в 21:22
поделиться

Поскольку все другие сказали: профиль, профиль профиля.

Что касается фактических методов, тот, что я не думаю, был упомянут все же:

Горячий & Холодное Разделение Данных : Пребывание в кэше ЦП невероятно важно. Один способ помочь сделать это путем разделения структур данных на часто получаемый доступ ("горячий") и редко получало доступ к ("холодным") разделам.

пример: Предположим, что у Вас есть структура для клиента, который выглядит примерно так:

struct Customer
{
    int ID;
    int AccountNumber;
    char Name[128];
    char Address[256];
};

Customer customers[1000];

Теперь, позволяет, предполагают, что Вы хотите получить доступ к идентификатору и AccountNumber много, но не так имени и адресу. То, что Вы сделали бы, должно разделить его на два:

struct CustomerAccount
{
    int ID;
    int AccountNumber;
    CustomerData *pData;
};

struct CustomerData
{
    char Name[128];
    char Address[256];
};

CustomerAccount customers[1000];

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

26
ответ дан 27 November 2019 в 21:22
поделиться

основы / общий:

  • не оптимизируют, когда у Вас нет проблемы.
  • Знают Вашу платформу/ЦП...
  • ... знают, что это полностью
  • знает, что Ваш ABI
  • Позволил компилятору сделать оптимизацию, просто помочь ему с заданием.

некоторые вещи, которые на самом деле помогли:

Выбирают размер/память:

  • битовые поля Использования для хранения повторного использования bools
  • большие глобальные массивы путем накладывания с объединением (быть осторожными)

Выбирают скорость (быть осторожными):

  • использование предварительно вычислило таблицы, куда возможный
  • помещают критические функции/данные в быстродействующую память
  • , Использование выделило регистры для часто используемого globals
  • , рассчитывают к нулю, обнуляют флаг, свободен
3
ответ дан 27 November 2019 в 21:22
поделиться

Если у кого-то нет ответа на этот вопрос, возможно, он мало что знает.

Также может быть, что они знают много. Я много знаю (ИМХО :-), и если бы мне задали этот вопрос, я бы спросил вас: почему вы думаете, что это важно?

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

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

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

2
ответ дан 27 November 2019 в 21:22
поделиться

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

  • пропустите компоновщик

    , если у вас есть приложение, разделенное на два файла, например main.c и lib.c, во многих случаях вы можете просто добавить \ # include "lib.c" в свой main.c, что полностью обойдет компоновщик и позволит значительно более эффективно оптимизировать компилятор.

1
ответ дан 27 November 2019 в 21:22
поделиться

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

1
ответ дан 27 November 2019 в 21:22
поделиться
Другие вопросы по тегам:

Похожие вопросы: