Является ли конвейерная обработка, смещение или расширение параметров более эффективным?

Это последняя версия Fedora 18.

Исходный файл PS насыщен математикой (это набор из 28 слайдов, созданных из файла TeX DVI с растровыми шрифтами). Он отлично читается в evince (но в перевернутом виде), а результат ps2pdf также хорошо читается в evince и xpdf .

В лучшем случае вы можете надеяться получить (скудный) текст на слайдах, по большей части это математика, которая вообще не переводится в понятный текст даже при идеальных обстоятельствах, а тем более в формулы. Единственный способ снова превратить это в математические формулы LaTeX (или любой другой формат, который вам нужен) - это перепечатать их.

26
31.01.2019, 21:34
8 ответов

Не используйте команды оболочки, если хотите быть эффективным. Ограничьтесь каналами, перенаправлениями, заменами и т. д., а также программами. Вот почему существуют утилиты xargsи parallel-, потому что циклы while в bash неэффективны и очень медленны. Используйте циклы bash только в качестве последнего решения.

list="1 ant bat 5 cat dingo 6 emu fish 9 gecko hare 15 i j"
if 
    <<<"$list" tr -d -s '[0-9 ]' | 
    tr -s ' ' | tr ' ' '\n' | 
    grep -q -x '9'
then
    found=true
else 
    found=false
fi
echo ${found} 

Но с хорошим awkвы, вероятно, должны работать несколько быстрее.

1
27.01.2020, 19:39

Довольно просто с awk. Это даст вам значение каждого четвертого поля для ввода любой длины:

$ awk -F' ' '{for( i=1;i<=NF;i+=3) { printf( "%s%s", $i, OFS ) }; printf( "\n" ) }' <<< $list
1 5 6 9 15

При этом используются встроенные -в awkпеременные, такие какNF(количество полей в записи ), и выполняется некоторый простой forцикл для перебора полей, чтобы получить те, которые вы хотите, не зная заранее, сколько их будет.

Или, если вам действительно нужны только те конкретные поля, которые указаны в вашем примере:

$ awk -F' ' '{ print $1, $4, $7, $10, $13 }' <<< $list
1 5 6 9 15

Что касается вопроса об эффективности, самым простым путем было бы проверить этот или каждый из ваших других методов и использовать time, чтобы показать, сколько времени это занимает; вы также можете использовать такие инструменты, как strace, чтобы увидеть, как проходят системные вызовы. Использование timeвыглядит как:

$ time./script.sh

real    0m0.025s
user    0m0.004s
sys     0m0.008s

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

18
27.01.2020, 19:39

Возможно, это?

cut -d' ' -f1,4,7,10,13 <<<$list
1 5 6 9 15
2
27.01.2020, 19:39
  • Первое правило оптимизации программного обеспечения:Не делайте этого .

    Пока вы не знаете, что проблема в скорости работы программы, не нужно думать. о том, как быстро. Если ваш список примерно такой длины или просто ~100 -1000 элементов долго, вы, вероятно, даже не заметите, сколько времени это займет. Есть вероятность, что вы тратите больше времени на размышления об оптимизации, чем о разнице.

  • Второе правило:Мера .

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

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

  • В-третьих, первое правило оптимизации сценариев оболочки:Не использовать оболочку .

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

    Вместо этого используйте что-то вроде awk или Perl. В тривиальном микро -тесте, который я сделал, awkбыл в десятки раз быстрее, чем любая обычная оболочка при выполнении простого цикла (без ввода-вывода ).

    Однако, если вы используете оболочку, используйте встроенные функции оболочки вместо внешних команд. Здесь вы используете expr, который не встроен ни в одну оболочку, которую я нашел в своей системе, но которую можно заменить стандартным арифметическим расширением. Например.i=$((i+1))вместо i=$(expr $i + 1)для увеличения i. Использование вами cutв последнем примере также может быть заменено раскрытием стандартных параметров.

    См. также:Почему использование цикла оболочки для обработки текста считается плохой практикой?

Шаги #1 и #2 должны относиться к вашему вопросу.

36
27.01.2020, 19:39

awk — отличный выбор, если вы можете выполнять всю свою обработку внутри скрипта Awk. В противном случае вы просто перенаправляете вывод Awk в другие утилиты, уничтожая прирост производительности awk.

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

Однако конвейерный подход:

xargs -n3 <<< "$list" | while read -ra a; do echo $a; done | grep 9

