SlideShare ist ein Scribd-Unternehmen logo
1 von 42
Downloaden Sie, um offline zu lesen
Разработка на Perl

под Raspberry PI
!

Илья Чесноков
Raspberry PI (model B)
•

CPU: 700 MHz (ARM11) с графическим ядром

•

RAM: 512 Mb

•

I/O: 2xUSB 2.0, Ethernet, HDMI, RCA Video, Audio,
GPIO, разъем для SD карт

•

Питание: 5В x 1A (з/у смартфона)

•

Потребление энергии в режиме простоя: ~2 Вт

•

Стоимость: $35 (без периферии)
Официальные
дистрибутивы
•

Raspbian

•

Pidora

•

RISC OS

•

RaspBMC

•

Arch

•

OpenELEC

•

…
Raspbian
•

Устанавливается копированием образа на
флешку

•

Perl v5.14.2

•

Легко ставится cpanminus, perlbrew, local::lib и
т.д.

•

Много Perl-модулей среди пакетов системы
Задача
•

Поиск объявлений, релевантных для указанных
пользователей

•

Реализация на Perl

•

Должно работать на Raspberry PI
•

Время обработки 100 профилей и 100
объявлений не должно превышать 2 секунды
Файл профилей
<?xml version="1.0" encoding="utf-8"?>
<xml>
<profiles>
. . .
<profile>
<name>David Brown</name>
<gender>Male</gender>
<age>34</age>
<likes>
<like>Music</like>
<like>Sports</like>
<like>Movies</like>
</likes>
<timestamp>2013-10-25 13:34:45</timestamp>
</profile>
. . .
</profiles>
</xml>
Файл объявлений
<?xml version="1.0" encoding="utf-8"?>
<xml>
<Playlist PlayerId="1000001">
<Content id="23" type="image" location="/Content/a2/1w3ewsed.jpg"
ageMin="23" ageMax="35" gender="male" likes="music;movies" />
<Content id="237" type="image" location="/Content/0f/gtfrddsed.jpg"
ageMax="35" gender="all" likes="sports" />
<Content id="21" type="image" location="/Content/bf/1w3ewsed.jpg"
ageMin="40" gender="female" />
<Content id="33" type="video" location="/Content/9b/jiuhnnj.mp4"
gender="male" likes="music;movies" />
</Playlist>
</xml>
Алгоритм выбора объявления
•

Для каждого профиля:
•

Если профиль устаревший, то пропускаем

•

Для каждого объявления добавляем 1 очко, если
в данном профиле совпадает что-то из:
•

•

пол, возраст, предпочтения (likes)

Выбираем объявления с максимумом очков (если их
несколько, выбираем из них случайным образом)
Первый вариант программы
use
use
use
use
use
use
use
use
use
use

!

common::sense;
local::lib;
Const::Fast;
DateTime;
Data::Dump qw(pp);
File::Temp ();
File::Copy qw(mv);
List::UtilsBy qw(max_by);
Time::HiRes qw(gettimeofday tv_interval);
XML::Rules;

process_files(@ARGV);
Парсинг XML и обработка данных
# Parse media file
use XML::Rules;
my $media_parser = XML::Rules->new(
style
=> 'parser',
stripspaces
=> 3,
warnoverwrite => 1,
rules
=> {
Content => sub {
my ($tag_name, $attr) = @_;

!

delete @{$attr}{qw(location type)};
$attr->{likes} = [split /s*;s*/, lc $attr->{likes}]
if exists $attr->{likes};
return $attr;
},
Playlist => sub {
return Playlist => {
ads
=> $_[1]->{_content},
PlayerId => $_[1]->{PlayerId},
};
},
xml => sub { $_[1] },
},
);
my $playlist = $media_parser->parse_file($media_content_file)->{Playlist};
# Parse profiles file
my $profile_parser = XML::Rules->new(
style
=> ‘filter’,
rules
=> {
_default => 'raw',
profile
=> sub {
my ($tag_name, $attr, undef, undef, $parser) = @_;
my $profile = extract_tags({ %{ $attr } }->{_content});
$profile->{likes}
= [map { lc $_->[1]->{_content} } @{ $profile->{likes} }];

!

if (is_profile_outdated($profile->{timestamp})) {
mark_as_outdated($profile);
return ();
}

!

for my $ad (@{ $parser->{parameters}->{ads} }) {
my $score = calc_score($ad, $profile);
$ad->{scores}->{ $profile->{name} } = $score;
$ad->{scores}->{total} += $score;
$parser->{parameters}->{total_scores} += $score;
}
return $tag_name => $attr;
},
});
$profile_parser->filterfile($profile_file, $tmp_fh->filename, $playlist);
Время работы
time ./parser.pl samples/profile.xml samples/media.xml 	
Done in 30.186789 seconds	

!
real	
0m30.531s	
user	
0m30.050s	
sys	 0m0.340s
Оптимизация
Шаг 1. Используем «быстрые» модули
use DateTime;
sub timed_out_at {
state $old_date = DateTime->now(
time_zone => DateTime::TimeZone->new(name => 'local')
)->add(seconds => -$PROFILE_TIMEOUT);
state $timed_out_at = $old_date->ymd('-') . ' ' . $old_date->hms(':');
return $timed_out_at;
}
sub is_profile_outdated {
my ($timestamp) = @_;
return timed_out_at() gt $timestamp;
}

!

time ./parser.pl samples/profile.xml samples/media.xml 	
Done in 30.186789 seconds	

!
real	
0m30.531s	
user	
0m30.050s	
sys	 0m0.340s
Шаг 1. Используем «быстрые» модули
use Date::Calc qw(Today_and_Now Add_Delta_DHMS);
{
my $timed_out_at;

!
!

sub timed_out_at {
return $timed_out_at if $timed_out_at;
my ($year, $month, $day, $hour, $min, $sec)
= Add_Delta_DHMS(Today_and_Now(),
00, 00, 00, -$PROFILE_TIMEOUT);
$timed_out_at = "$year-$month-$day $hour:$min:$sec";
return $timed_out_at;
}

}

!

time ./parser.pl samples/profile.xml samples/media.xml 	
Done in 26.389202 seconds	

!
real	
0m26.697s	
user	
0m26.310s	
sys	 0m0.150s
Шаг 2. Ненужная отладка
profile
=> sub {
# . . .
for my $ad (@{ $parser->{parameters}->{ads} }) {
my $score = calc_score($ad, $profile);

$ad->{scores}->{ $profile->{name} } = $score;
$ad->{scores}->{total} += $score;
$parser->{parameters}->{total_scores} += $score;

}
return $tag_name => $attr;

!

},

# . . .

