Страницы

вторник, 10 мая 2011 г.

Use perl или продолжаем оптимизацию

Бродил по коду проекта на новой работе и набрел на такую вот функцию :


sub translit
{
my $text = shift;
$text = "$text";

$text =~ s/А/A/g;
$text =~ s/Б/B/g;
$text =~ s/В/V/g;
$text =~ s/Г/G/g;
$text =~ s/Д/D/g;
$text =~ s/Е/E/g;
$text =~ s/Ё/E/g;
$text =~ s/Ж/ZCH/g;
$text =~ s/З/Z/g;
$text =~ s/И/I/g;
$text =~ s/Й/J/g;
$text =~ s/К/K/g;
$text =~ s/Л/L/g;
$text =~ s/М/M/g;
$text =~ s/Н/N/g;
$text =~ s/О/O/g;
$text =~ s/П/P/g;
$text =~ s/Р/R/g;
$text =~ s/С/S/g;
$text =~ s/Т/T/g;
$text =~ s/У/U/g;
$text =~ s/Ф/F/g;
$text =~ s/Х/H/g;
$text =~ s/Ц/C/g;
$text =~ s/Ч/CH/g;
$text =~ s/Ш/SH/g;
$text =~ s/Щ/SCH/g;
$text =~ s/Ь/'/g;
$text =~ s/Ъ/'/g;
$text =~ s/Ы/Y/g;
$text =~ s/Э/E/g;
$text =~ s/Ю/YU/g;
$text =~ s/Я/YA/g;
$text =~ s/а/a/g;
$text =~ s/б/b/g;
$text =~ s/в/v/g;
$text =~ s/г/g/g;
$text =~ s/д/d/g;
$text =~ s/е/e/g;
$text =~ s/ё/e/g;
$text =~ s/ж/zch/g;
$text =~ s/з/z/g;
$text =~ s/и/i/g;
$text =~ s/й/j/g;
$text =~ s/к/k/g;
$text =~ s/л/l/g;
$text =~ s/м/m/g;
$text =~ s/н/n/g;
$text =~ s/о/o/g;
$text =~ s/п/p/g;
$text =~ s/р/r/g;
$text =~ s/с/s/g;
$text =~ s/т/t/g;
$text =~ s/у/u/g;
$text =~ s/ф/f/g;
$text =~ s/х/h/g;
$text =~ s/ц/c/g;
$text =~ s/ч/ch/g;
$text =~ s/ш/sh/g;
$text =~ s/щ/sch/g;
$text =~ s/ь/'/g;
$text =~ s/ъ/'/g;
$text =~ s/ы/y/g;
$text =~ s/э/e/g;
$text =~ s/ю/yu/g;
$text =~ s/я/ya/g;
$text =~ s/і/i/g;
$text =~ s/ї/yi/g;
$text =~ s/є/e/g;

return $text;
}


Подумал про себя, чем не развертывание цикла, хоть и слегка...ммм, в общем не будем об этом.
Как вы догадались, это функция перевода в транслит текста, а теперь важный момент текста в sms сообщениях. Функция сразу выглядела ужасно, но как показали дальнейшие изыскания она имеет право на существование. Модифицируем ее


