Стоимость обслуживания базы программного кода SIMD

14

Вопрос:

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

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


Предыстория моего вопроса.

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

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

Пример типичной структуры кода:

  • Использование типа матрицы OpenCV ( Mat) для управления всей памятью, буфером и временем жизни.
  • После проверки размера (размеров) входных аргументов используются указатели на начальный адрес каждой строки пикселей.
  • Число пикселей и начальные адреса каждой строки пикселей из каждой входной матрицы передаются в некоторые низкоуровневые функции C ++.
  • Эти низкоуровневые функции C ++ используют встроенные функции SIMD (для архитектуры Intel и ARM NEON ), загружая и сохраняя необработанные адреса указателей.
  • Характеристики этих низкоуровневых функций C ++:
    • Исключительно одномерный (последовательный в памяти)
    • Не имеет дело с распределением памяти.
      (Каждое распределение, включая временные, обрабатывается внешним кодом с использованием средств OpenCV.)
    • Диапазон длины имен символов (внутренние, имена переменных и т. Д.) Составляет примерно 10–20 символов, что довольно много.
      (Читается как техно-болтовня.)
    • Повторное использование SIMD-переменных не рекомендуется, потому что компиляторы довольно глючат в правильном разборе кода, который не написан в стиле кодирования с «одним назначением».
      (Я подал несколько отчетов об ошибках компилятора.)

Какие аспекты программирования SIMD могут привести к тому, что обсуждение будет отличаться от общего случая? Или почему SIMD отличается?

С точки зрения первоначальной стоимости разработки

  • Хорошо известно, что первоначальная стоимость разработки SIMD-кода C ++ с хорошей производительностью составляет примерно 10-100 раз (с большим отрывом) по сравнению с небрежно написанным кодом C ++.
  • Как отмечено в ответах на Выбор между производительностью и читаемым / более чистым кодом? Большая часть кода (включая случайно написанный код и код SIMD) изначально не является ни чистой, ни быстрой .
  • Эволюционные улучшения производительности кода (как в скалярном, так и в SIMD-коде) не приветствуются (потому что это рассматривается как своего рода переработка программного обеспечения ), а стоимость и выгоды не отслеживаются.

С точки зрения склонности
(например, принцип Парето, ака правило 80-20 )

  • Даже если обработка изображений составляет всего 20% программной системы (как по размеру кода, так и по функциональности), обработка изображений сравнительно медленная (если смотреть в процентах от затраченного времени ЦП), занимая более 80% времени.
    • Это связано с эффектом размера данных: типичный размер изображения измеряется в мегабайтах, тогда как типичный размер данных без изображения измеряется в килобайтах.
  • В коде обработки изображений SIMD-программист обучен автоматически распознавать 20% код, содержащий горячие точки, путем идентификации структуры цикла в коде C ++. Таким образом, с точки зрения программиста SIMD, 100% «важного кода» является узким местом производительности.
  • Часто в системе обработки изображений существует множество горячих точек, которые занимают сопоставимые пропорции времени. Например, может быть 5 горячих точек, каждая из которых занимает (20%, 18%, 16%, 14%, 12%) общего времени. Для достижения высокой производительности все горячие точки должны быть переписаны в SIMD.
    • Это суммируется как правило всплывающих подсказок : шарик нельзя вытолкнуть дважды.
    • Предположим, есть несколько воздушных шаров, скажем, 5 из них. Единственный способ уничтожить их - это выдвинуть их один за другим.
    • После того, как первый шарик вытолкнут, оставшиеся 4 шарика теперь составляют более высокий процент от общего времени выполнения.
    • Чтобы сделать дальнейший выигрыш, нужно затем выскочить другой шар.
      (Это противоречит правилу оптимизации 80-20: хороший экономический результат может быть достигнут после сбора 20% плодов с минимальными висячими.)

С точки зрения читабельности и обслуживания

  • SIMD-код явно трудно читать.

    • Это верно даже в том случае, если следовать всем передовым методам разработки программного обеспечения, таким как именование, инкапсуляция, правильность констант (и выявление побочных эффектов), декомпозиция функций и т. Д.
    • Это верно даже для опытных программистов SIMD.
  • Оптимальный код SIMD сильно искажен (см. Примечание) по сравнению с его эквивалентным кодом прототипа C ++.

    • Существует много способов искажать код SIMD, но только 1 из 10 таких попыток даст приемлемо быстрые результаты.
    • (То есть в настройках 4x-10x прироста производительности, чтобы оправдать высокую стоимость разработки. На практике наблюдался еще более высокий прирост).