!
debug('ads with scores: ' . pp($playlist));
Шаг 2. Ненужная отладка
my $DEBUG = $ENV{DEBUG} // 0;
if ($DEBUG) {
require Data::Dump;
}

!

sub debug {
return if !$DEBUG;

!

warn map { ref $_ ? Data::Dump::pp($_) : $_ } @_;
}

!

debug('ads with scores: ', $playlist); # Запятая!

time ./parser.pl samples/profile.xml samples/media.xml 	
Done in 6.818755 seconds	

!
real	
0m7.080s	
user	
0m6.950s	
sys	 0m0.090s
Файл объявлений
•

Читается один раз

•

Не нуждается в потоковой обработке

•

Можно использовать более быстрый парсер XML
XML::Fast + File::Map
use XML::Fast qw(xml2hash);
use File::Map qw(map_file);

!

sub read_media_file {
my $media_content_file = shift;

!

map_file my $media_map, $media_content_file;
my $playlist = xml2hash($media_map, attr => '')->{xml}->{Playlist};
for my $ad (@{ $playlist->{Content} }) {
$ad->{likes} = [split /;/, lc $ad->{likes}]
if exists $ad->{likes};
}
return $playlist;
}
time ./parser.pl samples/profile.xml samples/media.xml 	
Done in 7.259239 seconds	

!
real	
0m7.522s	
user	
0m7.360s	
sys	 0m0.100s
Шаг 3. Замеряем время работы
#!/usr/bin/env perl
my $t;
BEGIN {
use Devel::Timer;
$t = Devel::Timer->new();
$t->mark('BEGIN');
}
END {
$t->mark('END');
$t->report(collapse => 1);
}
use local::lib;
use common::sense;
use Const::Fast;
use Date::Calc qw(Today_and_Now Add_Delta_DHMS);
use Data::Dump qw(pp);
use File::Temp ();
use File::Copy qw(mv);
use List::UtilsBy qw(max_by);
use XML::Rules;
use XML::Fast qw(xml2hash);
use File::Map qw(map_file);

!

