Filename | /mnt/stuff/src/my-cpan/hailo/lib/Hailo.pm |
Statements | Executed 125145 statements in 599ms |
Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
---|---|---|---|---|---|
25000 | 1 | 1 | 542ms | 50.4s | _learn_one | Hailo::
25000 | 1 | 1 | 39.7ms | 54.7ms | _engine (xsub) | Hailo::
25000 | 1 | 1 | 36.0ms | 82.6ms | _tokenizer (xsub) | Hailo::
3 | 1 | 1 | 4.29ms | 108ms | _new_class | Hailo::
3 | 3 | 1 | 756µs | 6.71ms | _is_interactive | Hailo::
1 | 1 | 1 | 418µs | 11.2ms | BEGIN@4 | Hailo::
1 | 1 | 1 | 287µs | 306µs | BEGIN@178 | Hailo::
3 | 3 | 2 | 164µs | 109ms | __ANON__[lib/Hailo.pm:170] | Hailo::
1 | 1 | 1 | 87µs | 51.9s | train | Hailo::
1 | 1 | 1 | 66µs | 149µs | BEGIN@3 | Hailo::
1 | 1 | 1 | 57µs | 104µs | CORE:open (opcode) | Hailo::
13 | 1 | 1 | 53µs | 53µs | CORE:regcomp (opcode) | Hailo::
6 | 6 | 2 | 38µs | 47.3ms | _storage (xsub) | Hailo::
17 | 3 | 2 | 34µs | 34µs | CORE:match (opcode) | Hailo::
1 | 1 | 1 | 26µs | 103µs | DEMOLISH | Hailo::
3 | 1 | 1 | 21µs | 21µs | CORE:sort (opcode) | Hailo::
1 | 1 | 1 | 18µs | 71µs | save | Hailo::
1 | 1 | 1 | 15µs | 39µs | BEGIN@123 | Hailo::
1 | 1 | 1 | 14µs | 84µs | BEGIN@11 | Hailo::
4 | 1 | 1 | 13µs | 13µs | __ANON__[lib/Hailo.pm:111] | Hailo::
1 | 1 | 1 | 13µs | 714µs | BEGIN@5 | Hailo::
1 | 1 | 1 | 13µs | 84µs | BEGIN@3.8 | Hailo::
1 | 1 | 1 | 12µs | 1.47ms | BEGIN@9 | Hailo::
1 | 1 | 1 | 12µs | 63µs | BEGIN@7 | Hailo::
1 | 1 | 1 | 12µs | 53µs | BEGIN@8 | Hailo::
1 | 1 | 1 | 11µs | 368µs | BEGIN@6.10 | Hailo::
3 | 1 | 1 | 5µs | 5µs | PLUGINS (xsub) | Hailo::
1 | 1 | 1 | 2µs | 2µs | engine_args (xsub) | Hailo::
1 | 1 | 1 | 2µs | 2µs | storage_args (xsub) | Hailo::
1 | 1 | 1 | 2µs | 2µs | tokenizer_args (xsub) | Hailo::
0 | 0 | 0 | 0s | 0s | __ANON__[lib/Hailo.pm:102] | Hailo::
0 | 0 | 0 | 0s | 0s | __ANON__[lib/Hailo.pm:145] | Hailo::
0 | 0 | 0 | 0s | 0s | __ANON__[lib/Hailo.pm:146] | Hailo::
0 | 0 | 0 | 0s | 0s | __ANON__[lib/Hailo.pm:151] | Hailo::
0 | 0 | 0 | 0s | 0s | __ANON__[lib/Hailo.pm:155] | Hailo::
0 | 0 | 0 | 0s | 0s | __ANON__[lib/Hailo.pm:182] | Hailo::
0 | 0 | 0 | 0s | 0s | __ANON__[lib/Hailo.pm:33] | Hailo::
0 | 0 | 0 | 0s | 0s | __ANON__[lib/Hailo.pm:65] | Hailo::
0 | 0 | 0 | 0s | 0s | _train_fh | Hailo::
0 | 0 | 0 | 0s | 0s | learn | Hailo::
0 | 0 | 0 | 0s | 0s | learn_reply | Hailo::
0 | 0 | 0 | 0s | 0s | reply | Hailo::
0 | 0 | 0 | 0s | 0s | stats | Hailo::
Line | State ments |
Time on line |
Calls | Time in subs |
Code |
---|---|---|---|---|---|
1 | package Hailo; | ||||
2 | |||||
3 | 4 | 84µs | 3 | 304µs | use 5.010; # spent 149µs making 1 call to Hailo::BEGIN@3
# spent 84µs making 1 call to Hailo::BEGIN@3.8
# spent 71µs making 1 call to feature::import |
4 | 2 | 178µs | 2 | 11.2ms | # spent 11.2ms (418µs+10.8) within Hailo::BEGIN@4 which was called:
# once (418µs+10.8ms) by Mouse::Util::_try_load_one_class at line 4 # spent 11.2ms making 1 call to Hailo::BEGIN@4
# spent 7µs making 1 call to autodie::import |
5 | 2 | 33µs | 2 | 1.42ms | # spent 714µs (13+701) within Hailo::BEGIN@5 which was called:
# once (13µs+701µs) by Mouse::Util::_try_load_one_class at line 5 # spent 714µs making 1 call to Hailo::BEGIN@5
# spent 701µs making 1 call to Any::Moose::import |
6 | 2 | 30µs | 2 | 725µs | # spent 368µs (11+357) within Hailo::BEGIN@6.10 which was called:
# once (11µs+357µs) by Mouse::Util::_try_load_one_class at line 6 # spent 368µs making 1 call to Hailo::BEGIN@6.10
# spent 357µs making 1 call to Any::Moose::import |
7 | 2 | 27µs | 2 | 115µs | # spent 63µs (12+52) within Hailo::BEGIN@7 which was called:
# once (12µs+52µs) by Mouse::Util::_try_load_one_class at line 7 # spent 63µs making 1 call to Hailo::BEGIN@7
# spent 51µs making 1 call to Exporter::import |
8 | 2 | 29µs | 2 | 95µs | # spent 53µs (12+42) within Hailo::BEGIN@8 which was called:
# once (12µs+42µs) by Mouse::Util::_try_load_one_class at line 8 # spent 53µs making 1 call to Hailo::BEGIN@8
# spent 42µs making 1 call to Exporter::import |
9 | 2 | 45µs | 2 | 2.93ms | # spent 1.47ms (12µs+1.46) within Hailo::BEGIN@9 which was called:
# once (12µs+1.46ms) by Mouse::Util::_try_load_one_class at line 9 # spent 1.47ms making 1 call to Hailo::BEGIN@9
# spent 1.46ms making 1 call to namespace::clean::import |
10 | |||||
11 | 1 | 70µs | # spent 84µs (14+70) within Hailo::BEGIN@11 which was called:
# once (14µs+70µs) by Mouse::Util::_try_load_one_class at line 19 # spent 70µs making 1 call to constant::import | ||
12 | Hailo::Engine::Default | ||||
13 | Hailo::Storage::MySQL | ||||
14 | Hailo::Storage::PostgreSQL | ||||
15 | Hailo::Storage::SQLite | ||||
16 | Hailo::Tokenizer::Chars | ||||
17 | Hailo::Tokenizer::Words | ||||
18 | Hailo::UI::ReadLine | ||||
19 | 2 | 346µs | 1 | 84µs | ] ]; # spent 84µs making 1 call to Hailo::BEGIN@11 |
20 | |||||
21 | 1 | 5µs | 1 | 351µs | has brain => ( # spent 351µs making 1 call to Mouse::has |
22 | isa => 'Str', | ||||
23 | is => 'rw', | ||||
24 | ); | ||||
25 | |||||
26 | has order => ( | ||||
27 | isa => 'Int', | ||||
28 | is => 'rw', | ||||
29 | default => 2, | ||||
30 | trigger => sub { | ||||
31 | my ($self, $order) = @_; | ||||
32 | $self->_custom_order(1); | ||||
33 | }, | ||||
34 | 1 | 7µs | 1 | 269µs | ); # spent 269µs making 1 call to Mouse::has |
35 | |||||
36 | 1 | 4µs | 1 | 263µs | has _custom_order => ( # spent 263µs making 1 call to Mouse::has |
37 | isa => 'Bool', | ||||
38 | is => 'rw', | ||||
39 | default => 0, | ||||
40 | init_arg => undef, | ||||
41 | documentation => "Here so we can differentiate between the default value of order being explictly set and being set by default", | ||||
42 | ); | ||||
43 | |||||
44 | 1 | 4µs | 1 | 262µs | has _custom_tokenizer_class => ( # spent 262µs making 1 call to Mouse::has |
45 | isa => 'Bool', | ||||
46 | is => 'rw', | ||||
47 | default => 0, | ||||
48 | init_arg => undef, | ||||
49 | documentation => "Here so we can differentiate between the default value of tokenizer_class being explictly set and being set by default", | ||||
50 | ); | ||||
51 | |||||
52 | 1 | 3µs | 1 | 272µs | has save_on_exit => ( # spent 272µs making 1 call to Mouse::has |
53 | isa => 'Bool', | ||||
54 | is => 'rw', | ||||
55 | default => 1, | ||||
56 | ); | ||||
57 | |||||
58 | has brain_resource => ( | ||||
59 | documentation => "Alias for `brain' for backwards compatibility", | ||||
60 | isa => 'Str', | ||||
61 | is => 'rw', | ||||
62 | trigger => sub { | ||||
63 | my ($self, $brain) = @_; | ||||
64 | $self->brain($brain); | ||||
65 | }, | ||||
66 | 1 | 6µs | 1 | 270µs | ); # spent 270µs making 1 call to Mouse::has |
67 | |||||
68 | 1 | 10µs | my %has = ( | ||
69 | engine => { | ||||
70 | name => 'Engine', | ||||
71 | default => 'Default', | ||||
72 | }, | ||||
73 | storage => { | ||||
74 | name => 'Storage', | ||||
75 | default => 'SQLite', | ||||
76 | }, | ||||
77 | tokenizer => { | ||||
78 | name => 'Tokenizer', | ||||
79 | default => 'Words', | ||||
80 | }, | ||||
81 | ui => { | ||||
82 | name => 'UI', | ||||
83 | default => 'ReadLine', | ||||
84 | }, | ||||
85 | ); | ||||
86 | |||||
87 | 1 | 13µs | for my $k (keys %has) { | ||
88 | 32 | 126µs | my $name = $has{$k}->{name}; | ||
89 | my $default = $has{$k}->{default}; | ||||
90 | my $method_class = "${k}_class"; | ||||
91 | my $method_args = "${k}_args"; | ||||
92 | |||||
93 | # working classes | ||||
94 | has "${k}_class" => ( | ||||
95 | isa => 'Str', | ||||
96 | is => "rw", | ||||
97 | default => $default, | ||||
98 | ($k ~~ 'tokenizer' | ||||
99 | ? (trigger => sub { | ||||
100 | my ($self, $class) = @_; | ||||
101 | $self->_custom_tokenizer_class(1); | ||||
102 | }) | ||||
103 | 4 | 1.09ms | : ()) # spent 1.09ms making 4 calls to Mouse::has, avg 273µs/call | ||
104 | ); | ||||
105 | |||||
106 | # Object arguments | ||||
107 | has "${k}_args" => ( | ||||
108 | documentation => "Arguments for the $name class", | ||||
109 | isa => 'HashRef', | ||||
110 | is => "ro", | ||||
111 | 4 | 15µs | # spent 13µs within Hailo::__ANON__[lib/Hailo.pm:111] which was called 4 times, avg 3µs/call:
# 4 times (13µs+0s) by Mouse::Object::new at line 78 of MouseX/Getopt/Basic.pm, avg 3µs/call | ||
112 | 4 | 1.08ms | ); # spent 1.08ms making 4 calls to Mouse::has, avg 271µs/call | ||
113 | |||||
114 | # Working objects | ||||
115 | 4 | 2.13ms | has "_${k}" => ( # spent 2.13ms making 4 calls to Mouse::has, avg 534µs/call | ||
116 | does => "Hailo::Role::$name", | ||||
117 | lazy_build => 1, | ||||
118 | is => 'ro', | ||||
119 | init_arg => undef, | ||||
120 | ); | ||||
121 | |||||
122 | # Generate the object itself | ||||
123 | 2 | 325µs | 2 | 64µs | # spent 39µs (15+24) within Hailo::BEGIN@123 which was called:
# once (15µs+24µs) by Mouse::Util::_try_load_one_class at line 123 # spent 39µs making 1 call to Hailo::BEGIN@123
# spent 24µs making 1 call to strict::unimport |
124 | # spent 109ms (164µs+108) within Hailo::__ANON__[lib/Hailo.pm:170] which was called 3 times, avg 36.2ms/call:
# once (78µs+47.1ms) by Hailo::_storage at line 221 of lib/Hailo/Command.pm
# once (31µs+46.5ms) by Hailo::_tokenizer at line 288
# once (55µs+14.8ms) by Hailo::_engine at line 286 | ||||
125 | 9 | 161µs | my ($self) = @_; | ||
126 | |||||
127 | my $obj = $self->_new_class( | ||||
128 | $name, | ||||
129 | $self->$method_class, | ||||
130 | { | ||||
131 | arguments => $self->$method_args, | ||||
132 | ($k ~~ [ qw< engine storage > ] | ||||
133 | ? (order => $self->order) | ||||
134 | : ()), | ||||
135 | ($k ~~ [ qw< engine > ] | ||||
136 | ? (storage => $self->_storage) | ||||
137 | : ()), | ||||
138 | (($k ~~ [ qw< storage > ] and defined $self->brain) | ||||
139 | ? ( | ||||
140 | 4 | 26µs | 15 | 108ms | hailo => do { # spent 108ms making 3 calls to Hailo::_new_class, avg 36.1ms/call
# spent 3µs making 2 calls to Hailo::Command::order, avg 2µs/call
# spent 3µs making 2 calls to Hailo::Command::brain, avg 2µs/call
# spent 3µs making 2 calls to Hailo::Command::tokenizer_class, avg 2µs/call
# spent 2µs making 1 call to Hailo::engine_args
# spent 2µs making 1 call to Hailo::Command::engine_class
# spent 2µs making 1 call to Hailo::_storage
# spent 2µs making 1 call to Hailo::storage_args
# spent 2µs making 1 call to Hailo::tokenizer_args
# spent 1µs making 1 call to Hailo::Command::storage_class |
141 | require Scalar::Util; | ||||
142 | 1 | 2µs | Scalar::Util::weaken(my $s = $self); # spent 2µs making 1 call to Scalar::Util::weaken | ||
143 | |||||
144 | my %callback = ( | ||||
145 | has_custom_order => sub { $s->_custom_order }, | ||||
146 | has_custom_tokenizer_class => sub { $s->_custom_tokenizer_class }, | ||||
147 | set_order => sub { | ||||
148 | my ($db_order) = @_; | ||||
149 | $s->order($db_order); | ||||
150 | $s->_engine->order($db_order); | ||||
151 | }, | ||||
152 | set_tokenizer_class => sub { | ||||
153 | my ($db_tokenizer_class) = @_; | ||||
154 | $s->tokenizer_class($db_tokenizer_class); | ||||
155 | }, | ||||
156 | ); | ||||
157 | |||||
158 | \%callback; | ||||
159 | }, | ||||
160 | brain => $self->brain | ||||
161 | ) | ||||
162 | : ()), | ||||
163 | (($k ~~ [ qw< storage > ] | ||||
164 | ? (tokenizer_class => $self->tokenizer_class) | ||||
165 | : ())) | ||||
166 | }, | ||||
167 | ); | ||||
168 | |||||
169 | return $obj; | ||||
170 | }; | ||||
171 | } | ||||
172 | |||||
173 | # spent 108ms (4.29+104) within Hailo::_new_class which was called 3 times, avg 36.1ms/call:
# 3 times (4.29ms+104ms) by Hailo::__ANON__[lib/Hailo.pm:170] at line 140, avg 36.1ms/call | ||||
174 | 18 | 99µs | my ($self, $type, $class, $args) = @_; | ||
175 | |||||
176 | my $pkg; | ||||
177 | 9 | 99µs | 3 | 6µs | if ($class =~ m[^\+(?<custom_plugin>.+)$]) { # spent 6µs making 3 calls to Hailo::CORE:match, avg 2µs/call |
178 | 2 | 1.19ms | 1 | 306µs | # spent 306µs (287+20) within Hailo::BEGIN@178 which was called:
# once (287µs+20µs) by Mouse::Util::_try_load_one_class at line 178 # spent 306µs making 1 call to Hailo::BEGIN@178 |
179 | } else { | ||||
180 | 3 | 5µs | my @plugins = @{ $self->PLUGINS }; # spent 5µs making 3 calls to Hailo::PLUGINS, avg 2µs/call | ||
181 | # Be fuzzy about includes, e.g. DBD::SQLite or SQLite or sqlite will go | ||||
182 | 13 | 176µs | 26 | 78µs | $pkg = first { / $type : .* : $class /ix } # spent 53µs making 13 calls to Hailo::CORE:regcomp, avg 4µs/call
# spent 25µs making 13 calls to Hailo::CORE:match, avg 2µs/call |
183 | 6 | 193µs | sort { length $a <=> length $b } # spent 172µs making 3 calls to List::Util::first, avg 57µs/call
# spent 21µs making 3 calls to Hailo::CORE:sort, avg 7µs/call | ||
184 | @plugins; | ||||
185 | |||||
186 | unless ($pkg) { | ||||
187 | local $" = ', '; | ||||
188 | my @p = grep { /$type/ } @plugins; | ||||
189 | die "Couldn't find a class name matching '$class' in plugins '@p'"; | ||||
190 | } | ||||
191 | } | ||||
192 | |||||
193 | 3 | 105µs | 3 | 15µs | if (Any::Moose::moose_is_preferred()) { # spent 15µs making 3 calls to Any::Moose::moose_is_preferred, avg 5µs/call |
194 | require Class::MOP; | ||||
195 | eval { Class::MOP::load_class($pkg) }; | ||||
196 | } else { | ||||
197 | eval qq[require $pkg]; # spent 100µs executing statements in string eval
# spent 80µs executing statements in string eval
# spent 69µs executing statements in string eval | ||||
198 | } | ||||
199 | die $@ if $@; | ||||
200 | |||||
201 | 1 | 204µs | 30 | 1.06ms | return $pkg->new(%$args); # spent 621µs making 3 calls to Mouse::Object::new, avg 207µs/call
# spent 292µs making 3 calls to Mouse::Meta::Class::_calculate_all_attributes, avg 97µs/call
# spent 85µs making 1 call to Hailo::Engine::Default::BUILD
# spent 25µs making 1 call to Hailo::Role::Tokenizer::BUILD
# spent 15µs making 10 calls to Mouse::Meta::TypeConstraint::_compiled_type_constraint, avg 2µs/call
# spent 7µs making 3 calls to Mouse::Meta::Class::strict_constructor, avg 2µs/call
# spent 5µs making 1 call to Hailo::Role::Tokenizer::__ANON__[lib/Hailo/Role/Tokenizer.pm:15]
# spent 5µs making 3 calls to Mouse::Meta::Class::is_immutable, avg 2µs/call
# spent 4µs making 3 calls to Mouse::Meta::Class::is_anon_class, avg 1µs/call
# spent 3µs making 2 calls to Mouse::Meta::Attribute::default, avg 2µs/call |
202 | } | ||||
203 | |||||
204 | # spent 71µs (18+53) within Hailo::save which was called:
# once (18µs+53µs) by Hailo::DEMOLISH at line 334 | ||||
205 | 3 | 17µs | my ($self, @args) = @_; | ||
206 | 2 | 53µs | $self->_storage->save(@args); # spent 51µs making 1 call to Hailo::Storage::SQLite::save
# spent 2µs making 1 call to Hailo::_storage | ||
207 | return; | ||||
208 | } | ||||
209 | |||||
210 | # spent 51.9s (87µs+51.9) within Hailo::train which was called:
# once (87µs+51.9s) by Hailo::Command::run at line 260 of lib/Hailo/Command.pm | ||||
211 | 5 | 34µs | my ($self, $input) = @_; | ||
212 | |||||
213 | 2 | 53.0ms | $self->_storage->start_training(); # spent 53.0ms making 1 call to Hailo::Storage::SQLite::start_training
# spent 1µs making 1 call to Hailo::_storage | ||
214 | |||||
215 | 3 | 5µs | given ($input) { | ||
216 | # With STDIN | ||||
217 | when (not ref and defined and $_ eq '-') { | ||||
218 | die "You must provide STDIN when training from '-'" if $self->_is_interactive(*STDIN); | ||||
219 | $self->_train_fh(*STDIN); | ||||
220 | } | ||||
221 | # With a filehandle | ||||
222 | when (ref eq 'GLOB') { | ||||
223 | $self->_train_fh($input); | ||||
224 | } | ||||
225 | # With a file | ||||
226 | 2 | 37µs | when (not ref) { | ||
227 | 1 | 29µs | open my $fh, '<:encoding(utf8)', $input; # spent 29µs making 1 call to Hailo::__ANON__[(eval 30)[Fatal.pm:1188]:47] | ||
228 | 1 | 51.8s | $self->_train_fh($fh, $input); # spent 51.8s making 1 call to Hailo::Command::_train_fh | ||
229 | } | ||||
230 | # With an Array | ||||
231 | when (ref eq 'ARRAY') { | ||||
232 | $self->_learn_one($_) for @$input; | ||||
233 | } | ||||
234 | # With something naughty | ||||
235 | default { | ||||
236 | die "Unknown input: $input"; | ||||
237 | } | ||||
238 | } | ||||
239 | |||||
240 | 2 | 4.47ms | $self->_storage->stop_training(); # spent 4.47ms making 1 call to Hailo::Storage::SQLite::stop_training
# spent 3µs making 1 call to Hailo::_storage | ||
241 | |||||
242 | return; | ||||
243 | } | ||||
244 | |||||
245 | sub _train_fh { | ||||
246 | my ($self, $fh, $filename) = @_; | ||||
247 | |||||
248 | while (my $line = <$fh>) { | ||||
249 | chomp $line; | ||||
250 | $self->_learn_one($line); | ||||
251 | } | ||||
252 | |||||
253 | return; | ||||
254 | } | ||||
255 | |||||
256 | sub learn { | ||||
257 | my ($self, $input) = @_; | ||||
258 | my $inputs; | ||||
259 | |||||
260 | given ($input) { | ||||
261 | when (not defined) { | ||||
262 | die "Cannot learn from undef input"; | ||||
263 | } | ||||
264 | when (not ref) { | ||||
265 | $inputs = [$input]; | ||||
266 | } | ||||
267 | # With an Array | ||||
268 | when (ref eq 'ARRAY') { | ||||
269 | $inputs = $input | ||||
270 | } | ||||
271 | default { | ||||
272 | die "Unknown input: $input"; | ||||
273 | } | ||||
274 | } | ||||
275 | |||||
276 | my $storage = $self->_storage; | ||||
277 | |||||
278 | $storage->start_learning(); | ||||
279 | $self->_learn_one($_) for @$inputs; | ||||
280 | $storage->stop_learning(); | ||||
281 | return; | ||||
282 | } | ||||
283 | |||||
284 | # spent 50.4s (542ms+49.9) within Hailo::_learn_one which was called 25000 times, avg 2.02ms/call:
# 25000 times (542ms+49.9s) by Hailo::Command::train_progress at line 334 of lib/Hailo/Command.pm, avg 2.02ms/call | ||||
285 | 125000 | 595ms | my ($self, $input) = @_; | ||
286 | 1 | 19µs | 25004 | 69.7ms | my $engine = $self->_engine; # spent 54.7ms making 25000 calls to Hailo::_engine, avg 2µs/call
# spent 14.9ms making 1 call to Hailo::__ANON__[lib/Hailo.pm:170]
# spent 119µs making 1 call to Mouse::Util::TypeConstraints::__ANON__[Mouse/Util/TypeConstraints.pm:217]
# spent 2µs making 1 call to Mouse::Meta::Attribute::builder
# spent 2µs making 1 call to Mouse::Meta::TypeConstraint::_compiled_type_constraint |
287 | |||||
288 | 1 | 22µs | 50004 | 11.5s | my $tokens = $self->_tokenizer->make_tokens($input); # spent 11.4s making 25000 calls to Hailo::Tokenizer::Words::make_tokens, avg 454µs/call
# spent 82.6ms making 25000 calls to Hailo::_tokenizer, avg 3µs/call
# spent 46.5ms making 1 call to Hailo::__ANON__[lib/Hailo.pm:170]
# spent 119µs making 1 call to Mouse::Util::TypeConstraints::__ANON__[Mouse/Util/TypeConstraints.pm:217]
# spent 2µs making 1 call to Mouse::Meta::TypeConstraint::_compiled_type_constraint
# spent 2µs making 1 call to Mouse::Meta::Attribute::builder |
289 | 25000 | 38.4s | $engine->learn($tokens); # spent 38.4s making 25000 calls to Hailo::Engine::Default::learn, avg 1.54ms/call | ||
290 | |||||
291 | return; | ||||
292 | } | ||||
293 | |||||
294 | sub learn_reply { | ||||
295 | my ($self, $input) = @_; | ||||
296 | $self->learn($input); | ||||
297 | return $self->reply($input); | ||||
298 | } | ||||
299 | |||||
300 | sub reply { | ||||
301 | my ($self, $input) = @_; | ||||
302 | |||||
303 | my $storage = $self->_storage; | ||||
304 | # start_training() hasn't been called so we can't guarentee that | ||||
305 | # the storage has been engaged at this point. This must be called | ||||
306 | # before ->_engine() is called anywhere to ensure that the | ||||
307 | # lazy-loading in the engine works. | ||||
308 | $storage->_engage() unless $storage->_engaged; | ||||
309 | |||||
310 | my $engine = $self->_engine; | ||||
311 | my $tokenizer = $self->_tokenizer; | ||||
312 | |||||
313 | my $reply; | ||||
314 | if (defined $input) { | ||||
315 | my $tokens = $tokenizer->make_tokens($input); | ||||
316 | $reply = $engine->reply($tokens); | ||||
317 | } | ||||
318 | else { | ||||
319 | $reply = $engine->reply(); | ||||
320 | } | ||||
321 | |||||
322 | return unless defined $reply; | ||||
323 | return $tokenizer->make_output($reply); | ||||
324 | } | ||||
325 | |||||
326 | sub stats { | ||||
327 | my ($self) = @_; | ||||
328 | |||||
329 | return $self->_storage->totals(); | ||||
330 | } | ||||
331 | |||||
332 | # spent 103µs (26+77) within Hailo::DEMOLISH which was called:
# once (26µs+77µs) by Mouse::Object::DESTROY at line 0 of bin/hailo | ||||
333 | 3 | 31µs | my ($self) = @_; | ||
334 | 3 | 77µs | $self->save() if blessed $self->{_storage} and $self->save_on_exit; # spent 71µs making 1 call to Hailo::save
# spent 4µs making 1 call to Scalar::Util::blessed
# spent 2µs making 1 call to Hailo::Command::save_on_exit | ||
335 | return; | ||||
336 | } | ||||
337 | |||||
338 | # spent 6.71ms (756µs+5.95) within Hailo::_is_interactive which was called 3 times, avg 2.24ms/call:
# once (738µs+5.84ms) by Hailo::Command::__ANON__[lib/Hailo/Command.pm:59] at line 58 of lib/Hailo/Command.pm
# once (9µs+59µs) by Hailo::Command::__ANON__[lib/Hailo/Command.pm:298] at line 293 of lib/Hailo/Command.pm
# once (10µs+57µs) by Hailo::Command::run at line 249 of lib/Hailo/Command.pm | ||||
339 | 6 | 197µs | require IO::Interactive; | ||
340 | 3 | 184µs | return IO::Interactive::is_interactive(); # spent 184µs making 3 calls to IO::Interactive::is_interactive, avg 61µs/call | ||
341 | } | ||||
342 | |||||
343 | 1 | 35µs | 2 | 158µs | __PACKAGE__->meta->make_immutable; # spent 142µs making 1 call to Mouse::Meta::Class::make_immutable
# spent 15µs making 1 call to Hailo::meta |
344 | |||||
345 | =encoding utf8 | ||||
346 | |||||
347 | =head1 NAME | ||||
348 | |||||
349 | Hailo - A pluggable Markov engine analogous to MegaHAL | ||||
350 | |||||
351 | =head1 SYNOPSIS | ||||
352 | |||||
353 | This is the synopsis for using Hailo as a module. See L<hailo> for | ||||
354 | command-line invocation. | ||||
355 | |||||
356 | # Hailo requires Perl 5.10 | ||||
357 | use 5.010; | ||||
358 | use Any::Moose; | ||||
359 | use Hailo; | ||||
360 | |||||
361 | # Construct a new in-memory Hailo using the SQLite backend. See | ||||
362 | # backend documentation for other options. | ||||
363 | my $hailo = Hailo->new; | ||||
364 | |||||
365 | # Various ways to learn | ||||
366 | my @train_this = ("I like big butts", "and I can not lie"); | ||||
367 | $hailo->learn(\@train_this); | ||||
368 | $hailo->learn($_) for @train_this; | ||||
369 | |||||
370 | # Heavy-duty training interface. Backends may drop some safety | ||||
371 | # features like journals or synchronous IO to train faster using | ||||
372 | # this mode. | ||||
373 | $hailo->train("megahal.trn"); | ||||
374 | $hailo->train($filehandle); | ||||
375 | |||||
376 | # Make the brain babble | ||||
377 | say $hailo->reply("hello good sir."); | ||||
378 | # Just say something at random | ||||
379 | say $hailo->reply(); | ||||
380 | |||||
381 | =head1 DESCRIPTION | ||||
382 | |||||
383 | Hailo is a fast and lightweight markov engine intended to replace | ||||
384 | L<AI::MegaHAL|AI::MegaHAL>. It has a L<Mouse|Mouse> (or | ||||
385 | L<Moose|Moose>) based core with pluggable | ||||
386 | L<storage|Hailo::Role::Storage>, L<tokenizer|Hailo::Role::Tokenizer> | ||||
387 | and L<engine|Hailo::Role::Engine> backends. | ||||
388 | |||||
389 | It is similar to MegaHAL in functionality, the main differences (with the | ||||
390 | default backends) being better scalability, drastically less memory usage, | ||||
391 | an improved tokenizer, and tidier output. | ||||
392 | |||||
393 | With this distribution, you can create, modify, and query Hailo brains. To | ||||
394 | use Hailo in event-driven POE applications, you can use the | ||||
395 | L<POE::Component::Hailo|POE::Component::Hailo> wrapper. One example is | ||||
396 | L<POE::Component::IRC::Plugin::Hailo|POE::Component::IRC::Plugin::Hailo>, | ||||
397 | which implements an IRC chat bot. | ||||
398 | |||||
399 | =head2 Etymology | ||||
400 | |||||
401 | I<Hailo> is a portmanteau of I<HAL> (as in MegaHAL) and | ||||
402 | L<failo|http://identi.ca/failo>. | ||||
403 | |||||
404 | =head1 Backends | ||||
405 | |||||
406 | Hailo supports pluggable L<storage|Hailo::Role::Storage> and | ||||
407 | L<tokenizer|Hailo::Role::Tokenizer> backends, it also supports a | ||||
408 | pluggable L<UI|Hailo::Role::UI> backend which is used by the L<hailo> | ||||
409 | command-line utility. | ||||
410 | |||||
411 | =head2 Storage | ||||
412 | |||||
413 | Hailo can currently store its data in either a | ||||
414 | L<SQLite|Hailo::Storage::SQLite>, | ||||
415 | L<PostgreSQL|Hailo::Storage::PostgreSQL> or | ||||
416 | L<MySQL|Hailo::Storage::MySQL> database. Some NoSQL backends were | ||||
417 | supported in earlier versions, but they were removed as they had no | ||||
418 | redeeming quality. | ||||
419 | |||||
420 | SQLite is the primary target for Hailo. It's much faster and uses less | ||||
421 | resources than the other two. It's highly recommended that you use it. | ||||
422 | |||||
423 | See L<Hailo::Storage/"Comparison of backends"> for benchmarks showing | ||||
424 | how the various backends compare under different workloads, and how | ||||
425 | you can create your own. | ||||
426 | |||||
427 | =head2 Tokenizer | ||||
428 | |||||
429 | By default Hailo will use L<the word | ||||
430 | tokenizer|Hailo::Tokenizer::Words> to split up input by whitespace, | ||||
431 | taking into account things like quotes, sentence terminators and more. | ||||
432 | |||||
433 | There's also a L<the character | ||||
434 | tokenizer|Hailo::Tokenizer::Chars>. It's not generally useful for a | ||||
435 | conversation bot but can be used to e.g. generate new words given a | ||||
436 | list of existing words. | ||||
437 | |||||
438 | =head1 UPGRADING | ||||
439 | |||||
440 | Hailo makes no promises about brains generated with earlier versions | ||||
441 | being compatable with future version and due to the way Hailo works | ||||
442 | there's no practical way to make that promise. Learning in Hailo is | ||||
443 | lossy so an accurate conversion is impossible. | ||||
444 | |||||
445 | If you're maintaining a Hailo brain that you want to keep using you | ||||
446 | should save the input you trained it on and re-train when you upgrade. | ||||
447 | |||||
448 | Hailo is always going to lose information present in the input you | ||||
449 | give it. How input tokens get split up and saved to the storage | ||||
450 | backend depends on the version of the tokenizer being used and how | ||||
451 | that input gets saved to the database. | ||||
452 | |||||
453 | For instance if an earlier version of Hailo tokenized C<"foo+bar"> | ||||
454 | simply as C<"foo+bar"> but a later version split that up into | ||||
455 | C<"foo", "+", "bar">, then an input of C<"foo+bar are my favorite | ||||
456 | metasyntactic variables"> wouldn't take into account the existing | ||||
457 | C<"foo+bar"> string in the database. | ||||
458 | |||||
459 | Tokenizer changes like this would cause the brains to accumulate | ||||
460 | garbage and would leave other parts in a state they wouldn't otherwise | ||||
461 | have gotten into. | ||||
462 | |||||
463 | There have been more drastic changes to the database format itself in | ||||
464 | the past. | ||||
465 | |||||
466 | Having said all that the database format and the tokenizer are | ||||
467 | relatively stable. At the time of writing 0.33 is the latest release | ||||
468 | and it's compatable with brains down to at least 0.17. If you're | ||||
469 | upgrading and there isn't a big notice about the storage format being | ||||
470 | incompatable in the F<Changes> file your old brains will probably work | ||||
471 | just fine. | ||||
472 | |||||
473 | =head1 ATTRIBUTES | ||||
474 | |||||
475 | =head2 C<brain> | ||||
476 | |||||
477 | The name of the brain (file name, database name) to use as storage. | ||||
478 | There is no default. Whether this gets used at all depends on the | ||||
479 | storage backend, currently only SQLite uses it. | ||||
480 | |||||
481 | =head2 C<save_on_exit> | ||||
482 | |||||
483 | A boolean value indicating whether Hailo should save its state before | ||||
484 | its object gets destroyed. This defaults to true and will simply call | ||||
485 | L<save|/save> at C<DEMOLISH> time. | ||||
486 | |||||
487 | See L<Hailo::Storage::SQLite/"in_memory"> for how the SQLite backend | ||||
488 | uses this option in conjunction with its C<in_memory> option. | ||||
489 | |||||
490 | =head2 C<order> | ||||
491 | |||||
492 | The Markov order (chain length) you want to use for an empty brain. | ||||
493 | The default is 2. | ||||
494 | |||||
495 | =head2 C<engine_class> | ||||
496 | |||||
497 | =head2 C<storage_class> | ||||
498 | |||||
499 | =head2 C<tokenizer_class> | ||||
500 | |||||
501 | =head2 C<ui_class> | ||||
502 | |||||
503 | A a short name name of the class we use for the engine, storage, | ||||
504 | tokenizer or ui backends. | ||||
505 | |||||
506 | By default this is B<Default> for the engine, B<SQLite> for storage, | ||||
507 | B<Words> for the tokenizer and B<ReadLine> for the UI. The UI backend | ||||
508 | is only used by the L<hailo> command-line interface. | ||||
509 | |||||
510 | You can only specify the short name of one of the packages Hailo | ||||
511 | itself ships with. If you need another class then just prefix the | ||||
512 | package with a plus (Catalyst style), e.g. C<+My::Foreign::Tokenizer>. | ||||
513 | |||||
514 | =head2 C<engine_args> | ||||
515 | |||||
516 | =head2 C<storage_args> | ||||
517 | |||||
518 | =head2 C<tokenizer_args> | ||||
519 | |||||
520 | =head2 C<ui_args> | ||||
521 | |||||
522 | A C<HashRef> of arguments for engine/storage/tokenizer/ui | ||||
523 | backends. See the documentation for the backends for what sort of | ||||
524 | arguments they accept. | ||||
525 | |||||
526 | =head1 METHODS | ||||
527 | |||||
528 | =head2 C<new> | ||||
529 | |||||
530 | This is the constructor. It accepts the attributes specified in | ||||
531 | L</ATTRIBUTES>. | ||||
532 | |||||
533 | =head2 C<learn> | ||||
534 | |||||
535 | Takes a string or an array reference of strings and learns from them. | ||||
536 | |||||
537 | =head2 C<train> | ||||
538 | |||||
539 | Takes a filename, filehandle or array reference and learns from all its | ||||
540 | lines. If a filename is passed, the file is assumed to be UTF-8 encoded. | ||||
541 | Unlike L<C<learn>|/learn>, this method sacrifices some safety (disables | ||||
542 | the database journal, fsyncs, etc) for speed while learning. | ||||
543 | |||||
544 | =head2 C<reply> | ||||
545 | |||||
546 | Takes an optional line of text and generates a reply that might be relevant. | ||||
547 | |||||
548 | =head2 C<learn_reply> | ||||
549 | |||||
550 | Takes a string argument, learns from it, and generates a reply that | ||||
551 | might be relevant. This is equivalent to calling L<learn|/learn> | ||||
552 | followed by L<reply|/reply>. | ||||
553 | |||||
554 | =head2 C<save> | ||||
555 | |||||
556 | Tells the underlying storage backend to L<save its | ||||
557 | state|Hailo::Role::Storage/"save">, any arguments to this method will | ||||
558 | be passed as-is to the backend. | ||||
559 | |||||
560 | =head2 C<stats> | ||||
561 | |||||
562 | Takes no arguments. Returns the number of tokens, expressions, previous | ||||
563 | token links and next token links. | ||||
564 | |||||
565 | =head1 SUPPORT | ||||
566 | |||||
567 | You can join the IRC channel I<#hailo> on FreeNode if you have questions. | ||||
568 | |||||
569 | =head1 BUGS | ||||
570 | |||||
571 | Bugs, feature requests and other issues are tracked in L<Hailo's RT on | ||||
572 | rt.cpan.org|https://rt.cpan.org/Dist/Display.html?Name=Hailo> | ||||
573 | |||||
574 | =head1 SEE ALSO | ||||
575 | |||||
576 | =over | ||||
577 | |||||
578 | =item * L<POE::Component::Hailo> - A non-blocking POE wrapper around Hailo | ||||
579 | |||||
580 | =item * L<POE::Component::IRC::Plugin::Hailo> - A Hailo IRC bot plugin | ||||
581 | |||||
582 | =item * L<http://github.com/hinrik/failo> - Failo, an IRC bot that uses Hailo | ||||
583 | |||||
584 | =item * L<http://github.com/bingos/gumbybrain> - GumbyBRAIN, a more famous IRC bot that uses Hailo | ||||
585 | |||||
586 | =item * L<Hailo::UI::Web> - A L<Catalyst> and jQuery powered web | ||||
587 | interface to Hailo available at L<hailo.nix.is|http://hailo.nix.is> | ||||
588 | and as L<hailo-ui-web|http://github.com/avar/hailo-ui-web> on | ||||
589 | L<GitHub|http://github.com> | ||||
590 | |||||
591 | =item * L<HALBot> - Another L<Catalyst> Dojo powered web interface to | ||||
592 | Hailo available at L<bifurcat.es|http://bifurcat.es/> and as | ||||
593 | L<halbot-on-the-web|http://gitorious.org/halbot-on-the-web/halbot-on-the-web> | ||||
594 | at L<gitorious|http://gitorious.org> | ||||
595 | |||||
596 | =item * L<http://github.com/pteichman/cobe> - cobe, a Python port of MegaHAL "inspired by the success of Hailo" | ||||
597 | |||||
598 | =back | ||||
599 | |||||
600 | =head1 LINKS | ||||
601 | |||||
602 | =over | ||||
603 | |||||
604 | =item * L<hailo.org|http://hailo.org> - Hailo's website | ||||
605 | |||||
606 | =item * L<http://bit.ly/hailo_rewrite_of_megahal> - Hailo: A Perl rewrite of | ||||
607 | MegaHAL, A blog posting about the motivation behind Hailo | ||||
608 | |||||
609 | =item * L<http://blogs.perl.org/users/aevar_arnfjor_bjarmason/hailo/> - | ||||
610 | More blog posts about Hailo on E<AElig>var ArnfjE<ouml>rE<eth> | ||||
611 | Bjarmason's L<blogs.perl.org|http://blogs.perl.org> blog | ||||
612 | |||||
613 | =item * Hailo on L<freshmeat|http://freshmeat.net/projects/hailo> and | ||||
614 | L<ohloh|https://www.ohloh.net/p/hailo> | ||||
615 | |||||
616 | =back | ||||
617 | |||||
618 | =head1 AUTHORS | ||||
619 | |||||
620 | Hinrik E<Ouml>rn SigurE<eth>sson, hinrik.sig@gmail.com | ||||
621 | |||||
622 | E<AElig>var ArnfjE<ouml>rE<eth> Bjarmason <avar@cpan.org> | ||||
623 | |||||
624 | =head1 LICENSE AND COPYRIGHT | ||||
625 | |||||
626 | Copyright 2010 Hinrik E<Ouml>rn SigurE<eth>sson and | ||||
627 | E<AElig>var ArnfjE<ouml>rE<eth> Bjarmason <avar@cpan.org> | ||||
628 | |||||
629 | This program is free software, you can redistribute it and/or modify | ||||
630 | it under the same terms as Perl itself. | ||||
631 | |||||
632 | 1 | 29µs | 1 | 4.62ms | =cut # spent 4.62ms making 1 call to B::Hooks::EndOfScope::__ANON__[B/Hooks/EndOfScope.pm:26] |
# spent 34µs within Hailo::CORE:match which was called 17 times, avg 2µs/call:
# 13 times (25µs+0s) by List::Util::first at line 182, avg 2µs/call
# 3 times (6µs+0s) by Hailo::_new_class at line 177, avg 2µs/call
# once (3µs+0s) by Hailo::__ANON__[(eval 30)[/home/hinrik/perl5/perlbrew/perls/perl-5.13.5/lib/5.13.5/Fatal.pm:1188]:47] at line 14 of (eval 30)[Fatal.pm:1188] | |||||
# spent 104µs (57+47) within Hailo::CORE:open which was called:
# once (57µs+47µs) by Hailo::__ANON__[(eval 29)[/home/hinrik/perl5/perlbrew/perls/perl-5.13.5/lib/5.13.5/Fatal.pm:1102]:127] at line 109 of (eval 29)[Fatal.pm:1102] | |||||
# spent 53µs within Hailo::CORE:regcomp which was called 13 times, avg 4µs/call:
# 13 times (53µs+0s) by List::Util::first at line 182, avg 4µs/call | |||||
# spent 21µs within Hailo::CORE:sort which was called 3 times, avg 7µs/call:
# 3 times (21µs+0s) by Hailo::_new_class at line 183, avg 7µs/call | |||||
# spent 5µs within Hailo::PLUGINS which was called 3 times, avg 2µs/call:
# 3 times (5µs+0s) by Hailo::_new_class at line 180, avg 2µs/call | |||||
# spent 54.7ms (39.7+15.0) within Hailo::_engine which was called 25000 times, avg 2µs/call:
# 25000 times (39.7ms+15.0ms) by Hailo::_learn_one at line 286, avg 2µs/call | |||||
# spent 47.3ms (38µs+47.3) within Hailo::_storage which was called 6 times, avg 7.88ms/call:
# once (29µs+47.3ms) by Hailo::Command::__ANON__[lib/Hailo/Command.pm:234] at line 221 of lib/Hailo/Command.pm
# once (3µs+0s) by Hailo::train at line 240
# once (2µs+0s) by Hailo::Command::run at line 249 of lib/Hailo/Command.pm
# once (2µs+0s) by Hailo::save at line 206
# once (2µs+0s) by Hailo::__ANON__[lib/Hailo.pm:170] at line 140
# once (1µs+0s) by Hailo::train at line 213 | |||||
# spent 82.6ms (36.0+46.7) within Hailo::_tokenizer which was called 25000 times, avg 3µs/call:
# 25000 times (36.0ms+46.7ms) by Hailo::_learn_one at line 288, avg 3µs/call | |||||
# spent 2µs within Hailo::engine_args which was called:
# once (2µs+0s) by Hailo::__ANON__[lib/Hailo.pm:170] at line 140 | |||||
# spent 2µs within Hailo::storage_args which was called:
# once (2µs+0s) by Hailo::__ANON__[lib/Hailo.pm:170] at line 140 | |||||
# spent 2µs within Hailo::tokenizer_args which was called:
# once (2µs+0s) by Hailo::__ANON__[lib/Hailo.pm:170] at line 140 |