(Примечание)
Это основной тезис проекта MIT Halide - цитирование заголовка статьи дословно:

«Отсоединение алгоритмов от расписаний для легкой оптимизации конвейеров обработки изображений»

С точки зрения прямой применимости

  • Код SIMD строго привязан к единой архитектуре. Каждая новая архитектура (или каждое расширение регистров SIMD) требует переписывания.
  • В отличие от большинства программных разработок, каждый кусок кода SIMD обычно пишется с единственной целью, которая никогда не меняется.
    (За исключением портирования на другие архитектуры.)
  • Некоторые архитектуры поддерживают идеальную обратную совместимость (Intel); некоторые отстают на тривиальную величину (ARM AArch64, заменяя vtblна vtblq), но этого достаточно, чтобы заставить некоторый код не скомпилироваться.

С точки зрения навыков и обучения

  • Не ясно, какие предпосылки знаний необходимы, чтобы правильно обучить нового программиста писать и поддерживать код SIMD.
  • Выпускники колледжей, которые изучили программирование SIMD в школе, кажется, презирают и отклоняют это как непрактичный путь карьеры.
  • Чтение разборки и низкоуровневое профилирование производительности считаются двумя основными навыками написания высокопроизводительного кода SIMD. Однако неясно, как систематически обучать программистов этим двум навыкам.
  • Современная архитектура процессора (которая значительно отличается от того, чему учат в учебниках) делает обучение еще более трудным.

С точки зрения правильности и связанных с дефектами затрат

  • Одна функция обработки SIMD на самом деле достаточно связна, чтобы можно было установить правильность с помощью:
    • Применяя формальные методы (с ручкой и бумагой) , и
    • Проверка целочисленных выходных диапазонов (с кодом прототипа и выполнением вне времени выполнения) .
  • Однако процесс проверки является очень дорогостоящим (тратит 100% времени на проверку кода и 100% времени на проверку прототипа), что в три раза увеличивает и без того дорогостоящую стоимость разработки кода SIMD.
  • Если какой-либо ошибке удается ускользнуть через этот процесс проверки, почти невозможно «починить» (исправить), кроме как заменить (перезаписать) предполагаемую неисправную функцию.
  • SIMD-код страдает от тупых дефектов в компиляторе C ++ (оптимизирующий генератор кода).
    • SIMD-код, сгенерированный с использованием шаблонов выражений C ++, также сильно страдает от недостатков компилятора.

С точки зрения разрушительных инноваций

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

    • MIT Halide
    • Стэнфордская темная комната
    • NT2 (Numeric Template Toolbox) и связанный с ним Boost.SIMD
  • Библиотеки с широко распространенным коммерческим использованием, по-видимому, не поддерживают SIMD.

    • Библиотеки с открытым исходным кодом кажутся теплее SIMD.
      • Недавно у меня появилось это наблюдение из первых рук после профилирования большого количества функций API OpenCV, начиная с версии 2.4.9.
      • Многие другие библиотеки обработки изображений, которые я профилировал, также не интенсивно используют SIMD, или они пропускают истинные горячие точки.
    • Коммерческие библиотеки, похоже, вообще избегают SIMD.
      • В некоторых случаях я даже видел библиотеки обработки изображений, которые возвращали оптимизированный для SIMD код в более ранней версии к не-SIMD-коду в более поздней версии, что приводило к серьезному снижению производительности.
        (Ответ производителя заключается в том, что необходимо было избегать ошибок компилятора.)

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

Однако этот ответ в значительной степени «умиротворяет» точку зрения «преждевременной оптимизации», то есть точку зрения, которая:

  • Все оптимизации преждевременны по определению (или кратковременны по своей природе ), и
  • Единственная оптимизация, которая имеет долгосрочное преимущество - это простота.

Но такие точки зрения оспариваются в этой статье ACM .


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

