Да-да. По коду видно, что это какие-то вычисления. Автору было бы неплохо написать, что этот код решает такую-то задачу. Конечно можно по функциям сидеть и разбирать и найти известные алгоритмы, но у меня не возникает желания напрягать мозг для этого.
Скорее, не учат программировать в другом. Подобный стиль написания кода похож на запись математических уравнений, которым учат матмехов, а вот умению писать читаемый код их не учит никто.
Я не то чтобы готов, на самом деле, искать причины; я просто неоднократно видел программный код в исполнении матмеха, и каждый раз он был похож на вышеприведённый, и даже хуже. Вот, красивый примерчик нашёл (разбивка на строки моя, форматирование оригинала):
Зато наверняка и математика была слабее. Каждому — своё.
Ряд профессоров до сих пор на Фортране пишет. У программиста на Фортране своё, аутентичное понимание адекватности имён идентификаторов. В частности, длина имени должна быть не более 6 символов. Сложно при этом выдвигать промышленные требования к читаемости кода, и слава Богу, что они не пытаются этим заниматься.
Как матмех (физтех) скажу, что сложно только до выделения X[i+(N+1)*(j+1)] в семантическую единицу. Потом формула становится очень хорошо читаемой. Как будто фокус на микроскопе настроил, и все стало хорошо видно.
Я так же (хотя это и выглядит как будто наоборот) с SQL поступаю — использую всегда полные имена «базаданных».«таблица».«поле» Текста получается много, зато всегда ясно, что откуда берется. А так как писать и переписывать SQL мне приходится много, то экономится много времени на том, что не надо смотреть, откуда же берется это поле. При чтении такого SQL тоже достаточно «настроить фокус» и все хорошо видно.
Отлично написано и наглядно. В статью в букмарки, книги в 2read.
p.s. нынче в большинстве случаев дешевле купить мощнее сервер, чем нанять толкового программиста который знает толк в оптимизации. или разрешить ему потратить только на это 1-5 дней.
p.p.s. еще есть злая штука — преждевременная и избыточная оптимизация. когда в момент разработки кода, когда еще толком не ясно что и как код будет делать — его пробуют улучшить и ускорить. в результате 2 дня оптимизируют функцию, которая потом выпиливается, т.к. agile и планы поменялись. (пример из жизни)
А можно узнать, что можно 2 дня оптимизировать в функции? Если Вы знаете оптимизирующие хаки языка, в котором пишите, то Вы либо сразу пишите код с оптимизацией, либо в течение 5 минут заменяете все что надо на оптимизированный вариант.
Пример с python
писать вместо str += 'vasya' str = ''.join([str,'vasya'])
Или вместо for i in range(5) i = 0; while i < 5: i+=1
Можно, например функция прорисовки обьекта или какой-то кастомный контрол. Во время разработки черновика приложения допустимо использовать такое решение, которое займет меньше времени. Иначе, если оптимизировать все что пишешь — уйдет больше времени на придумывание хороших решений и к дню дедлайна будет сделана треть приложения. Отлично сделана — но за нее не заплатят.
Все по делу, любой человек, который занимался оптимизацией подтвердит :)
Единственное замечание — попытка повторного использования счетчиков в циклах вкупе for private из open mp. Зачем, если можно объявлять счетчик в каждом цикле и он автоматически будет объявлен приватным при распараллеливании? Ну и заодно дать счетчикам осмысленные имена.
скажем так, почти все оптимизации банальны, кроме SSE: все таки не каждый программист знает такие низкоуровневые функции, которые в большинстве приложений собственно и не требуются.
А данный код на том же фортране смотрелся бы куда изящнее.
Знаете, я бы не стал говорить обо всех оптимизациях.
Я бы даже сказал, что алгоритмические оптимизации зачастую очень сложно понять без специальных пояснений. В качестве примера, почитайте Майкла Абраша «Дзен графического программирования»: там очень наглядно показано, как из одного алгоритма получить совершенно другой потём серии оптимизаций.
Имелись в виду оптимизации рассмотренные в статье. А банальны, они в том смысле, что для тех кто этим занимается постоянно, приходится делать одни и те же оптимизации ( имею в виду научные вычисления).
Меня сейчас пристрелят, но не могу не процитировать (произносится с дебильным выражением лица):
— «Папа, а с кем это ты сейчас разговаривал?» :)
По сути обсуждаемого вопроса: синтетические тесты тем плохи, что напоминают по полезности перекладывание кучки щебня в другую кучку детским совочком. В реальной жизни такая штуковины либо будет исполняться «как есть», пусть даже на бейсике и будет считать 30 секунд, а не 0.3, да и фиг с ней; если же надо считать часто и много — то начиная с подсчетов на GPU и заканчивая решениями на ПЛИС (в том числе и с использованием lookup tables), ибо прирост скорости будет несравнимый.
Насколько я понимаю, то с чем занимался любовными утехами автор — это реальный проект. В котором надо было улучшить производительность. Видимо кого-то-таки не устраивало, что считалось 30 секунд вместо 0,3. И этот кто-то — начальство.
Да тут вроде довольно-таки явно видно, что пример — тестовый. Я не верю в то, что будут писать ТАК реальную систему, там же непонятно нифига начиная от имен функций.
Ну, или просто у меня мало опыта, но я реальной жизни такого не видел еще. :)
Всякое бывает. Помню года 4 назад пришлось мне разбираться в хтмплн-ом отчете с джаваскриптами, где все функции принимали параметр c именем O и назывались f1, f2, f3… Что при отсутствии типизации весьма доставляло.
А, да, еще: из общения с математиками у меня возникло ощущение, что пока они свои формулы не упростят донельзя, не оптимизируют, не обсудят с коллегами, и не посидят вместе еще пару дней, споря над результатом — на реализацию не выдадут. :)
Всё это здорово, только при чем здесь высокая производительность?
Из всего здесь написанного к делу имеет отношение только выбор правильных алгоритмов, который может повысить производительность в разы (точнее, на шаги сложности функции O(*) ). Неправильный выбор алгоритма — это баг, один из самых серьезных типов багов вообще. Этот баг не позволяет использовать программу на больших объемах входных данных (то есть в реальных условиях), и править этот баг нужно _обязательно_.
Да, повышение локальности _данных_ (а не ссылок) тоже может в разы ускорить программу, но это опять же — правильный выбор структур данных и работающих с ними алгоритмов.
Всё остальное… нет, ну не то чтобы неверно. Всё это правильно. Но просто эффект… Ну, допустим, вы, просидев пару месяцев с профайлером и книжкой Гербера-Билла, добились ускорения работы программы в целом… на 20%. А нагрузка на систему за эти пару месяцев выросла на 50%. Вы потратили кучу усилий, но копали не в ту сторону.
«Низкоуровневая» оптимизация нужна и полезна, только не для высокой производительности, а для систем реального времени. Вот библиотеки Intel для работы, скажем, с изображениями — они просто бесценны, если ваша программа большую часть времени обрабатывает изображения. Какие-нибудь трансляции, или распознавание образов в реальном времени, или системы слежения за чем-то — и всё это на относительно слабом железе — вот в этих случаях вы будете выжимать из железа любые проценты. Но это «высокая удельная производительность».
А в высоконагруженных системах по мере необходимости просто добавляется один-два-десять-сто новых серверов за 1000-1500 долларов каждый. И задача программиста — обеспечить безпроблемное горизонтальное масштабирование системы на эти новые сервера. Была обработка одной структуры данных на 20 серверах, стала на 25 — и всё это прозрачно, нигде ничего не останавливалось и не переиндексировалось. И вот там уже начинаются свои трюки, засады и просторы для улучшений.
Почему не over9000? =)
Я говорю о реальных проектах без явных ляпов, написанных нормальными программистами.
Понятно, что можно придумать примеры, поддающиеся ускорению и в 200, и в 200 тысяч раз.
Насчет блога — прямого попадания вроде нет, но по идее ближе всего блоги о Си++
Реальность такова, что единственный смысл использовать сегодня Си или Си++ — как раз таки выжать максимум удельной производительности.
1) А ты теперь скомпиль оба исходника в ICC с полной оптимизацией и сравни результаты.
2) А как ты замеряешь время? По времени выполнения кода или по процессорному времени которое даётся потоку. К тому как я вижу ты юзаешь и системные функции, по этому тут уже очень много факторов будут. тебе надо будет считать процессорное время для kernel mode и для user mode. Потому что замеры обычного времени тебе ничего не дадут. Тем более для такого малого времени тестирования.
Подсчет по тактам процессора — это жесть. Потому что тут ты всё равно не учитываешь многозадачность. за 1 секунду, смена потока произойдет огромное кол-во раз и как на крути у тебя будут данные завышены и сильно привязаны к состоянию системы. Потому как процессоры новые имею в себе функции энергосбережения и автоматического уменьшения таковой частоты в зависимости от нагрузки. И по этому первичная калибровка тебе мало что даст. И опять же malloc и free — как не крути но тут будут вызваны функции системы по выделению памяти, а скорость этой операция напрямую зависит от загруженности памяти, т.к. будут затрачено время на поиск свободной страницы виртуальной и физической страницы (конечно это время сверх мало, но всёже вносит свои ограничения) и таким образом по немного но собирается куча обстоятельств которые вносят погрешности
С многозадачностью можно бороться запуская на чистой машине, где нет других «тяжелых» задач и путем многократного замера.
Энергосбережение и уменьшение тактовой частоты не при делах, если в proc cpuinfo указан флаг constant_tsc (гарантирующий стабильность счетчика тактов). Ибо счетчик тактов (не только он) часто используется ОС-ми для реализации функций выдачи текущего времени. Разве что с turbo boost будут небольшие проблемы.
Не каждый malloc/free вызывает ОС, более того, выделение-освобождение часто можно вынести за пределы измеряемого кода.
Мда, на ускорение посмотреть не получилось… Проц Core 2 Duo E8500, ось 32-бит. С включённым openmp неоптимизированный вариант выполнился нормально, оптимизированный застрял сам в себе. После отключения openmp оба варианта выполнились, но дали разные результаты. Тестировалось в gcc 4.4.3 и ~4.5.0.
Предполагаю, что неоптимизированная версия считала на fpu (387), который немного точнее sse2. Лечить опциями "-msse2 -mfpmath=sse". На моей машине помогло.
хм, ну никто и не будет разворачивать цикл с нечетным количеством итераций по 4. упомянутый мною подводный камень с кратностью. берут потом остаток от деления и досчитывают…
Если не использовать системных вызовов (вход в который дороже ошибки предсказания в разы), и работать либо с хорошо буферизованным файлом либо с mmaped файлом, то будет работать.
При раскрутке циклов повышаются шансы, что компилятор что-нибудь более умное сгенерирует, например, векторизует, или префетч догадается поставить.
branch prediction от раскрутки выигрывает мало, т.к. предсказание переходов просто обязано правильно предсказывать циклы. Неверных предсказаний на каждый заход в цикл будет от 0 (ибо переходы назад — предсказываются как происходящие) до 2-3 (после нескольких итераций предсказатель увидит, что ошибался и поверит в этот цикл). Ну еще +1 ошибка на выход из цикла. )
Что еще выигрывает от раскрутки — логика цикла (проверка пора ли выходить) срабатывает реже (не на каждую итерацию, а на итерации/ степень раскрутки)
насколько я помню, это вообще был итоговый конкурс, после зимней школы оптимизации. дали такой странный код, со словами: у вас есть 20 минут на его оптимизацию, юзайте всякие профайлеры и все что хотите. суть в том была, чтобы не сами алгоритмы оптимизировани на высоком уровне, а пользуюясь сторонними утилитами находить узкие места программы и переписывать их нормальным образом. насколько я помню, 80% кода вообще ничего не делала (что-то типа гоняла данные туда-сюда), достаточно было этот факт подменить, выкинуть эту часть и все — победа в кармане :) но за 20 минут реально сложновато в этом сраче разобраться.
Про техники оптимизации