Производительность приложений

[АРХИВИРОВАНО] Последнее обновление от Joe Schaefer на пт, 26 апр. 2024    источник
 

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

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

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

Поэтому идите вперед и используйте элегантный язык программирования, как Python3 или Яваскрипт/Скрипти пусть эксперты по предмету (МСП) там, в мире с открытым исходным кодом, дадут вам мощный C/C++

Даже беззависимый сценарий bash – это работоспособное решение для многих основных задач. Вот тот, который я написал для фирмы дополненной реальности Волшебный скачок много лет назад, чтобы заменить неуклюжий OpenGrok сервис с чем-то, что использует многопроцессорную параллелизацию с xargs -P, и поддерживает Pусский поиск по простому Эмак/Вим

https://github.com/joesuf4/home/blob/wsl/bin/pffxg.sh

Этот скрипт на порядок быстрее, чем обычные подозреваемые на GitHub, которые были написаны на статических, скомпилированных языках программирования. Но путем определения точного узкого места в бюстгальтер (циклирование с большим объемом вилка + Exec Звонки посередине) и использование xargs вместо этого вы получаете сценарий, который очень похож на этот, с основным алгоритмом, реализованным в 10 строках оболочка

Он также использует сообщество с открытым исходным кодом SME разумным образом, а не так, как это сделали другие «фильтрованные рекурсивные реализации grep» на GitHub. Вместо того, чтобы внутренне принимать и поддерживать мою собственную (потоковую) реализацию находить, xargs, и рвота, я просто повторно использую предварительно установленные исполняемые файлы, другие SME‘s совершенствовались в течение десятилетий как есть. Мне не нужно осваивать их реализации, просто используйте их повторно интерфейс командной строки

Чтобы увидеть противоположный подход, где все делается внутри компании, полностью микрооптимизировано и по-прежнему не может побить этот сценарий с параметрами поиска по умолчанию, и нет доступной системы кэширования, вот прекрасный пример https://github.com/BurntSushi/ripgrep

Просто вытащить первый #performance #benchmark с этой страницы и масштабировать его с размера дерева образца игрушки (источники ядра linux) на гетерогенное дерево, которое 23GB: (лучшие прогоны после 3 итераций; ЯЗЫК=en_US.UTF-8

    % du -sh .
    23G .
    % time rg -uuniw '[A-Z]+_SUSPEND' | wc -l
    6259
    rg -uuniw '[A-Z]+_SUSPEND' 9.46s user 16.08s system 261% cpu 9.759 total
    wc -l 0.00s user 0.07s system 0% cpu 9.759 total
    % time pffxg.sh -- -wnE '[A-Z]+_SUSPEND' | wc -l
    5855
    pffxg.sh -- -wnE '[A-Z]+_SUSPEND' 16.66s user 2.68s system 429% cpu 4.501 total
    wc -l 0.00s user 0.00s system 0% cpu 4.501 total

Это довольно глупо для микрооптимизации того, что глубоко связано с состоянием кэша файловой системы ядра для вашего поиска. Вариация времени производительности зависит от скорости доступа к корпусу содержимого файлов, и это на порядок более актуально, чем любой другой фактор к конечным результатам. Быть на NVMe помогает, но ничего в этом пространстве не бьется ОЗУ

Вот почему наличие в памяти сжатого кэша для большого корпуса файлов стабилизирует сроки производительности. Удивительно, что никто больше не думал, что это достаточно важно для поддержки.

Выведите второй #performance #benchmark с этой страницы и увеличьте его, как и раньше (то же самое) 23GB

    % time rg -tc -uuuiwn '[A-Z]+_SUSPEND' | wc -l
    5629
    rg -tc -uuuiwn '[A-Z]+_SUSPEND' 3.51s user 1.71s system 1141% cpu 0.457 total
    wc -l 0.00s user 0.05s system 11% cpu 0.457 total
    % time LANG=C pffxg.sh --cache /tmp/pffxg-$USER --workers 32 --cc -- -wE '[A-Z]+_SUSPEND' | wc -l
    5628
    LANG=C pffxg.sh --cache /tmp/pffxg-$USER --workers 32 --cc -- -wE  3.14s user 0.88s system 1055% cpu 0.381 total
    wc -l 0.00s user 0.00s system 0% cpu 0.381 total

