2009-10-19 4 views
5

У нас есть зрелый код, который загружает данные из файлов в базу данных. Существует несколько форматов файлов; все поля фиксированной ширины.Как ускорить обработку данных фиксированной ширины Perl?

Часть кода использует функцию Perl unpack() для чтения полей из входных данных в переменные пакета. Бизнес-логика тогда может обращаться к этим полям «с точки зрения человека».

Код чтения файла создается из описания формата один раз, перед чтением файла.

В эскизе формы, сгенерированный код выглядит следующим образом:

while (<>) { 

    # Start of generated code. 

    # Here we unpack 2 fields, real code does around 200. 
    ($FIELDS::transaction_date, $FIELDS::customer_id) = unpack q{A8 A20}; 

    # Some fields have leading space removed 
    # Generated code has one line like this per affected field. 
    $FIELDS::customer_id =~ s/^\s+//; 

    # End of generated code. 

    # Then we apply business logic to the data ... 
    if ($FIELDS::transaction_date eq $today) { 
     push @fields, q{something or other}; 
    } 

    # Write to standard format for bulk load to the database. 
    print $fh join('|', @fields) . q{\n} or die; 
} 

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

Похоже, что нет единой части бизнес-логики, которая занимает более 1-2% от времени выполнения.

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

EDIT:

В случае это разница

$ perl -v 
This is perl, v5.8.0 built for PA-RISC1.1 
+0

Было бы интересно узнать, будет ли оптимальным использование списка переменных пакета в левой части распаковки. –

ответ

1

Да. Извлечение с использованием substr, вероятно, будет самым быстрым способом сделать это. То есть:

$FIELDS::transaction_date = substr $_, 0, 8; 
$FIELDS::customer_id  = substr $_, 8, 20; 

, скорее всего, будет быстрее. Теперь, если бы я почеркнул этот код, я бы не отказался от unpack, но если вы генерируете код, вы можете также дать ему шанс и измерить.

Смотрите также ответы на Is Perl’s unpack() ever faster than substr()?

Как для зачистки ведущие пробелы, s/^\s+//, вероятно, будет самым быстрым способом.

Обновление: Трудно сказать что-то определенное, не имея возможности запускать тесты. Однако, как насчет:

my $x = substr $_, 0, 8; 

для полей, которые не нуждаются в какой-либо обрезке и

my ($y) = substr($_, 8, 20) =~ /\A\s+(.+?)\s+\z/; 

, которые нужны зачистка?

+0

Я буду экспериментировать и отчитываться. –

+3

Следует отметить, что unpack 'A' бесплатно закрывает конечные пробелы '. –

+0

Выглядит многообещающе. Базовый бенчмаркинг показывает, что ряд подполей substrs и strip trailing regexes примерно на 50% быстрее для нас, чем использование одной распаковки. Тесты проходят, но на моем экране полно предупреждений Perlish, поэтому я еще не совсем там. –

7

Я действительно занимался этой проблемой снова и снова. Unpack is better than substr.

Что касается пробелов, вы в значительной степени завинчены. Этот румян регулярного выражения - это «официальный» способ сделать это. Вы могли бы получить некоторую эффективность, уточнив свои распаковывающие утверждения (если данные не превышают 4 цифры, зачем распаковывать полные 12 цифр поля?), Но в противном случае синтаксический анализ будет всего лишь p.i.t.a.

Удачи вам в ваших плоских данных. Заворачивание старого мусора, как я его ненавижу.

3

Вы уверены, что используете этот процессор? Вычисление достаточно просто, чтобы заподозрить, что весь процесс, вероятно, будет связан с I/O. В этом случае оптимизация для быстрой распаковки не принесет вам много времени.

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

+0

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

+2

Для меня самым большим узким местом является сама база данных; если вы имеете дело с плоскими данными, вы вполне можете иметь дело с ужасной древней плоской базой данных. Я обойду проблему, вытаскивая данные с интервалом в современную базу данных, но для данных «реального времени» почти всегда база данных вызывает замедление. – Satanicpuppy

+0

Согласитесь. В целом наш процесс тратит большую часть времени на то, чтобы вложить данные в базу данных.Мы можем использовать параллельные процессы, чтобы ускорить это. Мне показалось, что такая значительная часть «Perl-time» была потрачена на такую ​​небольшую часть кода, что здесь может быть легко оптимизирована. –

1

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

+0

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

+3

Если большая часть времени процессора расходуется на двигатель регулярных выражений и встроенную функцию, то большая часть времени тратится на землю C (т. Е. Не на ходячие режимы и обрабатывая структуры данных perl) в любом случае. И требуется хороший программист, чтобы победить людей, которые написали Perl VM. распаковать быстро! – tsee

1

Просто сделайте это параллельно. Это тривиально, и на любой даже отдаленно современной машине это будет быстрее.

0

Базовая версия нашего кода, основанного на основах, предположила, что она может быть примерно на 50% быстрее, чем наша существующая распаковка. Сравнивая коды на месте в реальном приложении, версия substr дала нам сокращение времени выполнения на 16%. Это близко к тому, что мы надеялись на основе эталона и профилирования, о которых идет речь в вопросе.

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

идиома мы имеем сейчас:

$FIELDS::transaction_date = substr($_, 0, 8) || ''; 
$FIELDS::transaction_date =~ s/\s+\z//; 
$FIELDS::customer_id = substr($_, 8, 20) || ''; 
$FIELDS::customer_id =~ s/\s+\z//; 

с последующим селективным удалением ведущего пространства, как и раньше.

Спасибо за все ответы. Я соглашусь с Синан, потому что это сработало для нас, несмотря на то, что оно «просто неправильно».