Где:

  • xargsгруппирует список, разделенный пробелами -, на группы по три, каждая новая -строка разделена
  • while readиспользует этот список и выводит первый столбец каждой группы
  • grepфильтрует первый столбец (, соответствующий каждой третьей позиции в исходном списке)

На мой взгляд, улучшает понимание. Люди уже знают, что делают эти инструменты, поэтому легко читать слева направо и рассуждать о том, что произойдет. Этот подход также четко документирует длину шага(-n3)и шаблон фильтра (9), поэтому его легко варьировать :

.
count=3
find=9
xargs -n "$count" <<< "$list" | while read -ra a; do echo $a; done | grep "$find"

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

3
27.01.2020, 19:39

По моему мнению, самое ясное решение (и, возможно, наиболее эффективное )— это использование awk-переменных RS и ORS:

awk -v RS=' ' -v ORS=' ' 'NR % 3 == 1' <<< "$list"
1
27.01.2020, 19:39
  1. Использование GNUsedи сценария оболочки POSIX :

    echo $(printf '%s\n' $list | sed -n '1~3p')
    
  2. Или с заменой параметра bashна :

    echo $(sed -n '1~3p' <<< ${list// /$'\n'})
    
  3. Не-GNU (, то естьPOSIX)sedиbash:

    sed 's/\([^ ]* \)[^ ]* *[^ ]* */\1/g' <<< "$list"
    

    Или, что более переносимо, с использованием POSIXsedи сценария оболочки:

    echo "$list" | sed 's/\([^ ]* \)[^ ]* *[^ ]* */\1/g'
    

Вывод любого из этих:

1 5 6 9 15
1
27.01.2020, 19:39

В этом ответе я дам только общие советы, а не тесты. Бенчмарки — единственный способ достоверно ответить на вопросы о производительности. Но так как вы не говорите сколько данных вы обрабатываете и как часто вы выполняете эту операцию, нет никакого способа провести полезный тест. Что более эффективно для 10 элементов и что более эффективно для 1000000 элементов, часто не одно и то же.

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

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

Не вызывайте exprдля выполнения арифметических операций, если вас вообще беспокоит производительность. На самом деле, вообще не вызывайте exprдля выполнения арифметических операций. Оболочки построили -в арифметике, которая понятнее и быстрее, чем вызов expr.

Кажется, вы используете bash,поскольку вы используете конструкции bash, которых нет в sh. Так почему бы вам не использовать массив? Массив — наиболее естественное решение, и, вероятно, оно также будет самым быстрым. Обратите внимание, что индексы массива начинаются с 0.

list=(1 2 3 5 9 8 6 90 84 9 3 2 15 75 55)
for ((count = 0; count += 3; count < ${#list[@]})); do
  echo "${list[$count]}"
done

Ваш скрипт может быть быстрее, если вы используете sh, если в вашей системе есть dash или ksh как sh, а не bash. Если вы используете sh, вы не получаете именованные массивы, но вы все равно получаете массив с одним из позиционных параметров, который вы можете установить с помощью set. Чтобы получить доступ к элементу в позиции, которая неизвестна до времени выполнения, вам нужно использоватьeval(позаботьтесь о правильном цитировании! ).

# List elements must not contain whitespace or ?*\[
list='1 2 3 5 9 8 6 90 84 9 3 2 15 75 55'
set $list
count=1
while [ $count -le $# ]; do
  eval "value=\${$count}"
  echo "$value"
  count=$((count+1))
done

Если вы хотите получить доступ к массиву только один раз и перемещаетесь слева направо (, пропуская некоторые значения ), вы можете использовать shiftвместо индексов переменных.

# List elements must not contain whitespace or ?*\[
list='1 2 3 5 9 8 6 90 84 9 3 2 15 75 55'
set $list
while [ $# -ge 1 ]; do
  echo "$1"
  shift && shift && shift
done

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

Другой возможностью является использование обработки строк. Его преимущество в том, что он не использует позиционные параметры, поэтому вы можете использовать их для чего-то другого. Это будет медленнее для больших объемов данных, но вряд ли это будет иметь заметное значение для небольших объемов данных.

# List elements must be separated by a single space (not arbitrary whitespace)
list='1 2 3 5 9 8 6 90 84 9 3 2 15 75 55'
while [ -n "$list" ]; do
  echo "${list% *}"
  case "$list" in *\ *\ *\ *) :;; *) break;; esac
  list="${list#* * * }"
done
14
27.01.2020, 19:39

Теги

Похожие вопросы