rwong
источник
2
Есть ли требования к производительности? Можете ли вы удовлетворить свои требования к производительности без использования SIMD? Если нет, то вопрос спорный.
Чарльз И. Грант
4
Это слишком долго для вопроса, скорее всего потому, что его значительная часть - это попытка ответить на вопрос, и слишком долго даже для ответа (отчасти потому, что он затрагивает гораздо больше аспектов, чем большинство разумных ответов).
3
Мне нравится иметь как чистый / простой / медленный код (для первоначального подтверждения концепции, так и для последующей документации) в дополнение к оптимизированной альтернативе / s. Это облегчает понимание (поскольку люди могут просто читать чистый / простой / медленный код) и легко проверить (сравнивая оптимизированную версию с чистой / простой / медленной версией вручную и в модульных тестах)
Brendan
2
@Brendan Я был в похожем проекте и использовал подход тестирования с простым / медленным кодом. Хотя этот вариант стоит рассмотреть, он также имеет ограничения. Во-первых, разница в производительности может оказаться чрезмерной: тесты с использованием неоптимизированного кода могут выполняться часами ... днями. Во-вторых, для обработки изображений может оказаться, что побитовое сравнение просто не будет работать, когда оптимизированный код дает немного отличающиеся результаты - так что придется использовать более сложное сравнение, такое как среднеквадратичная разница ef
gnat
2
Я голосую, чтобы закрыть этот вопрос как не по теме, потому что это не проблема концептуального программирования, как описано в справочном центре .
durron597

Ответы:

6

Я написал не много SIMD-кода для себя, но много ассемблерного кода несколько десятилетий назад. AFAIK с использованием встроенных функций SIMD - это, по сути, программирование на ассемблере, и весь ваш вопрос можно перефразировать, просто заменив «SIMD» словом «сборка». Например, пункты, которые вы уже упоминали, как

  • разработка кода занимает от 10 до 100 раз, чем «код высокого уровня»

  • это связано с конкретной архитектурой

  • код никогда не бывает «чистым» и не может быть легко переработан

  • вам нужны специалисты для написания и поддержания его

  • отладка и сопровождение трудны, развиваются очень тяжело

ни в коем случае не являются «особенными» для SIMD - эти пункты верны для любого языка ассемблера, и все они являются «отраслевым консенсусом». И вывод в индустрии программного обеспечения также почти такой же, как и для ассемблера:

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

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

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

Конечно, с «классической» сборкой или машинным кодом действительно есть разница: сегодня современные компиляторы обычно производят высококачественный машинный код на языке высокого уровня, который часто лучше оптимизируется, чем код на ассемблере, написанный вручную. Для популярных сегодня SIMD-архитектур качество доступных компиляторов намного ниже, чем у AFAIK, и, возможно, оно никогда не достигнет этого, поскольку автоматическая векторизация все еще является темой научных исследований. См., Например, эту статью, в которой описываются различия в оптимизации между компилятором и человеком, давая представление о том, что создать хорошие SIMD-компиляторы может быть очень сложно.

Как вы уже описали в своем вопросе, существуют также проблемы с качеством современных библиотек. Поэтому лучше всего надеяться, что в ближайшие годы качество компиляторов и библиотек повысится, возможно, оборудование SIMD придется изменить, чтобы стать более «дружественным к компилятору», возможно, специализированные языки программирования, поддерживающие более простую векторизацию (например, Halide, который Вы упомянули дважды) станет более популярным (разве это не сила Fortran?). Согласно Википедии , SIMD стал «массовым продуктом» около 15–20 лет назад (а Галиду меньше 3 лет, когда я правильно истолковываю документы). Сравните это со временем компиляторов для "классического" языка ассемблера, необходимого, чтобы стать зрелым. Согласно этой статье в Википедиипотребовалось почти 30 лет (с ~ 1970 года до конца 1990-х годов), пока компиляторы не превысили производительность человеческих экспертов (в производстве непараллельного машинного кода). Поэтому нам, возможно, придется подождать еще 10–15 лет, пока то же самое не случится с компиляторами с SIMD.

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

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

Мы «решили» (или, возможно, «справились») проблему, написав собственный компилятор. Это не так безумно, как кажется на первый взгляд. У него ограниченный набор входов. Мы знаем, что весь код работает с изображениями, в основном с изображениями RGBA. Мы установили некоторые ограничения, например, чтобы входной и выходной буферы никогда не перекрывались, поэтому нет наложения указателей. Такие вещи.

Затем мы пишем наш код на языке затенения OpenGL (glsl). Он компилируется в скалярный код, SSE, SSE2, SSE3, AVX, Neon и, конечно, фактический glsl. Когда нам нужно поддержать новую платформу, мы обновляем компилятор для вывода кода для этой платформы.

Мы также делаем мозаику изображений, чтобы улучшить когерентность кэша, и тому подобное. Но поддерживая обработку изображений в небольшом ядре и используя glsl (который даже не поддерживает указатели), мы значительно уменьшаем сложность компиляции кода.