sub translit_new_cycle
{
my $text = shift;
my %hash_=(
'А'=>'A',
'Б'=>'B',
'В'=>'V',
'Г'=>'G',
'Д'=>'D',
'Е'=>'E',
'Ё'=>'E',
'Ж'=>'ZCH',
'З'=>'Z',
'И'=>'I',
'Й'=>'J',
'К'=>'K',
'Л'=>'L',
'М'=>'M',
'Н'=>'N',
'О'=>'O',
'П'=>'P',
'Р'=>'R',
'С'=>'S',
'Т'=>'T',
'У'=>'U',
'Ф'=>'F',
'Х'=>'H',
'Ц'=>'C',
'Ч'=>'CH',
'Ш'=>'SH',
'Щ'=>'SCH',
'Ь'=>"'",
'Ъ'=>"'",
'Ы'=>'Y',
'Э'=>'E',
'Ю'=>'YU',
'Я'=>'YA',
'а'=>'a',
'б'=>'b',
'в'=>'v',
'г'=>'g',
'д'=>'d',
'е'=>'e',
'ё'=>'e',
'ж'=>'zch',
'з'=>'z',
'и'=>'i',
'й'=>'j',
'к'=>'k',
'л'=>'l',
'м'=>'m',
'н'=>'n',
'о'=>'o',
'п'=>'p',
'р'=>'r',
'с'=>'s',
'т'=>'t',
'у'=>'u',
'ф'=>'f',
'х'=>'h',
'ц'=>'c',
'ч'=>'ch',
'ш'=>'sh',
'щ'=>'sch',
'ь'=>"'",
'ъ'=>"'",
'ы'=>'y',
'э'=>'e',
'ю'=>'yu',
'я'=>'ya',
'і'=>'i',
'ї'=>'yi',
'є'=>'e',
' '=>' ',
"\n"=>"\n"

);
my @a=split(//,$text);
foreach(@a){
$_=$hash_{$_};
}


#
return join("",@a);
}


А теперь тестируем и...

#!/usr/bin/perl
use strict;
use Benchmark qw(:all);
use Data::Dumper;
use utf8;


my @a=(
"Я родил собаку это меня убило как же все тупо Я родил собаку это меня убило как же все тупо"

);


my $count=100000;

foreach(@a){
timethese($count, {
'may be good' => "translit_new_cycle('$_')",
'may be bad' => "translit('$_')",
});
}


получаем

may be bad: 11 wallclock secs (10.24 usr + 0.00 sys = 10.24 CPU) @ 9765.62/s (n=100000)
may be good: 12 wallclock secs (10.45 usr + 0.00 sys = 10.45 CPU) @ 9569.38/s (n=100000)

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

Benchmark: timing 100000 iterations of may be bad, may be good...
may be bad: 3 wallclock secs ( 2.63 usr + 0.00 sys = 2.63 CPU) @ 38022.81/s (n=100000)
may be good: 5 wallclock secs ( 4.96 usr + 0.00 sys = 4.96 CPU) @ 20161.29/s (n=100000)


Но увеличившая разница во времени( почти в два раза) насторожила, введем очень длинную строку для тестов "Я родил собаку родил собаку это меня убило как же все тупо родил собаку это меня убило как же все тупо родил собаку это меня убило как же все тупо родил собаку это меня убило как же все тупо"

may be bad: 22 wallclock secs (18.29 usr + 0.02 sys = 18.31 CPU) @ 5461.50/s (n=100000)
may be good: 22 wallclock secs (17.58 usr + 0.01 sys = 17.59 CPU) @ 5685.05/s (n=100000)



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

my %hash_=(
'А'=>'A',
'Б'=>'B',
'В'=>'V',
'Г'=>'G',
'Д'=>'D',
'Е'=>'E',
'Ё'=>'E',
'Ж'=>'ZCH',
'З'=>'Z',
'И'=>'I',
'Й'=>'J',
'К'=>'K',
'Л'=>'L',
'М'=>'M',
'Н'=>'N',
'О'=>'O',
'П'=>'P',
'Р'=>'R',
'С'=>'S',
'Т'=>'T',
'У'=>'U',
'Ф'=>'F',
'Х'=>'H',
'Ц'=>'C',
'Ч'=>'CH',
'Ш'=>'SH',
'Щ'=>'SCH',
'Ь'=>"'",
'Ъ'=>"'",
'Ы'=>'Y',
'Э'=>'E',
'Ю'=>'YU',
'Я'=>'YA',
'а'=>'a',
'б'=>'b',
'в'=>'v',
'г'=>'g',
'д'=>'d',
'е'=>'e',
'ё'=>'e',
'ж'=>'zch',
'з'=>'z',
'и'=>'i',
'й'=>'j',
'к'=>'k',
'л'=>'l',
'м'=>'m',
'н'=>'n',
'о'=>'o',
'п'=>'p',
'р'=>'r',
'с'=>'s',
'т'=>'t',
'у'=>'u',
'ф'=>'f',
'х'=>'h',
'ц'=>'c',
'ч'=>'ch',
'ш'=>'sh',
'щ'=>'sch',
'ь'=>"'",
'ъ'=>"'",
'ы'=>'y',
'э'=>'e',
'ю'=>'yu',
'я'=>'ya',
'і'=>'i',
'ї'=>'yi',
'є'=>'e',
' '=>' ',
"\n"=>"\n"

);

sub translit_new_cycle
{
my $text = shift;

my @a=split(//,$text);
foreach(@a){
$_=$hash_{$_};
}


#
return join("",@a);
}


то получим наконец то, что ожидали...Хотя в принципе можно и лучше, например если заменить хеш, массивом, а цикл развернуть, так как длинна смс все таки фиксированная..


may be bad: 23 wallclock secs (18.13 usr + 0.00 sys = 18.13 CPU) @ 5515.72/s (n=100000)
may be good: 17 wallclock secs (13.86 usr + 0.02 sys = 13.88 CPU) @ 7204.61/s (n=100000)

На более коротких текстах типа SMS, наша процедура выигрывает с преимуществом в два раза.

Собственно к чему я это...Наверно к тому что панацеи не существует

4 комментария:

afiskon комментирует...

Я извиняюсь, а про tr// вы не слышали?

Богдан комментирует...

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

Богдан комментирует...
Этот комментарий был удален автором.
Богдан комментирует...

кстати решил переписать исходную функцию с использованием tr//, результат сильно хуже вообще всех рассмотренных вариантов, будет время вылажу вариант и результаты, заодно и перепроверю