Как сделать этот sed сценарий быстрее?

Иначе:

SELECT *
FROM TABLE A
WHERE EXISTS (
  SELECT 1 FROM TABLE
  WHERE COLUMN_NAME = A.COLUMN_NAME
  AND ROWID < A.ROWID
)

Хорошо работает (достаточно быстрый), когда существует индекс на column_name. И это - лучший способ удалить или обновить дублирующиеся строки.

9
задан erotsppa 1 December 2009 в 22:11
поделиться

10 ответов

Лучшее, что я смог сделать с sed, это сценарий:

s/[\s\t]*|[\s\t]*/|/g
s/[\s\t]*$//
s/^|/null|/

В моих тестах он работал примерно на 30% быстрее, чем ваш сценарий sed. Повышение производительности происходит за счет объединения первых двух регулярных выражений и исключения флага «g» там, где он не нужен.

Однако увеличение скорости на 30% - это лишь незначительное улучшение (все равно должно потребоваться около полутора часов, чтобы запустить выше скрипт в вашем файле данных размером 1 ГБ). Я хотел посмотреть, смогу ли я сделать что-нибудь лучше.

В конце концов, ни один другой метод, который я пробовал (awk, perl и другие подходы с sed), не показал себя лучше, за исключением, конечно, простого старого C реализация. Как и следовало ожидать от C, код является немного подробным для публикации здесь, но если вам нужна программа, которая скорее всего будет быстрее, чем любой другой метод, вы можете взглянуть на него .

В моих тестах реализация C завершается примерно за 20% времени, необходимого для ваш сценарий sed. Так что для запуска на вашем сервере Unix может потребоваться около 25 минут.

Я не тратил много времени на оптимизацию реализации C. Без сомнения, есть ряд мест, где алгоритм можно было бы улучшить, но, честно говоря, я не знаю, можно ли сэкономить значительное количество времени сверх того, что он уже дает. Во всяком случае, я думаю, что это определенно устанавливает верхний предел производительности, которую вы можете ожидать от других методов (sed, awk, perl, python и т. Д.).

Изменить: В исходной версии была небольшая ошибка, которая заставил его, возможно, напечатать неправильную вещь в конце вывода (например, мог напечатать "

29
ответ дан 4 December 2019 в 06:16
поделиться

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

#!/bin/sh
INFILE=data.txt
OUTFILE=fixed.txt
SEDSCRIPT=script.sed
SPLITLIMIT=`wc -l $INFILE | awk '{print $1 / 20}'`

split -d -l $SPLITLIMT $INFILE x_

for chunk in ls x_??
do
  sed -f $SEDSCRIPT $chunk > $chunk.out &
done

wait 

cat x_??.out >> output.txt

rm -f x_??
rm -f x_??.out
4
ответ дан 4 December 2019 в 06:16
поделиться

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

for each line
    split the line into an array of fields
    remove the leading and trailing white space
    join the fields back back together as a pipe delimited line handling the empty first field correctly.

Я бы также использовал для этого другой язык, такой как Perl или Ruby.

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

2
ответ дан 4 December 2019 в 06:16
поделиться

Попробуйте изменить первые две строки на:

s/[ \t]*|[ \t]*/|/g
2
ответ дан 4 December 2019 в 06:16
поделиться

Этот сценарий Perl должен быть намного быстрее

s/\s*|\s*/|/go;
s/\s *$//o;
s/^|/null|/o;

В принципе, убедитесь, что ваши регулярные выражения скомпилированы один раз (флаг 'o'), и нет необходимости использовать 'g' в регулярных выражениях, которые применяется только к концу и началу строки.

Кроме того, [\ s \ t] * эквивалентно \ s *

1
ответ дан 4 December 2019 в 06:16
поделиться

Это может сработать. Я только немного его проверил.

awk  'BEGIN {FS="|"; OFS="|"} {for (i=1; i<=NF; i++) gsub("[ \t]", "", $i); $1=$1; if ( $1 == "" ) $1 = "null"; print}'
1
ответ дан 4 December 2019 в 06:16
поделиться

use gawk, not sed.

awk -vFS='|' '{for(i=1;i<=NF;i++) gsub(/ +|\t+/,"",$i)}1' OFS="|"  file
1
ответ дан 4 December 2019 в 06:16
поделиться

How about Perl:

#!/usr/bin/perl

while(<>) {
    s/\s*\|\s*/|/g;
    s/^\s*//;
    s/\s*$//;
    s/^\|/null|/;
    print;
}

EDIT: changed approach significantly. On my machine this is almost 3x faster than your sed script.

If you really need the best speed possible, write a specialized C program to do this task.

1
ответ дан 4 December 2019 в 06:16
поделиться

Попробуйте сделать это одной командой:

sed 's/[^|]*(|.*|).*/\1/'
0
ответ дан 4 December 2019 в 06:16
поделиться

Вы пробовали Perl? Это может быть быстрее.

#!/usr/local/bin/perl -p

s#[\t ]+\|#|#g;
s#\|[\t ]+#|#g;
s#[\t ]*$##;
s#^\|#null|#;

Редактировать: На самом деле, похоже, примерно в три раза медленнее, чем программа sed. Странно ...

0
ответ дан 4 December 2019 в 06:16
поделиться
Другие вопросы по тегам:

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