Этот подход не для всех, и у него есть свои проблемы (например, вам нужно убедиться в корректности компилятора). Но это сработало довольно хорошо для нас.

user1118321
источник
Это звучит 🔥🔥! Этот продукт вы продаете или делаете доступным отдельно? (Также «AVC» = AVX?)
Ахмед Фасих
Извините, да, я имел в виду AVX (я исправлю это.). В настоящее время мы не продаем компилятор как самостоятельный продукт, хотя это может произойти в будущем.
user1118321
Без шуток, это звучит очень аккуратно. Самое близкое, что я видел, это то, как компилятор CUDA имел обыкновение создавать «последовательные» программы, работающие на CPU, для отладки - мы надеялись, что это обобщит способ написания многопоточного и SIMD-кода ЦП, но Увы. Следующая ближайшая вещь, о которой я могу подумать, - это OpenCL. Вы, ребята, оценили OpenCL и обнаружили, что он ниже вашего компилятора GLSL-to-all?
Ахмед Фасих
1
Ну, OpenCL не существовало, когда мы начинали, я не думаю. (Или, если это так, это было довольно ново.) Так что это на самом деле не входило в уравнение.
user1118321
0

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

Vector<float> values = GetValues();
Vector<float> increment = GetIncrement();

// Perform addition as a vector operation:
List<float> result = (values + increment).ToList();

против

List<float> values = GetValues();
List<float> increment = GetIncrement();

// Perform addition as a monadic sequence operation:
List<float> result = values.Zip(increment, (v, i) => v + i).ToList();

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

http://blogs.msdn.com/b/dotnet/archive/2014/04/07/the-jit-finally-proposed-jit-and-simd-are-getting-married.aspx

http://blogs.msdn.com/b/dotnet/archive/2014/05/13/update-to-simd-support.aspx

логово
источник
Насколько я понимаю, возможность использования внешних библиотек уже была исследована и рассмотрена Аскером: «Похоже, что библиотеки с широко распространенным коммерческим использованием сильно не поддерживают SIMD ...»
gnat
@gnat Я на самом деле прочитал весь этот абзац, а не только маркеры верхнего уровня, и на постере не упоминаются какие-либо универсальные библиотеки SIMD, только компьютерное зрение и обработка изображений. Не говоря уже о том, что анализ приложений на языках высокого уровня полностью отсутствует, несмотря на отсутствие тега C ++ и специфики C ++, что отражено в заголовке вопроса. Это заставляет меня верить, что, хотя мой вопрос не будет считаться первичным, он, скорее всего, увеличит ценность, заставляя людей осознавать другие варианты.
День
1
Насколько я понимаю, ОП спрашивает, существуют ли решения с широко распространенным коммерческим использованием. Хотя я ценю вашу подсказку (возможно, я смогу использовать lib для проекта здесь), из того, что я вижу, RyuJIT далеко не соответствует «широко принятому отраслевому стандарту».
Док Браун
Возможно, @DocBrown, но его реальный вопрос сформулирован так, чтобы быть более общим: «... промышленный консенсус относительно ценности чистого и простого кода для кода SIMD ...». Я сомневаюсь, что вообще существует какой-либо (официальный) консенсус, но я утверждаю, что языки более высокого уровня могут уменьшить разницу между «обычным» и SIMD-кодом, точно так же, как C ++, и вы забудете о сборке, тем самым уменьшив затраты на обслуживание.
День
-1

В прошлом я занимался программированием на ассемблере, а не программированием SIMD.

Вы рассматривали возможность использования компилятора с поддержкой SIMD, такого как Intel? Интересно ли руководство по векторизации с помощью компиляторов Intel® C ++ ?

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

ChrisW
источник
согласно моему прочтению, этот подход был опробован asker, см. упоминания об ошибках / дефектах компилятора в вопросе
gnat
ОП не сказал, пытались ли они компилятор Intel , что также является предметом этой темы Programmers.SE . Большинство людей не пробовали это. Это не для всех; но это могло бы удовлетворить бизнес / вопрос ОП (лучшая производительность для более низких затрат на кодирование / проектирование / обслуживание).
ChrisW
хорошо то, что я прочитал в этом вопросе, говорит о том, что asker знает о компиляторах для Intel и других архитектур: «Некоторые архитектуры поддерживают идеальную обратную совместимость (Intel); некоторые терпят неудачу ...»
gnat
«Intel» в этом предложении означает «Intel-the-chip-designer», а не «Intel-the-compiler-writer».
ChrisW