$t->mark(‘loaded'); # такие отметки по всей программе
Шаг 3. Замеряем время работы
time ./parser.pl samples/profile.xml samples/media.xml

!
!
Devel::Timer Report -- Total time: 7.3259 secs
Count Time Percent
---------------------------------------------1 3.6184 49.39% created temp file -> profiles processed
1 1.6455 22.46% BEGIN -> loaded
1 1.1747 16.03% loaded -> media file read
1 0.8662 11.82% file moved -> ad shown
1 0.0079 0.11% ad shown -> END
1 0.0060 0.08% set up profile rules -> created temp file
1 0.0036 0.05% profiles processed -> file moved
1 0.0032 0.04% media file read -> set up profile rules
1 0.0004 0.00% INIT -> BEGIN

!
real 0m7.584s
user 0m7.370s
sys 0m0.130s

# подсчет очков
# загрузка модулей
# чтение файла объявлений
# get_ad_to_show() && show_ad()
Время загрузки модулей
use
use
use
use
use
use
use
use
use
use
use

!

common::sense;
$t->mark('loaded
local::lib;
$t->mark('loaded
Const::Fast;
$t->mark('loaded
Date::Calc qw(Today_and_Now Add_Delta_DHMS);
$t->mark('loaded Date::Calc');
Data::Dump qw(pp);
$t->mark('loaded
File::Temp ();
$t->mark('loaded
File::Copy qw(mv);
$t->mark('loaded
List::UtilsBy qw(max_by);
$t->mark('loaded
XML::Rules;
$t->mark('loaded
XML::Fast qw(xml2hash);
$t->mark('loaded
File::Map qw(map_file);
$t->mark('loaded

$t->mark('modules loaded');
exit;

!

common::sense;');
local::lib;');
Const::Fast;');
Data::Dump');
File::Temp');
File::Copy');
List::UtilsBy');
XML::Rules;');
XML::Fast');
File::Map');
Время загрузки модулей
Devel::Timer Report -- Total time: 1.6661 secs
Interval Time Percent
---------------------------------------------01 -> 02 1.6611 99.70% BEGIN -> loaded common::sense;
13 -> 14 0.0023 0.14% modules loaded -> END
00 -> 01 0.0004 0.02% INIT -> BEGIN
10 -> 11 0.0003 0.02% loaded XML::Rules; -> loaded XML::Fast
02 -> 03 0.0003 0.02% loaded common::sense; -> loaded local::lib;
12 -> 13 0.0002 0.01% loaded File::Map -> modules loaded
04 -> 05 0.0002 0.01% loaded Const::Fast; -> loaded Date::Calc
11 -> 12 0.0002 0.01% loaded XML::Fast -> loaded File::Map
07 -> 08 0.0002 0.01% loaded File::Temp -> loaded File::Copy
06 -> 07 0.0002 0.01% loaded Data::Dump -> loaded File::Temp
05 -> 06 0.0002 0.01% loaded Date::Calc -> loaded Data::Dump
08 -> 09 0.0002 0.01% loaded File::Copy -> loaded List::UtilsBy
09 -> 10 0.0002 0.01% loaded List::UtilsBy -> loaded XML::Rules;
03 -> 04 0.0002 0.01% loaded local::lib; -> loaded Const::Fast;
Время загрузки модулей
require
require
require
require
require
require
require
require
require
require
require

!

common::sense;
local::lib;
Const::Fast;
Date::Calc;
Data::Dump;
File::Temp;
File::Copy;
List::UtilsBy;
XML::Rules;
XML::Fast;
File::Map;

$t->mark('loaded
$t->mark('loaded
$t->mark('loaded
$t->mark('loaded
$t->mark('loaded
$t->mark('loaded
$t->mark('loaded
$t->mark('loaded
$t->mark('loaded
$t->mark('loaded
$t->mark('loaded

$t->mark('modules loaded');
exit;

common::sense;');
local::lib;');
Const::Fast;');
Date::Calc');
Data::Dump');
File::Temp');
File::Copy');
List::UtilsBy');
XML::Rules;');
XML::Fast');
File::Map');
Время загрузки модулей
Devel::Timer Report -- Total time: 1.6027 secs
Interval Time Percent
---------------------------------------------09 -> 10 0.3867 24.13% loaded List::UtilsBy -> loaded XML::Rules;
06 -> 07 0.3401 21.22% loaded Data::Dump -> loaded File::Temp
02 -> 03 0.3279 20.46% loaded common::sense; -> loaded local::lib;
10 -> 11 0.1328 8.28% loaded XML::Rules; -> loaded XML::Fast
04 -> 05 0.1080 6.74% loaded Const::Fast; -> loaded Date::Calc
05 -> 06 0.0851 5.31% loaded Date::Calc -> loaded Data::Dump
11 -> 12 0.0772 4.81% loaded XML::Fast -> loaded File::Map
03 -> 04 0.0539 3.36% loaded local::lib; -> loaded Const::Fast;
07 -> 08 0.0521 3.25% loaded File::Temp -> loaded File::Copy
08 -> 09 0.0265 1.65% loaded File::Copy -> loaded List::UtilsBy
01 -> 02 0.0095 0.59% BEGIN -> loaded common::sense;
13 -> 14 0.0024 0.15% modules loaded -> END
00 -> 01 0.0004 0.02% INIT -> BEGIN
12 -> 13 0.0003 0.02% loaded File::Map -> modules loaded
Выкидываем лишние модули
•

XML::Rules -> XML::Fast (асинхронность не
нужна)

•

File::Temp, File::Copy, File::Map (работаем с
файлами через стандартные функции Perl)

•

local::lib (настраиваем пути в .bashrc)

•

Const::Fast

•

Data::Dump - загружаем по требованию
Многократно повторяющиеся
элементы
$profile->{likes}
= [map { lc $_->[1]->{_content} } @{ $profile->{likes} }];
...
+
my $profiles = xml2hash(lc slurp($profile_file))->{xml}->{profiles}>{profile};

!
!

for my $ad (@{ $playlist->{Content} }) {
$ad->{likes} = [split /;/, lc $ad->{likes}]
...
+
my $playlist = xml2hash(lc slurp($media_file), attr => '')->{xml}>{playlist};

•

Проблема: изменен регистр символов на выходе
- но это не критично
Результат
time ./parser.pl samples/profile.xml samples/media.xml

!
Devel::Timer Report -- Total time: 1.8889 secs
Count Time Percent
---------------------------------------------1 1.3133 69.53% calculating scores: start -> calculating scores: end
1 0.4887 25.87% program: start -> modules loaded
1 0.0329 1.74% read profile file: start -> read profile file: end
1 0.0157 0.83% searching for ad to show: start -> searching for ad to show: end
1 0.0143 0.76% write profile file: start -> write profile file: end
1 0.0126 0.67% read media file: start -> read media file: end
1 0.0046 0.24% convert media file: start -> convert media file: end
1 0.0027 0.14% searching for ad to show: end -> program: end
1 0.0023 0.12% write profile file: end -> searching for ad to show: start

!
real 0m2.142s
user 0m2.040s
sys 0m0.070s
Почти хорошо!
Но можно лучше!
Пересмотрим результаты
time ./parser.pl samples/profile.xml samples/media.xml

!
Devel::Timer Report -- Total time: 1.8889 secs
Count Time Percent
---------------------------------------------1 1.3133 69.53% calculating scores: start -> calculating scores: end!
1 0.4887 25.87% program: start -> modules loaded!
1 0.0329 1.74% read profile file: start -> read profile file: end
1 0.0157 0.83% searching for ad to show: start -> searching for ad to show: end
1 0.0143 0.76% write profile file: start -> write profile file: end
...

!
real 0m2.142s
user 0m2.040s
sys 0m0.070s

•

Не хватает CPU!
Сложность алгоритма
!

O(MxN)
M - количество объявлений
N - количество пользователей
Как уменьшить?
!

(в идеале - привести к O(N))
Объявления
•

Основные параметры
•

Возраст клиента

•

Пол клиента

•

Предпочтения клиента (их меньше, чем объявлений)

•

Количество значений ограничено

•

Значения известны после загрузки объявлений

•

Почему бы не проиндексировать?
my %age;
# Ads valid for given age
my %gender; # Ads scored for given gender
my %like;
# Ads scored for given preference
for my $ad (@{ $playlist->{content} }) {
# Fill ages
my $age_min = int($ad->{agemin}) || 0;
my $age_max = int($ad->{agemax}) || 100;
for my $current_age ($age_min .. $age_max) {
push @{ $age{$current_age} }, $ad->{id};
}
# Fill genders
given ($ad->{gender}) {
when ('male') {
push @{ $gender{male} }, $ad->{id};
}
when ('female') {
push @{ $gender{female} }, $ad->{id};
}
when ('all') {
push @{ $gender{male} }, $ad->{id};
push @{ $gender{female} }, $ad->{id};
}
}
# Fill likes
for my $current_like (split /;/, $ad->{likes}) {
push @{ $like{$current_like} }, $ad->{id};
}
}
Подсчет очков
•

Параметры профиля - те же, что и в объявлениях

•

Сразу получаем нужные объявления для каждого
значения параметра и добавляем им очки
!
# Score of each ad
my %score;
sub calc_score {
my ($profile) = @_;
# Gender
if (exists $profile->{gender}) {
for my $ad_id (@{ $gender{$profile->{gender}} || [] }) {
$score{$ad_id}++;
}
}
# Age
if (exists $profile->{age}) {
for my $ad_id (@{ $age{$profile->{age}} }) {
$score{$ad_id}++;
}
}
# Likes
if (ref $profile->{likes} && ref $profile->{likes}->{like}) {
for my $profile_like (@{ $profile->{likes}->{like} }) {
for my $ad_id (@{ $like{ $profile_like } || [] }) {
$score{$ad_id}++;
}
}
}
}
Результат
time ./parser.pl samples/profile.xml samples/media.xml

!
Devel::Timer Report -- Total time: 0.8667 secs
Count Time Percent
---------------------------------------------1 0.5066 58.45% program: start -> modules loaded
1 0.1656 19.11% calculating scores: start -> calculating scores: end
1 0.1177 13.58% index media file: start -> index media file: end
1 0.0339 3.91% read profile file: start -> read profile file: end
1 0.0152 1.75% write profile file: start -> write profile file: end
1 0.0130 1.50% read media file: start -> read media file: end

!
real 0m1.119s
user 0m1.070s
sys 0m0.020s
И еще кое-что
•

Raspberry PI поддерживает оверклокинг
•

системными инструментами (raspi-config)

•

безопасно (типа :))

•

700 -> 800 -> 900 -> 950 -> 1000 MHz
После оверклокинга
time ./parser.pl samples/profile.xml samples/media.xml

!
Devel::Timer Report -- Total time: 0.5798 secs
Count Time Percent
---------------------------------------------1 0.3390 58.48% program: start -> modules loaded
1 0.1135 19.58% calculating scores: start -> calculating scores: end
1 0.0754 13.00% index media file: start -> index media file: end
1 0.0216 3.72% read profile file: start -> read profile file: end
1 0.0098 1.69% write profile file: start -> write profile file: end
1 0.0082 1.42% read media file: start -> read media file: end

!
real 0m0.814s
user 0m0.740s
sys 0m0.040s
Выводы
•

Измеряй все, что можешь - никому нельзя
доверять безоговорочно.

•

Выдели важное. Выбрось ненужное.

•

Оптимизируй алгоритм: всегда есть другой путь.

•

Выйди из зоны комфорта.

•

Думай и побеждай.
Спасибо!

Чесноков Илья <chesnokov.ilya@gmail.com>

Weitere ähnliche Inhalte

Was ist angesagt?

Юрий Гольцев - Сервис PLWWW
Юрий Гольцев - Сервис PLWWWЮрий Гольцев - Сервис PLWWW
Юрий Гольцев - Сервис PLWWWPositive Hack Days
 
plwww (24.03) MEPHI (PHDays)
plwww (24.03) MEPHI (PHDays)plwww (24.03) MEPHI (PHDays)
plwww (24.03) MEPHI (PHDays)ygoltsev
 
Что нового в Perl? 5.10 — 5.16
Что нового в Perl? 5.10 — 5.16Что нового в Perl? 5.10 — 5.16
Что нового в Perl? 5.10 — 5.16Anatoly Sharifulin
 
Игорь Любин - PowerShell - ConfeT&QA 2011
Игорь Любин - PowerShell - ConfeT&QA 2011Игорь Любин - PowerShell - ConfeT&QA 2011
Игорь Любин - PowerShell - ConfeT&QA 2011ilyubin
 
Перевод базы Caché из 8 бит в Unicode
Перевод базы Caché из 8 бит в UnicodeПеревод базы Caché из 8 бит в Unicode
Перевод базы Caché из 8 бит в UnicodeInterSystems
 
Импорт данных с фреймворком Migrate. Владислав Богатырев.
Импорт данных с фреймворком Migrate. Владислав Богатырев.Импорт данных с фреймворком Migrate. Владислав Богатырев.
Импорт данных с фреймворком Migrate. Владислав Богатырев.DrupalCampDN
 
PHP Advanced
PHP AdvancedPHP Advanced
PHP AdvancedNoveo
 
Web осень 2013 лекция 3
Web осень 2013 лекция 3Web осень 2013 лекция 3
Web осень 2013 лекция 3Technopark
 
Hacking PostgreSQL. Физическое представление данных
Hacking PostgreSQL. Физическое представление данныхHacking PostgreSQL. Физическое представление данных
Hacking PostgreSQL. Физическое представление данныхAnastasia Lubennikova
 
константин лебедев
константин лебедевконстантин лебедев
константин лебедевkuchinskaya
 
FrontTalks: Константин Лебедев (Mail.ru), File API: обработка файлов на клиен...
FrontTalks: Константин Лебедев (Mail.ru), File API: обработка файлов на клиен...FrontTalks: Константин Лебедев (Mail.ru), File API: обработка файлов на клиен...
FrontTalks: Константин Лебедев (Mail.ru), File API: обработка файлов на клиен...Yandex
 
Web осень 2012 лекция 4
Web осень 2012 лекция 4Web осень 2012 лекция 4
Web осень 2012 лекция 4Technopark
 
Динамический код: модифицируем таблицу символов во время выполнения. Елена Ши...
Динамический код: модифицируем таблицу символов во время выполнения. Елена Ши...Динамический код: модифицируем таблицу символов во время выполнения. Елена Ши...
Динамический код: модифицируем таблицу символов во время выполнения. Елена Ши...Moscow.pm
 

Was ist angesagt? (20)

Почему Mojolicious?
Почему Mojolicious?Почему Mojolicious?
Почему Mojolicious?
 
Nginx.pm
Nginx.pmNginx.pm
Nginx.pm
 
Юрий Гольцев - Сервис PLWWW
Юрий Гольцев - Сервис PLWWWЮрий Гольцев - Сервис PLWWW
Юрий Гольцев - Сервис PLWWW
 
plwww (24.03) MEPHI (PHDays)
plwww (24.03) MEPHI (PHDays)plwww (24.03) MEPHI (PHDays)
plwww (24.03) MEPHI (PHDays)
 
Perl 5.10 и 5.12
Perl 5.10 и 5.12Perl 5.10 и 5.12
Perl 5.10 и 5.12
 
Что нового в Perl? 5.10 — 5.16
Что нового в Perl? 5.10 — 5.16Что нового в Perl? 5.10 — 5.16
Что нового в Perl? 5.10 — 5.16
 
Yserver
YserverYserver
Yserver
 
Игорь Любин - PowerShell - ConfeT&QA 2011
Игорь Любин - PowerShell - ConfeT&QA 2011Игорь Любин - PowerShell - ConfeT&QA 2011
Игорь Любин - PowerShell - ConfeT&QA 2011
 
Перевод базы Caché из 8 бит в Unicode
Перевод базы Caché из 8 бит в UnicodeПеревод базы Caché из 8 бит в Unicode
Перевод базы Caché из 8 бит в Unicode
 
Импорт данных с фреймворком Migrate. Владислав Богатырев.
Импорт данных с фреймворком Migrate. Владислав Богатырев.Импорт данных с фреймворком Migrate. Владислав Богатырев.
Импорт данных с фреймворком Migrate. Владислав Богатырев.
 
PHP Advanced
PHP AdvancedPHP Advanced
PHP Advanced
 
Web осень 2013 лекция 3
Web осень 2013 лекция 3Web осень 2013 лекция 3
Web осень 2013 лекция 3
 
Hacking PostgreSQL. Физическое представление данных
Hacking PostgreSQL. Физическое представление данныхHacking PostgreSQL. Физическое представление данных
Hacking PostgreSQL. Физическое представление данных
 
Приручаем linux-консоль
Приручаем linux-консольПриручаем linux-консоль
Приручаем linux-консоль
 
Target.Mail.Ru API v2
Target.Mail.Ru API v2Target.Mail.Ru API v2
Target.Mail.Ru API v2
 
Erlang tasty & useful stuff
Erlang tasty & useful stuffErlang tasty & useful stuff
Erlang tasty & useful stuff
 
константин лебедев
константин лебедевконстантин лебедев
константин лебедев
 
FrontTalks: Константин Лебедев (Mail.ru), File API: обработка файлов на клиен...
FrontTalks: Константин Лебедев (Mail.ru), File API: обработка файлов на клиен...FrontTalks: Константин Лебедев (Mail.ru), File API: обработка файлов на клиен...
FrontTalks: Константин Лебедев (Mail.ru), File API: обработка файлов на клиен...
 
Web осень 2012 лекция 4
Web осень 2012 лекция 4Web осень 2012 лекция 4
Web осень 2012 лекция 4
 
Динамический код: модифицируем таблицу символов во время выполнения. Елена Ши...
Динамический код: модифицируем таблицу символов во время выполнения. Елена Ши...Динамический код: модифицируем таблицу символов во время выполнения. Елена Ши...
Динамический код: модифицируем таблицу символов во время выполнения. Елена Ши...
 

Ähnlich wie Разработка на Perl под Raspberry PI

Вёрстка в стиле Ruby: HAML, SASS, Compass, CoffeeScript, Jammit
Вёрстка в стиле Ruby: HAML, SASS, Compass, CoffeeScript, JammitВёрстка в стиле Ruby: HAML, SASS, Compass, CoffeeScript, Jammit
Вёрстка в стиле Ruby: HAML, SASS, Compass, CoffeeScript, JammitAndrey Sitnik
 
Psgi app
Psgi appPsgi app
Psgi appund3f
 
Behat в PHP с использованием Behat и Mink
Behat в PHP с использованием Behat и MinkBehat в PHP с использованием Behat и Mink
Behat в PHP с использованием Behat и Minktyomo4ka
 
Hacking PostgreSQL. Обзор исходного кода
Hacking PostgreSQL. Обзор исходного кодаHacking PostgreSQL. Обзор исходного кода
Hacking PostgreSQL. Обзор исходного кодаAnastasia Lubennikova
 
Anton Shabouta "Implementing async binary clients in pure PHP"
Anton Shabouta "Implementing async binary clients in pure PHP" Anton Shabouta "Implementing async binary clients in pure PHP"
Anton Shabouta "Implementing async binary clients in pure PHP" Fwdays
 
Миша Рудрастых: Введение в HTTP API WordPress
Миша Рудрастых: Введение в HTTP API WordPressМиша Рудрастых: Введение в HTTP API WordPress
Миша Рудрастых: Введение в HTTP API WordPressRuslan Begaliev
 
Страх и ненависть в исходном коде
Страх и ненависть в исходном кодеСтрах и ненависть в исходном коде
Страх и ненависть в исходном кодеKolya Korobochkin
 
Расширенное кеширование Doctrine2 (Ильяс Салихов, Intaro)
Расширенное кеширование Doctrine2 (Ильяс Салихов, Intaro)Расширенное кеширование Doctrine2 (Ильяс Салихов, Intaro)
Расширенное кеширование Doctrine2 (Ильяс Салихов, Intaro)Symfoniacs
 
Примеры решения типичных задач за рамками ядра Yii2
Примеры решения типичных задач за рамками ядра Yii2Примеры решения типичных задач за рамками ядра Yii2
Примеры решения типичных задач за рамками ядра Yii2Paul Klimov
 
Инструменты для з̶а̶х̶в̶а̶т̶а̶ ̶м̶и̶р̶а̶ отладки в Tarantool
Инструменты для з̶а̶х̶в̶а̶т̶а̶ ̶м̶и̶р̶а̶  отладки в TarantoolИнструменты для з̶а̶х̶в̶а̶т̶а̶ ̶м̶и̶р̶а̶  отладки в Tarantool
Инструменты для з̶а̶х̶в̶а̶т̶а̶ ̶м̶и̶р̶а̶ отладки в TarantoolTimur Safin
 
Cобачники против кинофобов
Cобачники против кинофобовCобачники против кинофобов
Cобачники против кинофобовLidiya Myalkina
 
Silverlight 4, есть ли жизнь на десктопе
Silverlight 4, есть ли жизнь на десктопеSilverlight 4, есть ли жизнь на десктопе
Silverlight 4, есть ли жизнь на десктопеAlex Tumanoff
 
Командная разработка “толстых клиентов”
Командная разработка “толстых клиентов”Командная разработка “толстых клиентов”
Командная разработка “толстых клиентов”Open-IT
 
13 - Hadoop. Парадигма Spark
13 - Hadoop. Парадигма Spark13 - Hadoop. Парадигма Spark
13 - Hadoop. Парадигма SparkRoman Brovko
 
Взломать Web-сайт на ASP.NET? Сложно, но можно!
Взломать Web-сайт на ASP.NET? Сложно, но можно!Взломать Web-сайт на ASP.NET? Сложно, но можно!
Взломать Web-сайт на ASP.NET? Сложно, но можно!Vladimir Kochetkov
 

Ähnlich wie Разработка на Perl под Raspberry PI (20)

Perl – жив?!
Perl – жив?!Perl – жив?!
Perl – жив?!
 
UWDC 2013, Yii2
UWDC 2013, Yii2UWDC 2013, Yii2
UWDC 2013, Yii2
 
Mojolicious
MojoliciousMojolicious
Mojolicious
 
Вёрстка в стиле Ruby: HAML, SASS, Compass, CoffeeScript, Jammit
Вёрстка в стиле Ruby: HAML, SASS, Compass, CoffeeScript, JammitВёрстка в стиле Ruby: HAML, SASS, Compass, CoffeeScript, Jammit
Вёрстка в стиле Ruby: HAML, SASS, Compass, CoffeeScript, Jammit
 
Perl in practice
Perl in practicePerl in practice
Perl in practice
 
Psgi app
Psgi appPsgi app
Psgi app
 
Behat в PHP с использованием Behat и Mink
Behat в PHP с использованием Behat и MinkBehat в PHP с использованием Behat и Mink
Behat в PHP с использованием Behat и Mink
 
Hacking PostgreSQL. Обзор исходного кода
Hacking PostgreSQL. Обзор исходного кодаHacking PostgreSQL. Обзор исходного кода
Hacking PostgreSQL. Обзор исходного кода
 
Anton Shabouta "Implementing async binary clients in pure PHP"
Anton Shabouta "Implementing async binary clients in pure PHP" Anton Shabouta "Implementing async binary clients in pure PHP"
Anton Shabouta "Implementing async binary clients in pure PHP"
 
Миша Рудрастых: Введение в HTTP API WordPress
Миша Рудрастых: Введение в HTTP API WordPressМиша Рудрастых: Введение в HTTP API WordPress
Миша Рудрастых: Введение в HTTP API WordPress
 
Страх и ненависть в исходном коде
Страх и ненависть в исходном кодеСтрах и ненависть в исходном коде
Страх и ненависть в исходном коде
 
Расширенное кеширование Doctrine2 (Ильяс Салихов, Intaro)
Расширенное кеширование Doctrine2 (Ильяс Салихов, Intaro)Расширенное кеширование Doctrine2 (Ильяс Салихов, Intaro)
Расширенное кеширование Doctrine2 (Ильяс Салихов, Intaro)
 
Примеры решения типичных задач за рамками ядра Yii2
Примеры решения типичных задач за рамками ядра Yii2Примеры решения типичных задач за рамками ядра Yii2
Примеры решения типичных задач за рамками ядра Yii2
 
Инструменты для з̶а̶х̶в̶а̶т̶а̶ ̶м̶и̶р̶а̶ отладки в Tarantool
Инструменты для з̶а̶х̶в̶а̶т̶а̶ ̶м̶и̶р̶а̶  отладки в TarantoolИнструменты для з̶а̶х̶в̶а̶т̶а̶ ̶м̶и̶р̶а̶  отладки в Tarantool
Инструменты для з̶а̶х̶в̶а̶т̶а̶ ̶м̶и̶р̶а̶ отладки в Tarantool
 
Cобачники против кинофобов
Cобачники против кинофобовCобачники против кинофобов
Cобачники против кинофобов
 
Silverlight 4, есть ли жизнь на десктопе
Silverlight 4, есть ли жизнь на десктопеSilverlight 4, есть ли жизнь на десктопе
Silverlight 4, есть ли жизнь на десктопе
 
Render API.
Render API.Render API.
Render API.
 
Командная разработка “толстых клиентов”
Командная разработка “толстых клиентов”Командная разработка “толстых клиентов”
Командная разработка “толстых клиентов”
 
13 - Hadoop. Парадигма Spark
13 - Hadoop. Парадигма Spark13 - Hadoop. Парадигма Spark
13 - Hadoop. Парадигма Spark
 
Взломать Web-сайт на ASP.NET? Сложно, но можно!
Взломать Web-сайт на ASP.NET? Сложно, но можно!Взломать Web-сайт на ASP.NET? Сложно, но можно!
Взломать Web-сайт на ASP.NET? Сложно, но можно!
 

Разработка на Perl под Raspberry PI

  • 1. Разработка на Perl под Raspberry PI ! Илья Чесноков
  • 2. Raspberry PI (model B) • CPU: 700 MHz (ARM11) с графическим ядром • RAM: 512 Mb • I/O: 2xUSB 2.0, Ethernet, HDMI, RCA Video, Audio, GPIO, разъем для SD карт • Питание: 5В x 1A (з/у смартфона) • Потребление энергии в режиме простоя: ~2 Вт • Стоимость: $35 (без периферии)
  • 4. Raspbian • Устанавливается копированием образа на флешку • Perl v5.14.2 • Легко ставится cpanminus, perlbrew, local::lib и т.д. • Много Perl-модулей среди пакетов системы
  • 5. Задача • Поиск объявлений, релевантных для указанных пользователей • Реализация на Perl • Должно работать на Raspberry PI • Время обработки 100 профилей и 100 объявлений не должно превышать 2 секунды
  • 6. Файл профилей <?xml version="1.0" encoding="utf-8"?> <xml> <profiles> . . . <profile> <name>David Brown</name> <gender>Male</gender> <age>34</age> <likes> <like>Music</like> <like>Sports</like> <like>Movies</like> </likes> <timestamp>2013-10-25 13:34:45</timestamp> </profile> . . . </profiles> </xml>
  • 7. Файл объявлений <?xml version="1.0" encoding="utf-8"?> <xml> <Playlist PlayerId="1000001"> <Content id="23" type="image" location="/Content/a2/1w3ewsed.jpg" ageMin="23" ageMax="35" gender="male" likes="music;movies" /> <Content id="237" type="image" location="/Content/0f/gtfrddsed.jpg" ageMax="35" gender="all" likes="sports" /> <Content id="21" type="image" location="/Content/bf/1w3ewsed.jpg" ageMin="40" gender="female" /> <Content id="33" type="video" location="/Content/9b/jiuhnnj.mp4" gender="male" likes="music;movies" /> </Playlist> </xml>
  • 8. Алгоритм выбора объявления • Для каждого профиля: • Если профиль устаревший, то пропускаем • Для каждого объявления добавляем 1 очко, если в данном профиле совпадает что-то из: • • пол, возраст, предпочтения (likes) Выбираем объявления с максимумом очков (если их несколько, выбираем из них случайным образом)
  • 9. Первый вариант программы use use use use use use use use use use ! common::sense; local::lib; Const::Fast; DateTime; Data::Dump qw(pp); File::Temp (); File::Copy qw(mv); List::UtilsBy qw(max_by); Time::HiRes qw(gettimeofday tv_interval); XML::Rules; process_files(@ARGV);
  • 10. Парсинг XML и обработка данных # Parse media file use XML::Rules; my $media_parser = XML::Rules->new( style => 'parser', stripspaces => 3, warnoverwrite => 1, rules => { Content => sub { my ($tag_name, $attr) = @_; ! delete @{$attr}{qw(location type)}; $attr->{likes} = [split /s*;s*/, lc $attr->{likes}] if exists $attr->{likes}; return $attr; }, Playlist => sub { return Playlist => { ads => $_[1]->{_content}, PlayerId => $_[1]->{PlayerId}, }; }, xml => sub { $_[1] }, }, ); my $playlist = $media_parser->parse_file($media_content_file)->{Playlist};
  • 11. # Parse profiles file my $profile_parser = XML::Rules->new( style => ‘filter’, rules => { _default => 'raw', profile => sub { my ($tag_name, $attr, undef, undef, $parser) = @_; my $profile = extract_tags({ %{ $attr } }->{_content}); $profile->{likes} = [map { lc $_->[1]->{_content} } @{ $profile->{likes} }]; ! if (is_profile_outdated($profile->{timestamp})) { mark_as_outdated($profile); return (); } ! for my $ad (@{ $parser->{parameters}->{ads} }) { my $score = calc_score($ad, $profile); $ad->{scores}->{ $profile->{name} } = $score; $ad->{scores}->{total} += $score; $parser->{parameters}->{total_scores} += $score; } return $tag_name => $attr; }, }); $profile_parser->filterfile($profile_file, $tmp_fh->filename, $playlist);
  • 12. Время работы time ./parser.pl samples/profile.xml samples/media.xml Done in 30.186789 seconds ! real 0m30.531s user 0m30.050s sys 0m0.340s
  • 14. Шаг 1. Используем «быстрые» модули use DateTime; sub timed_out_at { state $old_date = DateTime->now( time_zone => DateTime::TimeZone->new(name => 'local') )->add(seconds => -$PROFILE_TIMEOUT); state $timed_out_at = $old_date->ymd('-') . ' ' . $old_date->hms(':'); return $timed_out_at; } sub is_profile_outdated { my ($timestamp) = @_; return timed_out_at() gt $timestamp; } ! time ./parser.pl samples/profile.xml samples/media.xml Done in 30.186789 seconds ! real 0m30.531s user 0m30.050s sys 0m0.340s
  • 15. Шаг 1. Используем «быстрые» модули use Date::Calc qw(Today_and_Now Add_Delta_DHMS); { my $timed_out_at; ! ! sub timed_out_at { return $timed_out_at if $timed_out_at; my ($year, $month, $day, $hour, $min, $sec) = Add_Delta_DHMS(Today_and_Now(), 00, 00, 00, -$PROFILE_TIMEOUT); $timed_out_at = "$year-$month-$day $hour:$min:$sec"; return $timed_out_at; } } ! time ./parser.pl samples/profile.xml samples/media.xml Done in 26.389202 seconds ! real 0m26.697s user 0m26.310s sys 0m0.150s
  • 16. Шаг 2. Ненужная отладка profile => sub { # . . . for my $ad (@{ $parser->{parameters}->{ads} }) { my $score = calc_score($ad, $profile); $ad->{scores}->{ $profile->{name} } = $score; $ad->{scores}->{total} += $score; $parser->{parameters}->{total_scores} += $score; } return $tag_name => $attr; ! }, # . . . ! debug('ads with scores: ' . pp($playlist));
  • 17. Шаг 2. Ненужная отладка my $DEBUG = $ENV{DEBUG} // 0; if ($DEBUG) { require Data::Dump; } ! sub debug { return if !$DEBUG; ! warn map { ref $_ ? Data::Dump::pp($_) : $_ } @_; } ! debug('ads with scores: ', $playlist); # Запятая! time ./parser.pl samples/profile.xml samples/media.xml Done in 6.818755 seconds ! real 0m7.080s user 0m6.950s sys 0m0.090s
  • 18. Файл объявлений • Читается один раз • Не нуждается в потоковой обработке • Можно использовать более быстрый парсер XML
  • 19. XML::Fast + File::Map use XML::Fast qw(xml2hash); use File::Map qw(map_file); ! sub read_media_file { my $media_content_file = shift; ! map_file my $media_map, $media_content_file; my $playlist = xml2hash($media_map, attr => '')->{xml}->{Playlist}; for my $ad (@{ $playlist->{Content} }) { $ad->{likes} = [split /;/, lc $ad->{likes}] if exists $ad->{likes}; } return $playlist; } time ./parser.pl samples/profile.xml samples/media.xml Done in 7.259239 seconds ! real 0m7.522s user 0m7.360s sys 0m0.100s
  • 20. Шаг 3. Замеряем время работы #!/usr/bin/env perl my $t; BEGIN { use Devel::Timer; $t = Devel::Timer->new(); $t->mark('BEGIN'); } END { $t->mark('END'); $t->report(collapse => 1); } use local::lib; use common::sense; use Const::Fast; use Date::Calc qw(Today_and_Now Add_Delta_DHMS); use Data::Dump qw(pp); use File::Temp (); use File::Copy qw(mv); use List::UtilsBy qw(max_by); use XML::Rules; use XML::Fast qw(xml2hash); use File::Map qw(map_file); ! $t->mark(‘loaded'); # такие отметки по всей программе
  • 21. Шаг 3. Замеряем время работы time ./parser.pl samples/profile.xml samples/media.xml ! ! Devel::Timer Report -- Total time: 7.3259 secs Count Time Percent ---------------------------------------------1 3.6184 49.39% created temp file -> profiles processed 1 1.6455 22.46% BEGIN -> loaded 1 1.1747 16.03% loaded -> media file read 1 0.8662 11.82% file moved -> ad shown 1 0.0079 0.11% ad shown -> END 1 0.0060 0.08% set up profile rules -> created temp file 1 0.0036 0.05% profiles processed -> file moved 1 0.0032 0.04% media file read -> set up profile rules 1 0.0004 0.00% INIT -> BEGIN ! real 0m7.584s user 0m7.370s sys 0m0.130s # подсчет очков # загрузка модулей # чтение файла объявлений # get_ad_to_show() && show_ad()
  • 22. Время загрузки модулей use use use use use use use use use use use ! common::sense; $t->mark('loaded local::lib; $t->mark('loaded Const::Fast; $t->mark('loaded Date::Calc qw(Today_and_Now Add_Delta_DHMS); $t->mark('loaded Date::Calc'); Data::Dump qw(pp); $t->mark('loaded File::Temp (); $t->mark('loaded File::Copy qw(mv); $t->mark('loaded List::UtilsBy qw(max_by); $t->mark('loaded XML::Rules; $t->mark('loaded XML::Fast qw(xml2hash); $t->mark('loaded File::Map qw(map_file); $t->mark('loaded $t->mark('modules loaded'); exit; ! common::sense;'); local::lib;'); Const::Fast;'); Data::Dump'); File::Temp'); File::Copy'); List::UtilsBy'); XML::Rules;'); XML::Fast'); File::Map');
  • 23. Время загрузки модулей Devel::Timer Report -- Total time: 1.6661 secs Interval Time Percent ---------------------------------------------01 -> 02 1.6611 99.70% BEGIN -> loaded common::sense; 13 -> 14 0.0023 0.14% modules loaded -> END 00 -> 01 0.0004 0.02% INIT -> BEGIN 10 -> 11 0.0003 0.02% loaded XML::Rules; -> loaded XML::Fast 02 -> 03 0.0003 0.02% loaded common::sense; -> loaded local::lib; 12 -> 13 0.0002 0.01% loaded File::Map -> modules loaded 04 -> 05 0.0002 0.01% loaded Const::Fast; -> loaded Date::Calc 11 -> 12 0.0002 0.01% loaded XML::Fast -> loaded File::Map 07 -> 08 0.0002 0.01% loaded File::Temp -> loaded File::Copy 06 -> 07 0.0002 0.01% loaded Data::Dump -> loaded File::Temp 05 -> 06 0.0002 0.01% loaded Date::Calc -> loaded Data::Dump 08 -> 09 0.0002 0.01% loaded File::Copy -> loaded List::UtilsBy 09 -> 10 0.0002 0.01% loaded List::UtilsBy -> loaded XML::Rules; 03 -> 04 0.0002 0.01% loaded local::lib; -> loaded Const::Fast;
  • 25. Время загрузки модулей Devel::Timer Report -- Total time: 1.6027 secs Interval Time Percent ---------------------------------------------09 -> 10 0.3867 24.13% loaded List::UtilsBy -> loaded XML::Rules; 06 -> 07 0.3401 21.22% loaded Data::Dump -> loaded File::Temp 02 -> 03 0.3279 20.46% loaded common::sense; -> loaded local::lib; 10 -> 11 0.1328 8.28% loaded XML::Rules; -> loaded XML::Fast 04 -> 05 0.1080 6.74% loaded Const::Fast; -> loaded Date::Calc 05 -> 06 0.0851 5.31% loaded Date::Calc -> loaded Data::Dump 11 -> 12 0.0772 4.81% loaded XML::Fast -> loaded File::Map 03 -> 04 0.0539 3.36% loaded local::lib; -> loaded Const::Fast; 07 -> 08 0.0521 3.25% loaded File::Temp -> loaded File::Copy 08 -> 09 0.0265 1.65% loaded File::Copy -> loaded List::UtilsBy 01 -> 02 0.0095 0.59% BEGIN -> loaded common::sense; 13 -> 14 0.0024 0.15% modules loaded -> END 00 -> 01 0.0004 0.02% INIT -> BEGIN 12 -> 13 0.0003 0.02% loaded File::Map -> modules loaded
  • 26. Выкидываем лишние модули • XML::Rules -> XML::Fast (асинхронность не нужна) • File::Temp, File::Copy, File::Map (работаем с файлами через стандартные функции Perl) • local::lib (настраиваем пути в .bashrc) • Const::Fast • Data::Dump - загружаем по требованию
  • 27. Многократно повторяющиеся элементы $profile->{likes} = [map { lc $_->[1]->{_content} } @{ $profile->{likes} }]; ... + my $profiles = xml2hash(lc slurp($profile_file))->{xml}->{profiles}>{profile}; ! ! for my $ad (@{ $playlist->{Content} }) { $ad->{likes} = [split /;/, lc $ad->{likes}] ... + my $playlist = xml2hash(lc slurp($media_file), attr => '')->{xml}>{playlist}; • Проблема: изменен регистр символов на выходе - но это не критично
  • 28. Результат time ./parser.pl samples/profile.xml samples/media.xml ! Devel::Timer Report -- Total time: 1.8889 secs Count Time Percent ---------------------------------------------1 1.3133 69.53% calculating scores: start -> calculating scores: end 1 0.4887 25.87% program: start -> modules loaded 1 0.0329 1.74% read profile file: start -> read profile file: end 1 0.0157 0.83% searching for ad to show: start -> searching for ad to show: end 1 0.0143 0.76% write profile file: start -> write profile file: end 1 0.0126 0.67% read media file: start -> read media file: end 1 0.0046 0.24% convert media file: start -> convert media file: end 1 0.0027 0.14% searching for ad to show: end -> program: end 1 0.0023 0.12% write profile file: end -> searching for ad to show: start ! real 0m2.142s user 0m2.040s sys 0m0.070s
  • 31. Пересмотрим результаты time ./parser.pl samples/profile.xml samples/media.xml ! Devel::Timer Report -- Total time: 1.8889 secs Count Time Percent ---------------------------------------------1 1.3133 69.53% calculating scores: start -> calculating scores: end! 1 0.4887 25.87% program: start -> modules loaded! 1 0.0329 1.74% read profile file: start -> read profile file: end 1 0.0157 0.83% searching for ad to show: start -> searching for ad to show: end 1 0.0143 0.76% write profile file: start -> write profile file: end ... ! real 0m2.142s user 0m2.040s sys 0m0.070s • Не хватает CPU!
  • 32. Сложность алгоритма ! O(MxN) M - количество объявлений N - количество пользователей
  • 33. Как уменьшить? ! (в идеале - привести к O(N))
  • 34. Объявления • Основные параметры • Возраст клиента • Пол клиента • Предпочтения клиента (их меньше, чем объявлений) • Количество значений ограничено • Значения известны после загрузки объявлений • Почему бы не проиндексировать?
  • 35. my %age; # Ads valid for given age my %gender; # Ads scored for given gender my %like; # Ads scored for given preference for my $ad (@{ $playlist->{content} }) { # Fill ages my $age_min = int($ad->{agemin}) || 0; my $age_max = int($ad->{agemax}) || 100; for my $current_age ($age_min .. $age_max) { push @{ $age{$current_age} }, $ad->{id}; } # Fill genders given ($ad->{gender}) { when ('male') { push @{ $gender{male} }, $ad->{id}; } when ('female') { push @{ $gender{female} }, $ad->{id}; } when ('all') { push @{ $gender{male} }, $ad->{id}; push @{ $gender{female} }, $ad->{id}; } } # Fill likes for my $current_like (split /;/, $ad->{likes}) { push @{ $like{$current_like} }, $ad->{id}; } }
  • 36. Подсчет очков • Параметры профиля - те же, что и в объявлениях • Сразу получаем нужные объявления для каждого значения параметра и добавляем им очки
  • 37. ! # Score of each ad my %score; sub calc_score { my ($profile) = @_; # Gender if (exists $profile->{gender}) { for my $ad_id (@{ $gender{$profile->{gender}} || [] }) { $score{$ad_id}++; } } # Age if (exists $profile->{age}) { for my $ad_id (@{ $age{$profile->{age}} }) { $score{$ad_id}++; } } # Likes if (ref $profile->{likes} && ref $profile->{likes}->{like}) { for my $profile_like (@{ $profile->{likes}->{like} }) { for my $ad_id (@{ $like{ $profile_like } || [] }) { $score{$ad_id}++; } } } }
  • 38. Результат time ./parser.pl samples/profile.xml samples/media.xml ! Devel::Timer Report -- Total time: 0.8667 secs Count Time Percent ---------------------------------------------1 0.5066 58.45% program: start -> modules loaded 1 0.1656 19.11% calculating scores: start -> calculating scores: end 1 0.1177 13.58% index media file: start -> index media file: end 1 0.0339 3.91% read profile file: start -> read profile file: end 1 0.0152 1.75% write profile file: start -> write profile file: end 1 0.0130 1.50% read media file: start -> read media file: end ! real 0m1.119s user 0m1.070s sys 0m0.020s
  • 39. И еще кое-что • Raspberry PI поддерживает оверклокинг • системными инструментами (raspi-config) • безопасно (типа :)) • 700 -> 800 -> 900 -> 950 -> 1000 MHz
  • 40. После оверклокинга time ./parser.pl samples/profile.xml samples/media.xml ! Devel::Timer Report -- Total time: 0.5798 secs Count Time Percent ---------------------------------------------1 0.3390 58.48% program: start -> modules loaded 1 0.1135 19.58% calculating scores: start -> calculating scores: end 1 0.0754 13.00% index media file: start -> index media file: end 1 0.0216 3.72% read profile file: start -> read profile file: end 1 0.0098 1.69% write profile file: start -> write profile file: end 1 0.0082 1.42% read media file: start -> read media file: end ! real 0m0.814s user 0m0.740s sys 0m0.040s
  • 41. Выводы • Измеряй все, что можешь - никому нельзя доверять безоговорочно. • Выдели важное. Выбрось ненужное. • Оптимизируй алгоритм: всегда есть другой путь. • Выйди из зоны комфорта. • Думай и побеждай.