Как я могу улучшить эту процедуру для скорости? - PullRequest
3 голосов
/ 05 февраля 2010

Мне нужен эффективный фильтр или подпрограмма commify для использования с Template :: Toolkit. Это должно использоваться много раз на странице. Он должен поддерживать десятичные дроби.

Это можно найти в The Perl Cookbook :

sub commify {
    my $text = reverse $_[0];
    $text =~ s/(\d\d\d)(?=\d)(?!\d*\.)/$1,/g;
    return scalar reverse $text;
}

Есть ли более эффективные способы?

Ответы [ 6 ]

8 голосов
/ 05 февраля 2010

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

Это решение commify примерно настолько хорошо, насколько вы можете его получить, но есть и другие вещи, которые вы могли бы сделать, если вам нужно его обойти:

  • Используйте что-то вроде Memoize для кеширования результатов, если вы повторно вводите одни и те же числа
  • Предварительно вычислите все числа, если они вряд ли изменятся.
  • Кэшируйте обработанные шаблоны, когда можете
  • Используйте настройку обратного прокси-сервера с вашим веб-сервером для передачи тяжелой обработки на внутренние серверы.
4 голосов
/ 05 февраля 2010

Другой вариант:

sub commify {
    my $text = shift;
    1 while $text =~ s/ ( \d ) ( \d{3} ) (\D|\z) /$1,$2$3/xms;
    return $text;
}

Когда дело доходит до решения, что быстрее, модуль Benchmark очень полезен.

#!/usr/bin/perl

use strict;
use warnings;
use Benchmark;

sub your_commify {
    my $text = reverse 100000000;
    $text =~ s/(\d\d\d)(?=\d)(?!\d*\.)/$1,/g;
    return scalar reverse $text;
}

sub my_commify {
    my $text = 100000000;
    1 while $text =~ s/ ( \d ) ( \d{3} ) (\D|\z) /$1,$2$3/xms;
    return $text;
}

timethese(
    -10,
    {
        'yours' => \&your_commify,
        'mine'  => \&my_commify,
    }
);

Выполнение этого дает:

~$ ./benchmark.pl
Benchmark: running mine, yours for at least 10 CPU seconds...
mine: 10 wallclock secs (10.01 usr +  0.01 sys = 10.02 CPU) @ 111456.89/s (n=1116798)
yours: 11 wallclock secs (10.04 usr +  0.00 sys = 10.04 CPU) @ 250092.33/s (n=2510927)

Похоже, у вас в 2,25 раза быстрее! (При использовании режима «не менее 10 секунд ЦП» необходимо проверить значения «n»).

Похоже, вам нужно продолжать поиск ... но не забудьте использовать Benchmark!

4 голосов
/ 05 февраля 2010

Я думаю, что если вы беспокоитесь о скорости этого - вы серьезно теряете свои заботы.

Функция commify работает на моем рабочем столе 130000 раз в секунду, например: «31243245356.4432».

это означает, что если у вас на странице 10000 номеров, его передача займет 76мс. А обработка шаблонного инструментария страницы, вероятно, займет в 2-3 раза больше времени.

3 голосов
/ 05 февраля 2010
sub commify {
    my $n = $_[0];

    my $s = abs($n) != $n;
    my $x = index($n, '.');
    $x = length($n) if $x == -1;
    substr($n, $x, 0, ',') while ($x -= 3) > $s;
    return $n;
}
2 голосов
/ 16 марта 2010

Это можно сделать с помощью одного регулярного выражения:

$number =~ s/(?<=\d)(?=(?:\d\d\d)+(?!\d))/,/g;
2 голосов
/ 05 февраля 2010

Я согласен с Брайаном и Депесом: с практической точки зрения, эта функция, вероятно, не место для запуска, если вы пытаетесь улучшить производительность вашего приложения. Тем не менее, можно написать гораздо более быструю функцию commify. Один из способов - избежать регулярных выражений.

use strict;
use warnings;
use Benchmark qw(cmpthese);

sub commify_regex {
    my $text = reverse $_[0];
    $text =~ s{(\d\d\d)(?=\d)(?!\d*\.)}{$1,}g;
    return scalar reverse $text;
}

sub commify_substr {
    my $v = $_[0];
    my $len = length $v;
    my $dec = index($v, '.');
    my $i = 3 + ($dec < 0 ? 0 : $len - $dec);
    $len -- unless $v == abs($v);
    while ($i < $len ++){
        substr($v, -$i, 0, ',');
        $i += 4;    
    }
    return $v;
}

my @tests = qw(
    1 12 123 1234 12345 123456 1234567 
    12345678 123456789 1234567890 12345678901 
    123456789012 1234567890123
);
push @tests, map "$_.$_", @tests;
push @tests, map - $_,    @tests;

for my $t (@tests){
    print "Incorrect for: ", $t, "\n"
        unless commify_substr($t) eq commify_regex($t);
}

cmpthese( -2, {
    regex  => sub { commify_regex($_)  for @tests },
    substr => sub { commify_substr($_) for @tests },
});

# Output on my Windows machine.

         Rate  regex substr
regex  3277/s     --   -54%
substr 7109/s   117%     --
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...