LINUX.ORG.RU

Что использовать для разбора time string в perl?

 


1

2

Доброго времени суток.

Сабж. Три момента:

  • DateTime::Format::Strptime крайне тормозной. В 60 ( !!! ) раз медленнее, чем если самому парсить строку и преобразовывать через Time::Local. Притом что я ни разу не программист и никакой оптимизации не делал.

    Через DateTime::Format::Strptime :

    use DateTime::Format::Strptime;
    [...]
            my $strp = DateTime::Format::Strptime->new(
                pattern => '%b %e, %Y %H:%M:%S.%N %Z',
                time_zone => 'local',
            );
    
            my $ut_strp = DateTime::Format::Strptime->new(
                pattern => '%s',
                time_zone => 'local',
            );
            my $start_time = $ut_strp->format_datetime( $strp->parse_datetime($start_time_str) );
            my $end_time = $ut_strp->format_datetime( $strp->parse_datetime($end_time_str) );
    

    time { cat test | ./test_strptime.pl > test2 ; }
    
    real    31m27.591s
    user    20m20.728s
    sys     11m2.277s
    

    через велосипед :

    use Time::Local;
    
    [...]
    
    sub str2ut($) {
        my $time_shift;
    
        $_[0] =~ /^(\S+)\s+(\d+),\s+(\d{4})\s([0-9]{2}):([0-9]{2}):([0-9]{2})\.(\d{9})\s+(\S+)$/ || print "can't decode date $_[0]";
        my ( $month, $day, $year, $hour, $min, $sec, $nsec, $tzone ) = ( $1, $2, $3, $4, $5, $6, $7, $8 );
        my %m=(
            "Jan" => 0, "Feb"=>1,"Mar"=>2,"Apr"=>3,"May"=>4,"Jun"=>5,"Jul"=>6, "Aug"=>7,"Sep"=>8,"Oct"=>9,"Nov"=>10,"Dec"=>11,
            "Янв"   => 0, "Фев" => 1, "Мар" => 2, "Апр" => 3, "Май" => 4, "Июн" => 5, "Июл" => 6, "Авг" => 7, "Сен" => 8, "Окт" => 9, "Ноя" => 10, "Дек" => 11
        );
    
        my $time_shift = 0;
    
        return timelocal($sec,$min,$hour,$day,$m{$month},$year) + $time_shift * 60 * 60;
    }
    
    [...]
            my $start_time = str2ut($start_time_str);
            my $end_time = str2ut($end_time_str);
    
    

    time { cat test | ./test_timelocal.pl > test2 ; }
    
    real    0m25.730s
    user    0m23.097s
    sys     0m2.612s
    
  • Time::Piece пробовал, но заставить его работать не получилось
    Error parsing time at /usr/lib/perl/5.14/Time/Piece.pm line 469, <> line 1.
  • Таскать за собой свои велосипеды не хотелось бы

Пример строки:

Aug  6, 2013 17:03:04.453686000 MSK

★★★★★

#!/usr/bin/perl
use warnings;
use strict;
use Date::Parse;
use DateTime::Format::Strptime;
use Benchmark 'cmpthese';
use Time::Local 'timelocal';
use Time::Piece;
use POSIX::strptime 'strptime';

my @data = (
	'Aug  6, 2013 17:03:04.453686000 MSK'
);

my $strp = DateTime::Format::Strptime->new(
	pattern => '%b %e, %Y %H:%M:%S.%N %Z',
	time_zone => 'local',
);

