Методы кодирования C для производительности или размера кода - помимо того, что делает компилятор - PullRequest
5 голосов
/ 15 июля 2009

Я смотрю, что может сделать программист на C, который может определить производительность и / или размер сгенерированного объектного файла.

Например,
1. Объявление простых функций get / set как встроенных может повысить производительность (за счет большего размера)
2. Для циклов, которые не используют значение самой переменной цикла, обратный отсчет до нуля, а не до определенного значения и т.д.

Похоже, что компиляторы теперь продвинулись до уровня, когда "простые" трюки (как два пункта выше) вообще не требуются. Соответствующие опции во время компиляции делают работу в любом случае. Черт, я также видел здесь сообщения о том, как компиляторы обрабатывают рекурсию - это было очень интересно! Так что же нам делать на уровне C? :)

Моя конкретная среда: GCC 4.3.3 переназначена для архитектуры ARM (v4). Но ответы на другие компиляторы / процессоры также приветствуются и будут жаловаться.

PS: Мой подход идет вразрез с обычным подходом «сначала код! Потом тесты» и наконец оптимизация ».

Редактировать : Точно так же, как это случилось, я нашел аналогичный пост после публикации вопроса: Должны ли мы все еще оптимизировать "в малом"?

Ответы [ 6 ]

6 голосов
/ 15 июля 2009

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

Это относится ко всем языкам программирования, но если вы программируете на C, производительность, вероятно, критична для вас, поэтому она особенно актуальна.

5 голосов
/ 15 июля 2009

«Всегда» знают временную и пространственную сложность ваших алгоритмов. Компилятор никогда не сможет выполнить эту работу так хорошо, как вы. :)

3 голосов
/ 15 июля 2009

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

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

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

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

2 голосов
/ 15 июля 2009

Чтобы добавить к тому, что Мартин говорит выше об удобстве кэширования:

  • изменение порядка структур, так что поля, к которым обычно обращаются вместе, могут быть в одной строке кэша (например, загружая только одну строку кэша, а не две). Вы существенно увеличиваете плотность полезных данных в своем кеш данных, делая это. Существует инструмент linux, который может помочь вам в этом: dwarves 1 . http://www.linuxinsight.com/files/ols2007/melo-reprint.pdf

  • вы можете использовать аналогичную стратегию для увеличения плотности вашего кода. В gcc вы можете отмечать горячие и холодные ветки, используя вероятные / маловероятные теги. Это позволяет gcc хранить холодные ветви отдельно, что помогает увеличить плотность icache.

А теперь для чего-то совершенно другого:

  • для полей, к которым можно получить доступ (прочитайте и запись) в разных процессорах, обратная стратегия имеет смысл. Проблема состоит в том, что для целей согласования только одному ЦПУ может быть разрешено выполнять запись по одному и тому же адресу (на самом деле одна и та же кешлайн). Это может привести к условию, называемому пинг-понгом строки кэша. Это довольно плохо и может быть хуже, если эта строка кэша содержит другие несвязанные данные. Здесь имеет смысл дополнить эти данные до длины строки кэша.

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

1 голос
/ 15 июля 2009

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

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

0 голосов
/ 17 ноября 2009

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

...