VAR = `cat file`, а затем повторение эха« $ VAR »медленнее, чем повторение файла cat. Зачем?

В файлах содержится около 10.000 файлов files/ и 10.000 строк в metadata.csv . Где metadata.csv содержит информацию о файлах. У меня есть сценарий оболочки, который печатает информацию о каждом файле, а затем содержимое файла:

 #!/bin/sh for FILE in `find files/ -type f` do ID=`echo $FILE | sed 's/some/thing/'` cat metadata.csv | awk -v ORS="" -v id=$ID -F "\t" '$1==id { print "id=\""id"\" Some=\""$2"\" Thing=\""$5"\" "}' cat $FILE done 

Я думал, что могу ускорить это, назначив содержимое metadata.csv переменной METADATA . Я думал, что он не будет читать файл с диска каждый раз, но он сохранит его в памяти:

 #!/bin/sh METADATA=`cat metadata.csv` for FILE in `find files/ -type f` do ID=`echo $FILE | sed 's/some/thing/'` echo "$METADATA" | awk -v ORS="" -v id=$ID -F "\t" '$1==id { print "id=\""id"\" Some=\""$2"\" Thing=\""$5"\" "}' cat $FILE done 

Но второй не быстрее. Первый пробегает около 1 минуты, а второй – более 2 минут.

Как это работает и почему второй скрипт работает медленнее, а не быстрее?

edit : на моей системе / bin / sh -> тире

One Solution collect form web for “VAR = `cat file`, а затем повторение эха« $ VAR »медленнее, чем повторение файла cat. Зачем?”

Вы не предоставили достаточную информацию для других, чтобы воспроизвести свой бенчмарк. Я сделал свой собственный и нашел, что метод echo будет немного быстрее с тире и ksh, и примерно то же самое с mksh. Соотношение было намного меньше 1: 2, даже когда была разница. Очевидно, это зависит от многих вещей, включая оболочку, ядро, реализацию утилит и содержимое файлов данных.

Между этими двумя методами нет очевидного победителя. Чтение с диска практически не связано с тем, что файл будет находиться в кеше. Вызов cat имеет накладные расходы на разветвление внешнего процесса, тогда как echo – это оболочка bultin. Если ваш sh – bash, его встроенный echo печатает свой аргумент по одной строке за раз, даже когда выход идет в трубу, что может объяснять небольшую задержку. Dash и ksh этого не делают; как правило, они имеют лучшую производительность, чем bash.

В вашем скрипте есть ряд оптимизаций.

  • Очевидная оптимизация метода cat заключается в том, чтобы вместо этого использовать перенаправление ( <metadata.csv awk … ) или передать metadata.csv в качестве аргумента awk. В моих тестах перенаправление было очень немного быстрее, чем echo , и не было заметной разницы между перенаправлением и awk … metadata.csv .

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

  • Точно так же вы анализируете вывод find , который задушит некоторые имена файлов и потребует дополнительной работы. Каноническим решением является использование find -exec ; это может быть или не быть быстрее, хотя, поскольку это также должно сделать дополнительную работу, чтобы запустить оболочку для обработки файлов.
  • Я предполагаю, что ваш скрипт awk упрощен от реальной вещи. С помощью сценария, который вы показываете, считая, что первый столбец CSV-файла содержит только символы, которые не являются особыми в регулярных выражениях, вы можете попробовать использовать sed; это было бы более загадочным, но это может быть немного быстрее, потому что более специализированные инструменты, как правило, быстрее. Нет никакой гарантии, что вы получите улучшение, хотя, не говоря уже о измеримом.
  • Когда вы устанавливаете ID , вы вызываете внешнюю программу. В зависимости от того, что вы здесь делаете, это может быть выполнимо с помощью собственных строковых манипуляций оболочки: они обычно не очень быстрые и не очень мощные, но они не требуют вызова внешней программы.

В целом, объединив эти локальные оптимизации, я бы пошел с

 #!/bin/ksh find files/ -type f -exec sh -c ' for FILE do ID=${FILE//some/thing} sed '/^$ID\t/ s/\([^\t]*\)\t\([^\t]*\)\t[^\t]*\t[^\t]*\t\([^\t]*\).*/id="\1" Some="\2" Thing="\3"/' metadata.csv cat "$FILE" done' _ {} + 

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

 #!/bin/ksh join -j 1 -t $'\t' -o 2.1,2.2,2.5,1.2 \ <(find files/ -type f | sed 's!/some$!/thing\t&!' | sort) \ <(sort metadata.csv) | awk -F '\t' '{ print "id =\"" $1 "\" Some=\"" $2 "\" Thing=\" $3 "\""; system("cat \047" $4 "\047"); # Assuming no single quotes in file names }' 
Interesting Posts

Отрегулируйте зазор между двумя столбцами, чтобы заставить их смотреть прямо

Что делает инструкция Bind bindkeys-file?

Новая почта Полученное уведомление Kmail + Jovie Speech customizing

Изменение порядка разделов в GParted

Установите ffmpeg на Netgear NAS, выполнив произвольное сжатие Debian

Конкатенация определенных файлов в один файл

Можно ли зашифровать жесткий диск с помощью ключевого файла вместо пароля?

Версия ядра Force Debian

Поддерживает ли Linux Mint PAE для 32-битной Cinnamon и MATE?

Среднее использование ЦП процесса

Как можно запускать приложения Windows в Linux, которые уже установлены в среде Windows?

Массовое, непредсказуемое падение производительности ввода-вывода в Linux

разница между «ip link set» и «отключить / снова подключиться» в графическом интерфейсе?

Как скопировать файл, который все еще записывается поверх ssh?

Как получить набор исправлений ядра linux из списка рассылки?

Linux и Unix - лучшая ОС в мире.