Настроенный pffxg.sh все еще быстрее, несмотря на всю работу, поставленную в микрооптимизацию рипгрепа для этого C

Как я использовал этот сценарий AOSP было запланировать a репозиторий синхронизация и последующая pffxg.sh **лезоп-сжатый кэш-вкладышtmpfsБегать каждое утро перед работой (через передняя панель), с PFFXG_CACHE=... установить в мою ~/.pffxg.conf файл. Таким образом, любой pffxg.sh вызовы, которые выполнялись в течение рабочего дня, будут использовать сжатый кэш в tmpfs

.25M LOC между рипгреп и угреп. 632 ЛОК для pffxg.sh

Потому что это такая маленькая программа оболочки, pffxg.sh может дать вам мощные крючки в его внутренние с почти нулевым усилием. Даже рвота Сама команда настраивается: любая команда, которую вам нужно выполнить на выбранном корпусе файлов, которая может принять список имен файлов, добавленных к концу своих аргументов, является честной игрой. Вот «общее число строк в MiLOC

    % time find * -type f | xargs wc -l | awk '{ $2 == "total" {a+=$1} END {print a/1024**2}'
    28.451
    find * -type f 0.00s user 0.06s system 2% cpu 2.733 total
    xargs wc -l 0.53s user 1.02s system 54% cpu 2.853 total
    awk '$2 == "total" {a+=$1} END {print a/1024**2}' 0.23s user 0.59s system 28% cpu 2.853 total

    % time pffxg.sh --workers 8 --cmd wc --all -- -l | awk '{$2 == "total" {a+=$1} END {print a/1024**2}'
    28.4506
    pffxg.sh --workers 8 --cmd wc --all -- -l 0.92s user 0.66s system 826% cpu 0.192 total
    awk '$2 == "total" {a+=$1} END {print a/1024**2}' 0.02s user 0.00s system 11% cpu 0.192 total

рипгреп

    % time rg -c \$ | awk -F : '{a+=$2} END {print a/1024**2}'
    28.4284
    rg -c \$ 2.12s user 2.19s system 276% cpu 1.564 total
    awk -F : '{a+=$2} END {print a/1024**2}' 0.58s user 0.45s system 66% cpu 1.564 total

Здесь он ограничен C

    % time pffxg.sh --workers 8 --cc --cmd wc -- -l | awk '$2 == "total" {a+=$1} END {print a/1024**2}'
    25.3935
    pffxg.sh --workers 8 --cc --cmd wc -- -l 0.76s user 0.54s system 734% cpu 0.177 total
    awk '$2 == "total" {a+=$1} END {print a/1024**2}' 0.02s user 0.00s system 9% cpu 0.177 total

и рипгреп

    % time rg -tc -c \$ | awk -F : '{a+=$2} END {print a/1024**2}'
    25.3844
    rg -tc -c \$ 3.49s user 1.54s system 441% cpu 1.140 total
    awk -F : '{a+=$2} END {print a/1024**2}' 0.38s user 0.38s system 66% cpu 1.140 total

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

pffxg.sh не является продуктом, и это не шаг продаж для него. Это пример, чтобы проиллюстрировать мою точку зрения очень драматично. Если вы знакомы с длинной историей фильтрованных рекурсивных решений на GitHub, все они основаны на представлении о том, что проблема с оригиналом Энди Лестера. Перл исполнение подтверждатьбыло написано в Перл. Единственной реальной проблемой с точки зрения производительности было то, что Перл был написан Энди, у которого, похоже, не было никаких навыков для концепций производительности систем (например, выращивание находить параллелизация работы с специально созданными C двоичный), но вместо этого нацелен на ленивую переносимость, пытаясь захватить весь код как однопоточный Pure Перл

Пусть цветет тысяча цветов, какими бы глупыми они ни казались!