Разбиение файла с помощью If-Then-Else на 3 файла - PullRequest
0 голосов
/ 13 февраля 2019

Почему код для разделения больших файлов, написанных на Perl, работает намного быстрее кода, написанного на Korn Shell.Более миллиона входных записей.Первые 9 символов каждой записи используются для определения того, в какой файл записана запись, и это выполняется в Perl за 4-5 минут.Я попытался преобразовать этот код в ksh, и он, кажется, работает вечно (часы).

Я действительно понятия не имею, что я сделал неправильно, что вызывает эту проблему.В некоторых записях в строку встроены пустые и / или буквенные символы, поэтому сравнение должно быть сравнением типа строки.Любые идеи о том, как заставить мой сценарий ksh получить производительность perl или почему это не так?

Я пробовал несколько разных вариантов, так как ksh / bash имеет много способов сделать то же или подобное в обработке переменныхи сравнения.Я также не знаю, как именно работает этот очень старый Perl-код.

Мой Perl-код:

open(FILEIN,"base.dat") || die "Could not open FILEIN\n.";

open(FILEOUT1,">base1.dat") || die "Could not open FILEOUT1\n.";
open(FILEOUT2,">base2.dat") || die "Could not open FILEOUT2\n.";
open(FILEOUT3,">base3.dat") || die "Could not open FILEOUT3\n.";

$v_break =  "518000000";
$v_break2 = "525000000";

#Run until end of file
while (<FILEIN>)   {
  $v_pcn = substr($_, 0, 9);

  if ($v_break gt $v_pcn) {
     print FILEOUT1 $_;
  }
  elsif (($v_pcn ge $v_break) && ($v_pcn lt $v_break2)) {
     print FILEOUT2 $_;
  }
  else
  {
    print FILEOUT3 $_;
  }
}  #(<FILEIN>)

close(FILEIN);
close(FILEOUT1);
close(FILEOUT2);
close(FILEOUT3);

Мой сценарий оболочки (ksh):

while read inrec                           # Read base file until EOF
 do                                        # Start work loop
    v_pcn=${inrec:0:9}                     # Get 1st 9 Characters in v_pcn
#   v_pcn=${v_pcn/' '/0}                   # Replace blanks with '0'
    if [[ $v_pcn < '518000000' ]]; then    # pcn < "518000000"
         echo $inrec >> base1.dat          # write rec to "base1.dat"
    elif [[ $v_pcn > '525000000' || $v_pcn == '525000000' ]]; then  # pcn >= "525000000"
         echo $inrec >> base3.dat          # write rec to "base3.dat"
    else                                   # else >= "518000000" & < "525000000"
         echo $inrec >> base2.dat          # write rec to "base2.dat"
    fi
 done < base.dat

Я ожидаю, что сценарий оболочки выдаст 3 выходных файла, соответствующих тем, которые были созданыPerl-код и примерно столько же времени;

ввод:

-rw-r--r--. 1 mfadjobt mfadset 2095795750 Feb 13 10:07 base.dat

вывод:

-rw-r--r--. 1 mfadjobt mfadset  461650125 Feb 13 10:07 base1.dat
-rw-r--r--. 1 mfadjobt mfadset  519783625 Feb 13 10:07 base2.dat
-rw-r--r--. 1 mfadjobt mfadset 1114362000 Feb 13 10:07 base3.dat

Ответы [ 4 ]

0 голосов
/ 19 февраля 2019

Проверено только с bash, но следующее решение должно работать и с некоторыми ksh версиями.Сначала пересмотрите границы.Красивые круглые фигуры позволяют взглянуть на первые 3 символа.Это должно принести вам пользу во всех решениях.
С помощью tee вы можете записывать в стандартный вывод и файл, а также в различные процессы.

tee < base.dat \
    >(grep -E "^([0-4]|50|51[0-7])"    > base1.dat) \
    >(grep -E "^5(1[89]|2[0-4])"       > base2.dat) |
      grep -E "^(52[5-9]|5[3-9]|6-9])" > base3.dat
0 голосов
/ 13 февраля 2019

Perl-код компилируется в «двоичное» представление.Затем это двоичное представление выполняется высокооптимизированным интерпретатором.

Сценарии оболочки с другой стороны

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

Я не уверенчто встроено в оболочку Korn, но в bash их довольно много.

Выполнение внешних команд стоит дорого , поскольку включает как минимум системные вызовы fork() и execve().

В общем случае сценарий оболочки будет быстрее, чем сценарий Perl, когда он очень короткий, т. Е. Когда стоимость запуска компилятора Perl выше, чем фактическое время выполнения кода.

краткий ответ: не удивляйтесь, когда вы переводите сценарий оболочки в эквивалентный сценарий Perl, что он будет работать намного быстрее.

0 голосов
/ 14 февраля 2019

Чтобы избежать путаницы с файловыми дескрипторами, вы можете использовать цикл for.

for inrec in `cat base.dat`                           # Read base file until EOF
 do                                        # Start work loop
    v_pcn=${inrec:0:9}                     # Get 1st 9 Characters in v_pcn
#   v_pcn=${v_pcn/' '/0}                   # Replace blanks with '0'
    if [[ $v_pcn < '518000000' ]]; then    # pcn < "518000000"
         echo $inrec >> base1.dat          # write rec to "base1.dat"
    elif [[ $v_pcn > '525000000' || $v_pcn == '525000000' ]]; then  # pcn >= "525000000"
         echo $inrec >> base3.dat          # write rec to "base3.dat"
    else                                   # else >= "518000000" & < "525000000"
         echo $inrec >> base2.dat          # write rec to "base2.dat"
    fi
 done
0 голосов
/ 13 февраля 2019

Каждый раз, когда у вас есть >> filename, вы снова открываете файл, перемещаете указатель на конец файла, а затем снова закрываете файл в конце оператора.Лучше держать файлы открытыми.

while read inrec                           # Read base file until EOF
 do                                        # Start work loop
    v_pcn=${inrec:0:9}                     # Get 1st 9 Characters in v_pcn
#   v_pcn=${v_pcn/' '/0}                   # Replace blanks with '0'
    if [[ $v_pcn < '518000000' ]]; then    # pcn < "518000000"
         echo $inrec >&3
    elif [[ $v_pcn > '525000000' || $v_pcn == '525000000' ]]; then  # pcn >= "525000000"
         echo $inrec >&4
    else                                   # else >= "518000000" & < "525000000"
         echo $inrec >&5
    fi
 done < base.dat 3>> base1.dat 4>> base2.dat 5>> base3.dat

Это откроет файлы один раз, сохранит их указатели в файлах и должно значительно ускорить процесс.

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

...