my $time_shift = 0;
my %m=(
	"Jan" => 0, "Feb"=>1,"Mar"=>2,"Apr"=>3,"May"=>4,"Jun"=>5,"Jul"=>6, "Aug"=>7,"Sep"=>8,"Oct"=>9,"Nov"=>10,"Dec"=>11,
	"Янв"   => 0, "Фев" => 1, "Мар" => 2, "Апр" => 3, "Май" => 4, "Июн" => 5, "Июл" => 6, "Авг" => 7, "Сен" => 8, "Окт" => 9, "Ноя" => 10, "Дек" => 11
);
sub str2ut($) {
	my ( $month, $day, $year, $hour, $min, $sec, $nsec, $tzone ) = 
		($_[0] =~ /^(\S+)\s+(\d+),\s+(\d{4})\s([0-9]{2}):([0-9]{2}):([0-9]{2})\.(\d{9})\s+(\S+)$/)
		or die "can't decode date $_[0]";
	return timelocal($sec,$min,$hour,$day,$m{$month},$year) + $time_shift * 60 * 60;
}

cmpthese(
	-1,
	{
		'Date::Parse' => sub { # unixtime
			str2time($_) for @data;
		},
		'DateTime::Format::Strptime' => sub { # new DateTime
			$strp->parse_datetime($_) for @data;
		},
		'manual' => sub { # unixtime
			str2ut($_) for @data;
		},
		'Time::Piece' => sub { # new Time::Piece
			Time::Piece->strptime($_, '%b %e, %Y %H:%M:%S.%N %Z') for @data; # spits warnings
		},
		'POSIX::strptime' => sub { # time() array
			strptime($_, '%b %e, %Y %H:%M:%S.%N %Z') for @data;
		},
	}
);

__END__
                               Rate DateTime::Format::Strptime Date::Parse manual Time::Piece POSIX::strptime
DateTime::Format::Strptime    336/s                         --        -90%   -94%        -98%           -100%
Date::Parse                  3228/s                       860%          --   -47%        -78%            -97%
manual                       6081/s                      1709%         88%     --        -58%            -94%
Time::Piece                 14371/s                      4175%        345%   136%          --            -86%
POSIX::strptime            105412/s                     31255%       3166%  1634%        634%              --
AITap ★★★★★
()

1. Никогда не мерь через time производительность перлового кода. Как ты наверное не знаешь, перл для запуска программы производит две стадии: компиляция и компоновку в runtime. DateTime просто тянет кучу модулей за собой.

$ time ~/staticperl perl -MDate::Parse -e 'print str2time("Aug  6, 2013 17:03:04.453686000 MSK"), "\n";'
1375797784

real    0m0.059s
user    0m0.021s
sys     0m0.032s
gh0stwizard ★★★★★
()
Ответ на: комментарий от gh0stwizard

Никогда не мерь через time производительность перлового кода

Учитывая количество итераций ( ~ 260k ), мне скорость компиляции уже не важна была. Ты же не хочешь сказать, что DateTime::Format::Strptime полчаса только компилируется?

Date::Parse

Спасибо, попробую

router ★★★★★
() автор топика
Ответ на: комментарий от router

У многих модулей для парсинга/генерации времени основные тормоза это в вызовах open, stat(«/etc/localtime»). Вот мои данные:

  • Time::Local - вызывает
  • Date::Parse - вызывает
  • POSIX::strftime - вызывает
  • POSIX::strptime - не вызывает
  • Date::Handler - не вызывает, но течет (может пофиксили уже)
  • DateTime и Ко - вызывает
  • Time::Piece - вызывает (тянет Time::Local)

Насчет Time::Piece->strptime() заметил две проблемы (все в сишном/xs коде):

1. Не парсятся миллисекунды.
2. Не парсится таймзона в буквенном виде (+0400 парсится).

gh0stwizard ★★★★★
()
Ответ на: комментарий от AITap

Спасибо! Benchmark теперь буду использовать постоянно.

POSIX::strptime отлично подошёл. Единственное, он выдаёт время в виде массива, который уже нужно скормить timelocal'у:

timelocal ( strptime($_, '%b %e, %Y %H:%M:%S.%N %Z') )

И всё равно он работает вдвое быстрее моего велосипеда :)

router ★★★★★
() автор топика
Последнее исправление: router (всего исправлений: 1)
Ответ на: комментарий от router

Пожалуйста! :)

Быстрее, наверное, получится только в том случае, делать UNIX timestamp внутри кода на C при помощи mktime.

AITap ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.