← Index
NYTProf Performance Profile   « block view • line view • sub view »
For bin/hailo
  Run on Thu Oct 21 22:50:37 2010
Reported on Thu Oct 21 22:52:10 2010

Filename/mnt/stuff/src/my-cpan/hailo/lib/Hailo/Engine/Default.pm
StatementsExecuted 5729027 statements in 38.4s
Subroutines
Calls P F Exclusive
Time
Inclusive
Time
Subroutine
25000118.19s38.4sHailo::Engine::Default::::learnHailo::Engine::Default::learn
262148415.21s14.5sHailo::Engine::Default::::_inc_linkHailo::Engine::Default::_inc_link
142561111.91s4.18sHailo::Engine::Default::::_token_idHailo::Engine::Default::_token_id
131074111.55s3.36sHailo::Engine::Default::::_expr_idHailo::Engine::Default::_expr_id
142561111.37s6.33sHailo::Engine::Default::::_token_id_addHailo::Engine::Default::_token_id_add
77849111.24s3.70sHailo::Engine::Default::::_add_exprHailo::Engine::Default::_add_expr
22967341415ms415msHailo::Engine::Default::::storageHailo::Engine::Default::storage (xsub)
2074911335ms782msHailo::Engine::Default::::_add_tokenHailo::Engine::Default::_add_token
250001139.4ms39.4msHailo::Engine::Default::::orderHailo::Engine::Default::order (xsub)
111494µs1.23msHailo::Engine::Default::::BEGIN@6Hailo::Engine::Default::BEGIN@6
11182µs85µsHailo::Engine::Default::::BUILDHailo::Engine::Default::BUILD
11174µs158µsHailo::Engine::Default::::BEGIN@3Hailo::Engine::Default::BEGIN@3
11116µs82µsHailo::Engine::Default::::BEGIN@5Hailo::Engine::Default::BEGIN@5
11114µs85µsHailo::Engine::Default::::BEGIN@3.16Hailo::Engine::Default::BEGIN@3.16
11114µs817µsHailo::Engine::Default::::BEGIN@4Hailo::Engine::Default::BEGIN@4
0000s0sHailo::Engine::Default::::__ANON__[lib/Hailo/Engine/Default.pm:18]Hailo::Engine::Default::__ANON__[lib/Hailo/Engine/Default.pm:18]
0000s0sHailo::Engine::Default::::__ANON__[lib/Hailo/Engine/Default.pm:70]Hailo::Engine::Default::__ANON__[lib/Hailo/Engine/Default.pm:70]
0000s0sHailo::Engine::Default::::_construct_replyHailo::Engine::Default::_construct_reply
0000s0sHailo::Engine::Default::::_find_rare_tokensHailo::Engine::Default::_find_rare_tokens
0000s0sHailo::Engine::Default::::_pos_tokenHailo::Engine::Default::_pos_token
0000s0sHailo::Engine::Default::::_random_exprHailo::Engine::Default::_random_expr
0000s0sHailo::Engine::Default::::_token_infoHailo::Engine::Default::_token_info
0000s0sHailo::Engine::Default::::_token_similarHailo::Engine::Default::_token_similar
0000s0sHailo::Engine::Default::::replyHailo::Engine::Default::reply
Call graph for these subroutines as a Graphviz dot language file.
Line State
ments
Time
on line
Calls Time
in subs
Code
1package Hailo::Engine::Default;
2
3489µs3314µs
# spent 85µs (14+70) within Hailo::Engine::Default::BEGIN@3.16 which was called: # once (14µs+70µs) by Hailo::Engine::Default::BEGIN@3 at line 3 # spent 158µs (74+85) within Hailo::Engine::Default::BEGIN@3 which was called: # once (74µs+85µs) by Hailo::_new_class at line 3
use 5.010;
# spent 158µs making 1 call to Hailo::Engine::Default::BEGIN@3 # spent 85µs making 1 call to Hailo::Engine::Default::BEGIN@3.16 # spent 70µs making 1 call to feature::import
4235µs21.62ms
# spent 817µs (14+803) within Hailo::Engine::Default::BEGIN@4 which was called: # once (14µs+803µs) by Hailo::_new_class at line 4
use Any::Moose;
# spent 817µs making 1 call to Hailo::Engine::Default::BEGIN@4 # spent 803µs making 1 call to Any::Moose::import
5234µs2148µs
# spent 82µs (16+66) within Hailo::Engine::Default::BEGIN@5 which was called: # once (16µs+66µs) by Hailo::_new_class at line 5
use List::Util qw<min first shuffle>;
# spent 82µs making 1 call to Hailo::Engine::Default::BEGIN@5 # spent 66µs making 1 call to Exporter::import
621.71ms21.41ms
# spent 1.23ms (494µs+740µs) within Hailo::Engine::Default::BEGIN@6 which was called: # once (494µs+740µs) by Hailo::_new_class at line 6
use List::MoreUtils qw<uniq>;
# spent 1.23ms making 1 call to Hailo::Engine::Default::BEGIN@6 # spent 175µs making 1 call to Exporter::import
7
815µs110.0mswith qw[ Hailo::Role::Arguments Hailo::Role::Engine ];
# spent 10.0ms making 1 call to Mouse::with
9
10has repeat_limit => (
11 isa => 'Int',
12 is => 'rw',
13 lazy => 1,
14 default => sub {
15 my ($self) = @_;
16 my $order = $self->order;
17 return min(($order * 10), 50);
18 }
1917µs1276µs);
# spent 276µs making 1 call to Mouse::has
20
21
# spent 85µs (82+3) within Hailo::Engine::Default::BUILD which was called: # once (82µs+3µs) by Mouse::Object::new at line 201 of lib/Hailo.pm
sub BUILD {
22486µs my ($self) = @_;
23
24 # This performance hack is here because in our tight loops calling
25 # $self->storage->sth->{...} is actually a significant part of the
26 # overall program execution time since we're doing two method
27 # calls and hash dereferences for each call to the database.
28
2923µs my $sth = $self->storage->sth;
# spent 2µs making 1 call to Hailo::Storage::sth # spent 1µs making 1 call to Hailo::Engine::Default::storage
30 while (my ($k, $v) = each %$sth) {
31 $self->{"_sth_$k"} = $v;
32 }
33
34 return;
35}
36
37## no critic (Subroutines::ProhibitExcessComplexity)
38sub reply {
39 my $self = shift;
40 my $tokens = shift // [];
41
42 # we will favor these tokens when making the reply> shuffle them
43 # and discard half.
44 my @key_tokens = do {
45 my $i = 0;
46 grep { $i++ % 2 == 0 } shuffle(@$tokens);
47 };
48
49 my (@key_ids, %token_cache);
50 for my $token_info (@key_tokens) {
51 my $text = $token_info->[1];
52 my $info = $self->_token_similar($text);
53 next unless defined $info;
54 my ($id, $spacing) = @$info;
55 next unless defined $id;
56 push @key_ids, $id;
57 next if exists $token_cache{$id};
58 $token_cache{$id} = [$spacing, $text];
59 }
60
61 # sort the rest by rareness
62 @key_ids = $self->_find_rare_tokens(\@key_ids, 2);
63
64 # get the middle expression
65 my $seed_token_id = shift @key_ids;
66 my ($orig_expr_id, @token_ids) = $self->_random_expr($seed_token_id);
67 return unless defined $orig_expr_id; # we don't know any expressions yet
68
69 # remove key tokens we're already using
70 @key_ids = grep { my $used = $_; !first { $_ == $used } @token_ids } @key_ids;
71
72 my $expr_id = $orig_expr_id;
73
74 # construct the end of the reply
75 $self->_construct_reply('next', $expr_id, \@token_ids, \@key_ids);
76
77 # construct the beginning of the reply
78 $self->_construct_reply('prev', $expr_id, \@token_ids, \@key_ids);
79
80 # translate token ids to token spacing/text
81 my @reply = map {
82 $token_cache{$_} // ($token_cache{$_} = $self->_token_info($_))
83 } @token_ids;
84 return \@reply;
85}
86
87sub _token_info {
88 my ($self, $id) = @_;
89
90 $self->{_sth_token_info}->execute($id);
91 my @res = $self->{_sth_token_info}->fetchrow_array;
92 return \@res;
93}
94
95
# spent 38.4s (8.19+30.2) within Hailo::Engine::Default::learn which was called 25000 times, avg 1.54ms/call: # 25000 times (8.19s+30.2s) by Hailo::_learn_one at line 289 of lib/Hailo.pm, avg 1.54ms/call
sub learn {
96157592449ms my ($self, $tokens) = @_;
972500039.4ms my $order = $self->order;
# spent 39.4ms making 25000 calls to Hailo::Engine::Default::order, avg 2µs/call
98
99 # only learn from inputs which are long enough
100 return if @$tokens < $order;
101
102 my %token_cache;
103
104 for my $token (@$tokens) {
1054460051.31s my $key = join '', @$token;
106 next if exists $token_cache{$key};
1071425616.33s $token_cache{$key} = $self->_token_id_add($token);
# spent 6.33s making 142561 calls to Hailo::Engine::Default::_token_id_add, avg 44µs/call
108 }
109
110 # process every expression of length $order
111 for my $i (0 .. @$tokens - $order) {
11210485923.76s my @expr = map { $token_cache{ join('', @{ $tokens->[$_] }) } } $i .. $i+$order-1;
1131310743.36s my $expr_id = $self->_expr_id(\@expr);
# spent 3.36s making 131074 calls to Hailo::Engine::Default::_expr_id, avg 26µs/call
114
1151556983.35s if (!defined $expr_id) {
116778493.70s $expr_id = $self->_add_expr(\@expr);
# spent 3.70s making 77849 calls to Hailo::Engine::Default::_add_expr, avg 48µs/call
1172334831.87s $self->{_sth_inc_token_count}->execute($_) for uniq(@expr);
# spent 1.54s making 155634 calls to DBI::st::execute, avg 10µs/call # spent 333ms making 77849 calls to List::MoreUtils::uniq, avg 4µs/call
118 }
119
120 # add link to next token for this expression, if any
121220852678ms if ($i < @$tokens - $order) {
122 my $next_id = $token_cache{ join('', @{ $tokens->[$i+$order] }) };
1231104266.62s $self->_inc_link('next_token', $expr_id, $next_id);
# spent 6.62s making 110426 calls to Hailo::Engine::Default::_inc_link, avg 60µs/call
124 }
125
126 # add link to previous token for this expression, if any
127220852695ms if ($i > 0) {
128 my $prev_id = $token_cache{ join('', @{ $tokens->[$i-1] }) };
1291104265.72s $self->_inc_link('prev_token', $expr_id, $prev_id);
# spent 5.72s making 110426 calls to Hailo::Engine::Default::_inc_link, avg 52µs/call
130 }
131
132 # add links to boundary token if appropriate
133262148440ms my $b = $self->storage->_boundary_token_id;
# spent 239ms making 131074 calls to Hailo::Engine::Default::storage, avg 2µs/call # spent 200ms making 131074 calls to Hailo::Storage::_boundary_token_id, avg 2µs/call
134206481.13s $self->_inc_link('prev_token', $expr_id, $b) if $i == 0;
# spent 1.13s making 20648 calls to Hailo::Engine::Default::_inc_link, avg 55µs/call
13520648989ms $self->_inc_link('next_token', $expr_id, $b) if $i == @$tokens-$order;
# spent 989ms making 20648 calls to Hailo::Engine::Default::_inc_link, avg 48µs/call
136 }
137
138 return;
139}
140
141# sort token ids based on how rare they are
142sub _find_rare_tokens {
143 my ($self, $token_ids, $min) = @_;
144 return unless @$token_ids;
145
146 my %links;
147 for my $id (@$token_ids) {
148 next if exists $links{$id};
149 $self->{_sth_token_count}->execute($id);
150 $links{$id} = $self->{_sth_token_count}->fetchrow_array;
151 }
152
153 # remove tokens which are too rare
154 my @ids = grep { $links{$_} >= $min } @$token_ids;
155
156 @ids = sort { $links{$a} <=> $links{$b} } @ids;
157
158 return @ids;
159}
160
161# increase the link weight between an expression and a token
162
# spent 14.5s (5.21+9.24) within Hailo::Engine::Default::_inc_link which was called 262148 times, avg 55µs/call: # 110426 times (2.21s+4.41s) by Hailo::Engine::Default::learn at line 123, avg 60µs/call # 110426 times (2.20s+3.52s) by Hailo::Engine::Default::learn at line 129, avg 52µs/call # 20648 times (405ms+727ms) by Hailo::Engine::Default::learn at line 134, avg 55µs/call # 20648 times (403ms+586ms) by Hailo::Engine::Default::learn at line 135, avg 48µs/call
sub _inc_link {
163131074010.5s my ($self, $type, $expr_id, $token_id) = @_;
164
1652621483.93s $self->{"_sth_${type}_count"}->execute($expr_id, $token_id);
# spent 3.93s making 262148 calls to DBI::st::execute, avg 15µs/call
1662621481.22s my $count = $self->{"_sth_${type}_count"}->fetchrow_array;
# spent 1.22s making 262148 calls to DBI::st::fetchrow_array, avg 5µs/call
167
1682173634.08s44785988ms if (defined $count) {
# spent 988ms making 44785 calls to DBI::st::execute, avg 22µs/call
169 $self->{"_sth_${type}_inc"}->execute($expr_id, $token_id);
170 }
171 else {
1722173633.10s $self->{"_sth_${type}_add"}->execute($expr_id, $token_id);
# spent 3.10s making 217363 calls to DBI::st::execute, avg 14µs/call
173 }
174
175 return;
176}
177
178# add new expression to the database
179
# spent 3.70s (1.24+2.46) within Hailo::Engine::Default::_add_expr which was called 77849 times, avg 48µs/call: # 77849 times (1.24s+2.46s) by Hailo::Engine::Default::learn at line 116, avg 48µs/call
sub _add_expr {
1802335473.74s my ($self, $token_ids) = @_;
181
182 # add the expression
183778492.03s $self->{_sth_add_expr}->execute(@$token_ids);
# spent 2.03s making 77849 calls to DBI::st::execute, avg 26µs/call
184233547429ms return $self->storage->dbh->last_insert_id(undef, undef, "expr", undef);
# spent 185ms making 77849 calls to DBI::db::last_insert_id, avg 2µs/call # spent 138ms making 77849 calls to Hailo::Engine::Default::storage, avg 2µs/call # spent 105ms making 77849 calls to Hailo::Storage::dbh, avg 1µs/call
185}
186
187# look up an expression id based on tokens
188
# spent 3.36s (1.55+1.81) within Hailo::Engine::Default::_expr_id which was called 131074 times, avg 26µs/call: # 131074 times (1.55s+1.81s) by Hailo::Engine::Default::learn at line 113, avg 26µs/call
sub _expr_id {
1893932223.42s my ($self, $tokens) = @_;
1901310741.37s $self->{_sth_expr_id}->execute(@$tokens);
# spent 1.37s making 131074 calls to DBI::st::execute, avg 10µs/call
191131074443ms return $self->{_sth_expr_id}->fetchrow_array();
# spent 443ms making 131074 calls to DBI::st::fetchrow_array, avg 3µs/call
192}
193
194# return token id if the token exists
195
# spent 4.18s (1.91+2.27) within Hailo::Engine::Default::_token_id which was called 142561 times, avg 29µs/call: # 142561 times (1.91s+2.27s) by Hailo::Engine::Default::_token_id_add at line 209, avg 29µs/call
sub _token_id {
1966920564.29s my ($self, $token_info) = @_;
197
1981425611.62s $self->{_sth_token_id}->execute(@$token_info);
# spent 1.62s making 142561 calls to DBI::st::execute, avg 11µs/call
199142561646ms my $token_id = $self->{_sth_token_id}->fetchrow_array();
# spent 646ms making 142561 calls to DBI::st::fetchrow_array, avg 5µs/call
200
201 return unless defined $token_id;
202 return $token_id;
203}
204
205# get token id (adding the token if it doesn't exist)
206
# spent 6.33s (1.37+4.96) within Hailo::Engine::Default::_token_id_add which was called 142561 times, avg 44µs/call: # 142561 times (1.37s+4.96s) by Hailo::Engine::Default::learn at line 107, avg 44µs/call
sub _token_id_add {
2075702441.31s my ($self, $token_info) = @_;
208
2091425614.18s my $token_id = $self->_token_id($token_info);
# spent 4.18s making 142561 calls to Hailo::Engine::Default::_token_id, avg 29µs/call
21020749782ms $token_id = $self->_add_token($token_info) unless defined $token_id;
# spent 782ms making 20749 calls to Hailo::Engine::Default::_add_token, avg 38µs/call
211 return $token_id;
212}
213
214# return all tokens (regardless of spacing) that consist of this text
215sub _token_similar {
216 my ($self, $token_text) = @_;
217 $self->{_sth_token_similar}->execute($token_text);
218 return $self->{_sth_token_similar}->fetchrow_arrayref;
219}
220
221# add a new token and return its id
222
# spent 782ms (335+447) within Hailo::Engine::Default::_add_token which was called 20749 times, avg 38µs/call: # 20749 times (335ms+447ms) by Hailo::Engine::Default::_token_id_add at line 210, avg 38µs/call
sub _add_token {
22362247795ms my ($self, $token_info) = @_;
22420749332ms $self->{_sth_add_token}->execute(@$token_info);
# spent 332ms making 20749 calls to DBI::st::execute, avg 16µs/call
22562247115ms return $self->storage->dbh->last_insert_id(undef, undef, "token", undef);
# spent 50.1ms making 20749 calls to DBI::db::last_insert_id, avg 2µs/call # spent 37.0ms making 20749 calls to Hailo::Engine::Default::storage, avg 2µs/call # spent 27.9ms making 20749 calls to Hailo::Storage::dbh, avg 1µs/call
226}
227
228# return a random expression containing the given token
229sub _random_expr {
230 my ($self, $token_id) = @_;
231
232 my $expr;
233
234 if (!defined $token_id) {
235 $self->{_sth_random_expr}->execute();
236 $expr = $self->{_sth_random_expr}->fetchrow_arrayref();
237 }
238 else {
239 # try the positions in a random order
240 for my $pos (shuffle 0 .. $self->order-1) {
241 my $column = "token${pos}_id";
242
243 # get a random expression which includes the token at this position
244 $self->{"_sth_expr_by_$column"}->execute($token_id);
245 $expr = $self->{"_sth_expr_by_$column"}->fetchrow_arrayref();
246 last if defined $expr;
247 }
248 }
249
250 return unless defined $expr;
251 return @$expr;
252}
253
254# return a new next/previous token
255sub _pos_token {
256 my ($self, $pos, $expr_id, $key_tokens) = @_;
257
258 $self->{"_sth_${pos}_token_get"}->execute($expr_id);
259 my $pos_tokens = $self->{"_sth_${pos}_token_get"}->fetchall_arrayref();
260
261 if (defined $key_tokens) {
262 for my $i (0 .. $#{ $key_tokens }) {
263 my $want_id = $key_tokens->[$i];
264 my @ids = map { $_->[0] } @$pos_tokens;
265 my $has_id = grep { $_ == $want_id } @ids;
266 next unless $has_id;
267 return splice @$key_tokens, $i, 1;
268 }
269 }
270
271 my @novel_tokens;
272 for my $token (@$pos_tokens) {
273 push @novel_tokens, ($token->[0]) x $token->[1];
274 }
275 return $novel_tokens[rand @novel_tokens];
276}
277
278sub _construct_reply {
279 my ($self, $what, $expr_id, $token_ids, $key_ids) = @_;
280 my $order = $self->order;
281 my $repeat_limit = $self->repeat_limit;
282 my $boundary_token = $self->storage->_boundary_token_id;
283
284 my $i = 0;
285 while (1) {
286 if (($i % $order) == 0 and
287 (($i >= $repeat_limit * 3) ||
288 ($i >= $repeat_limit and uniq(@$token_ids) <= $order))) {
289 last;
290 }
291
292 my $id = $self->_pos_token($what, $expr_id, $key_ids);
293 last if $id eq $boundary_token;
294
295 given ($what) {
296 when ('next') {
297 push @$token_ids, $id;
298 $expr_id = $self->_expr_id([@$token_ids[-$order..-1]]);
299 }
300 when ('prev') {
301 unshift @$token_ids, $id;
302 $expr_id = $self->_expr_id([@$token_ids[0..$order-1]]);
303 }
304 }
305 } continue {
306 $i++;
307 }
308
309 return;
310}
311
312115µs2133µs__PACKAGE__->meta->make_immutable;
# spent 119µs making 1 call to Mouse::Meta::Class::make_immutable # spent 14µs making 1 call to Hailo::Engine::Default::meta
313
314=encoding utf8
315
316=head1 NAME
317
318Hailo::Engine::Default - The default engine backend for L<Hailo|Hailo>
319
320=head1 DESCRIPTION
321
322This backend implements the logic of replying to and learning from
323input using the resources given to the L<engine
324roles|Hailo::Role::Engine>.
325
326=head1 AUTHORS
327
328Hinrik E<Ouml>rn SigurE<eth>sson, hinrik.sig@gmail.com
329
330E<AElig>var ArnfjE<ouml>rE<eth> Bjarmason <avar@cpan.org>
331
332=head1 LICENSE AND COPYRIGHT
333
334Copyright 2010 Hinrik E<Ouml>rn SigurE<eth>sson and
335E<AElig>var ArnfjE<ouml>rE<eth> Bjarmason <avar@cpan.org>
336
337This program is free software, you can redistribute it and/or modify
338it under the same terms as Perl itself.
339
340=cut
 
# spent 39.4ms within Hailo::Engine::Default::order which was called 25000 times, avg 2µs/call: # 25000 times (39.4ms+0s) by Hailo::Engine::Default::learn at line 97, avg 2µs/call
sub Hailo::Engine::Default::order; # xsub
# spent 415ms within Hailo::Engine::Default::storage which was called 229673 times, avg 2µs/call: # 131074 times (239ms+0s) by Hailo::Engine::Default::learn at line 133, avg 2µs/call # 77849 times (138ms+0s) by Hailo::Engine::Default::_add_expr at line 184, avg 2µs/call # 20749 times (37.0ms+0s) by Hailo::Engine::Default::_add_token at line 225, avg 2µs/call # once (1µs+0s) by Hailo::Engine::Default::BUILD at line 29
sub Hailo::Engine::Default::storage; # xsub