Разработка
Более быстрое железо — плохое первое решение для медленного ПО
Если ваше первое решение любой проблемы с производительностью заключается в том, чтобы потратить больше денег на оборудование, вы можете в конечном итоге получить программное обеспечение, которое излишне медленное, трудно ускоряемое и чрезвычайно дорогое.
Ваш конвейер данных работает слишком медленно или использует слишком много памяти. Как вы можете ускорить его?
Одним из очевидных решений является покупка более быстрого железа. С облачными вычислениями переход на компьютер с большим количеством ядер или добавление большего объема оперативной памяти можно выполнить за минуты или даже секунды. Учитывая, что время разработчика стоит дорого, переход на более мощное оборудование часто рассматривается как дешевое первое решение для медленного программного обеспечения.
Но есть долгосрочные затраты, которые сразу не видны. Если ваше первое решение любой проблемы с производительностью заключается в том, чтобы потратить больше денег на оборудование, вы можете в конечном итоге получить программное обеспечение, которое излишне медленное, трудно ускоряемое и чрезвычайно дорогое.
Так как же решить, является ли более быстрое оборудование правильным решением проблем с производительностью программного обеспечения? В этой статье мы обсудим:
- Что можно купить за деньги с точки зрения оборудования.
- Почему железо не всегда помогает.
- Почему более быстрое железо не всегда должно быть вашим первым решением, даже если оно помогает.
- Изменение компромисса путем создания эффективного софта с самого начала.
Ускорение кода с помощью более быстрого железа
Если ваш код работает слишком медленно или использует слишком много памяти, вы можете потратить немного денег, чтобы получить доступ к более мощному оборудованию. Существует два основных подхода:
- Аренда оборудования в облаке: вы можете платить поминутно за доступ к виртуальным или даже выделенным машинам. Вы можете выбрать желаемую аппаратную конфигурацию (ЦП, ОЗУ, диск и т. д.) или, в каждом конкретном случае, увеличивать или уменьшать ресурсы по требованию.
- Покупка оборудования: если вы планируете интенсивно использовать один компьютер, может быть дешевле купить его, чем арендовать. Как физическое лицо, когда вы покупаете настольный компьютер, вы также можете получить гораздо лучшую производительность за ту же сумму денег по сравнению с ноутбуком. Некоторые организации также предпочитают создавать свои собственные кластеры или центры обработки данных.
Время программиста стоит дорого: в США время программиста может стоить его работодателю 100 долларов в час (или намного больше, если добавить дополнительные издержки, поскольку ожидается, что ценность, которую производит сотрудник, намного выше, чем затраты на него). Когда вы можете купить очень мощный компьютер за 2,000 долларов или арендовать виртуальную машину с 384 ГБ ОЗУ и 96 виртуальными процессорами за 8.60 долларов в час, может показаться, что дешевле просто решить проблемы с производительностью, заплатив за более быстрое оборудование.
К сожалению, существуют серьезные проблемы в использовании более дорогого оборудования в качестве первого решения проблем с производительностью.
Железа не всегда достаточно
Поможет ли более быстрое оборудование, зависит от того, где находится узкое место. Облачные машины могут иметь до 24 Тб ОЗУ (это 24576 Гб!) и до 192 ядер. Поэтому, если вам просто нужно больше оперативной памяти или вы имеете дело с программным обеспечением, которое может легко работать параллельно, вы можете пойти довольно далеко, используя более дорогое оборудование.
В других ситуациях существуют жесткие ограничения на скорость работы вашего железа.
Скорость одного ядра — жесткий предел
В некоторых случаях ваша скорость обработки привязана к скорости одного ядра центрального процессора, потому что ваше программное обеспечение плохо распараллеливается:
- Это обычное явление в Python из-за глобальной блокировки интерпретатора.
- Однако дело не только в Python: в Linux большинство компоновщиков не используют все преимущества многоядерности. Новый mold линкер исправляет это и предлагает несколько информативных сравнений производительности.
- Некоторые алгоритмы просто очень трудно распараллелить.
- Даже в программном обеспечении, которое может использовать преимущества нескольких ядер, некоторые алгоритмы могут распараллеливаться только до определенного числа ядер, а затем прекращать масштабирование из-за характера алгоритма или данных.
Это проблема, потому что, в отличие от растущего числа ядер, доступных в современных процессорах, производительность одноядерных процессоров за последние несколько лет не сильно выросла. Сравним 4-ядерный процессор Xeon 2014 года с тем, что доступно 8 лет спустя:
- Всего за 450 долларов вы можете купить ЦП в 7 раз более быстрый в многоядерной производительности. Потратьте достаточно денег, и вы сможете получить в 20 раз больше многоядерной скорости, чем у предшественника, а в ближайшем будущем, вероятно, и больше.
- В противовес, после 8 лет исследований и разработок Intel, AMD и Apple добились увеличения производительности CPU в одноядерном режиме только в 2.5 раза.
В целом, одноядерная производительность растет гораздо медленнее, чем многоядерная. Если одноядерная производительность является вашим узким местом, существует очень жесткий предел того, какие улучшения производительности вы можете получить от оборудования, независимо от того, сколько денег вы можете потратить.
Недостатки аппаратного подхода
Даже если более быстрое оборудование помогает, использование его в качестве первого решения проблем с производительностью может привести к долгосрочным затратам, помимо оплаты самого оборудования. Среди них:
- Культура неэффективности.
- Затраты на горизонтальное масштабирование.
- Затраты на вертикальное масштабирование.
- Выбросы парниковых газов.
Культура неэффективности
Если вы предоставите разработчикам кластер, который значительно масштабируется, или просто предоставите им доступ к виртуальным машинам любого размера, у них не будет мотивации писать быстрый код. Для небольшой группы людей это может быть хорошо. Но со временем со многими людьми в команде вы в конечном итоге создадите программное обеспечение, которое будет намного медленнее, чем могло бы быть, потому что у разработчиков будет меньше мотивации для изучения необходимых навыков.
Горизонтальное масштабирование: мультипликативные затраты
Если вы выполняете задание по обработке данных всего несколько раз, дополнительные 5 долларов за облачные вычисления не имеют большого значения. Но если вы выполняете одну и ту же работу 1000 раз в месяц, эти дополнительные дополнительные расходы теперь составляют до 60,000 долларов в год.
Вертикальное масштабирование: более раннее достижение архитектурных точек останова
Как только вы дойдете до того, что масштабирование на одной машине станет проблемой, если вы захотите продолжить масштабирование с помощью железа, вам нужно будет перейти на распределенную систему. Переход на распределенную систему может потребовать значительных изменений в вашем программном обеспечении и, возможно, существенного увеличения сложности отладки.
На этом этапе возможности улучшения только через аппаратное обеспечение сразу же заканчиваются, поскольку вам, вероятно, также придется потратить время на изменение самого ПО. Вы также можете увидеть снижение производительности на одной машине, и в этом случае увеличение затрат на оборудование при масштабировании будет пропорционально выше, чем при масштабировании одной машины.
Более медленный код заставляет этот дорогостоящий архитектурный сдвиг произойти раньше. Рассмотрим два алгоритма, решающих одну и ту же задачу: один требует O(N) процессорного времени, а другой требует O(N²) процессорного времени.
Input size | O(N) compute time | O(N²) compute time |
1 | 1 | 1 |
2 | 2 | 4 |
10 | 10 | 100 |
20 | 20 | 400 |
- Поскольку время выполнения алгоритма O(N) растет гораздо медленнее, его можно будет использовать в той же архитектурной парадигме для гораздо больших входных данных.
- Решение O(N²) не только будет использовать больше ресурсов, пока оно по-прежнему умещается на одном компьютере, но и заставит вас изменить парадигму гораздо раньше, как только вам нужно будет начать обрабатывать большие размеры входных данных.
Переключение на алгоритм O(N), вероятно, является лучшим подходом, а его использование с самого начала было бы еще лучше.
Выбросы парниковых газов
Центры обработки данных создают растущий процент глобальных выбросов парниковых газов. Это негативный побочный эффект, который не учитывается при ценообразовании вычислений, но вы все равно обязаны его уменьшить.
Смещение компромисса в сторону эффективного софта
До сих пор мы предполагали статический компромисс: вы можете потратить деньги на оборудование или потратить время разработчика на написание более эффективного кода. Вы, оценивая эти две статьи расходов, выбираете ту, которая ниже в вашей ситуации, и пересматриваете компромисс, когда ситуация меняется.
Но есть и другой способ подхода к проблеме. При прочих равных более эффективная программа лучше, чем менее эффективная. Таким образом, несмотря на то, что более быстрое аппаратное обеспечение по-прежнему будет подходящим решением во многих случаях, также стоит подумать о том, как сделать ваше программное обеспечение более эффективным по умолчанию.
1. Не думайте, что аппаратное обеспечение — единственное решение
Очень легко предположить, что медленное или неэффективное программное обеспечение неизбежно и неминуемо. И если вы верите в это, вы можете даже не думать о том, как сделать ваше программное обеспечение быстрее.
Но во многих случаях вполне возможно значительное ускорение во время выполнения. Например, вот Gas уменьшили нагрузку Redis на CPU на 80%, а вот ученый, который ускорил вычисления в 50 раз, с 8 часов до 10 минут. Только в 2 раза из-за параллелизма, так что это 25-кратное улучшение для одного ядра. Еще один пример ускорения в 50 раз я привожу в своей статье о разнице между оптимизацией и распараллеливанием.
Точно так же программному обеспечению не нужно использовать много памяти для обработки больших наборов данных. Переключение на потоковый/пакетный подход может перевести использование памяти с линейного масштабирования в зависимости от размера данных на небольшой фиксированный объем использования памяти, часто без влияния на время выполнения.
2. Сокращение затрат на написание эффективного программного обеспечения
Если вы согласны с тем, что возможно более быстрое и эффективное программное обеспечение, возникает вопрос, как сделать это без дополнительных затрат на разработку. Как вы можете написать более эффективное программное обеспечение за то же время? И как вы можете сократить расходы на оптимизацию существующего программного обеспечения?
Совершенствуйте свои навыки
Ваша способность писать эффективный код не фиксирована — у вас есть возможность улучшить ее.
В качестве примера сосредоточимся на использовании памяти: если вы анализируете большой файл JSON, использование потокового анализа JSON значительно сократит использование памяти. И во многих случаях это даже не больше работы! Вы просто заменяете две строки кода на две другие строки и структурируете свой код немного по-другому. В большинстве случаев вам просто нужно знать, что решение существует.
И чтобы было ясно, до того, как я написал эту статью, я не знал о существовании потоковой библиотеки ijson. Но я точно знал, что искать, потому что пакетная/потоковая обработка данных — один из основных методов обработки больших наборов данных с эффективным использованием памяти. Тот же высокоуровневый метод применяется, например, для заполнения фреймов данных Pandas из запросов SQL, только с другими деталями и API.
И способность идентифицировать квадратичные алгоритмы может помочь вам избежать распространенных ошибок производительности с очень небольшими усилиями.
Вы найдете написанные мной статьи об оптимизации производительности и сокращении использования памяти, а также множество других ресурсов, доступных для улучшения ваших навыков. Небольшие затраты времени сейчас могут привести к значительной экономии времени и денег в будущем.
Повышение видимости во время выполнения
Помимо улучшения ваших навыков, также необходимо понимать, почему ваш код работает медленно или использует слишком много памяти. Отчасти это касается использования соответствующих инструментов во время разработки. Например, для программ Python:
- py-spy, pyinstrument, VizTracer и другие инструменты позволяют по-разному измерять производительность.
- Fil позволяет измерять пиковые объемы памяти, а memory-profiler может отображать построчное распределение и освобождение памяти.
Вы также должны улучшить понимаемость производительности в продакшене, поскольку многие проблемы с производительностью видны только с реальными данными или в реальной среде, в которой выполняется ваш код. Для конвейеров обработки данных Python я создал профайлер Sciagraph; для других доменов вы можете обратиться к APM или инструментам наблюдения или другим непрерывным профайлерам.
Эффективность прежде всего
Обладая более качественной информацией и улучшенными навыками, вы можете тратить столько же времени на программирование и создавать программное обеспечение, которое работает быстрее и использует меньше ресурсов. Вы также можете оптимизировать свое программное обеспечение намного быстрее, если вам это нужно.
Это не означает, что вы не будете тратить деньги на аренду или покупку компьютерного оборудования. Но написание эффективного и быстрого программного обеспечения — это навык, которому вы можете научиться, и он не обязательно требует огромных инвестиций. Час, потраченный на изучение нового навыка, может быть использован во многих будущих проектах. И соответствующее повышение эффективности дает вам преимущества, которые увеличиваются в положительную сторону:
- Если вы масштабируете горизонтально, более низкие затраты на эффективное программное обеспечение мультипликативны.
- Для достижения архитектурных точек останова потребуется больше времени.
- Ваше программное обеспечение будет производить меньше выбросов парниковых газов.