=pod

=for stopwords smush smushed enums

=encoding utf-8

=head1 NAME

Type::Tiny::Manual::Optimization - squeeze the most out of your CPU

=head1 DESCRIPTION

Various tips to help you squeeze the most out of your CPU.

=head2 XS

The simplest thing you can do to increase performance of many of
the built-in type constraints is to install L<Type::Tiny::XS>, a
set of ultra-fast type constraint checks implemented in C.

L<Type::Tiny> will attempt to load L<Type::Tiny::XS> and use its
type checks. If L<Type::Tiny::XS> is not available, it will then
try to use L<Mouse> B<< if it is already loaded >>, but Type::Tiny
won't attempt to load Mouse for you.

=head3 Types that can be accelerated by Type::Tiny::XS

The following simple type constraints from L<Types::Standard> will
be accelerated by Type::Tiny::XS: C<Any>, C<ArrayRef>, C<Bool>,
C<ClassName>, C<CodeRef>, C<Defined>, C<FileHandle>, C<GlobRef>,
C<HashRef>, C<Int>, C<Item>, C<Object>, C<Map>, C<Ref>, C<ScalarRef>,
C<Str>, C<Tuple>, C<Undef>, and C<Value>. (Note that C<Num> and
C<RegexpRef> are B<not> on that list.)

The parameterized form of C<Ref> cannot be accelerated.

The parameterized forms of C<ArrayRef>, C<HashRef>, and C<Map> can be
accelerated only if their parameters are.

The parameterized form of C<Tuple> can be accelerated if its
parameters are, it has no C<Optional> components, and it does not use
C<slurpy>.

Certain type constraints may benefit partially from Type::Tiny::XS.
For example, C<RoleName> inherits from C<ClassName>, so part of the
type check will be conducted by Type::Tiny::XS.

The parameterized C<InstanceOf>, C<HasMethods>, and C<Enum> type
constraints will be accelerated. So will L<Type::Tiny::Class>,
L<Type::Tiny::Duck>, and L<Type::Tiny::Enum> objects. (But enums will
only be accelerated if the list of allowed string values consist
entirely of word characters and hyphens - that is:
C<< not grep /[^\w-]/, @values >>.)

The C<PositiveInt> and C<PositiveOrZeroInt> type constraints from
L<Types::Common::Numeric> will be accelerated, as will the
C<NonEmptyStr> type constraint from L<Types::Common::String>.

L<Type::Tiny::Union> and L<Type::Tiny::Intersection> will also be
accelerated if their constituent type constraints are.

=head3 Types that can be accelerated by Mouse

The following simple type constraints from L<Types::Standard> will
be accelerated by Type::Tiny::XS: C<Any>, C<ArrayRef>, C<Bool>,
C<ClassName>, C<CodeRef>, C<Defined>, C<FileHandle>, C<GlobRef>,
C<HashRef>, C<Ref>, C<ScalarRef>, C<Str>, C<Undef>, and C<Value>.
(Note that C<Item>, C<Num>, C<Int>, C<Object>, and C<RegexpRef>
are B<not> on that list.)

The parameterized form of C<Ref> cannot be accelerated.

The parameterized forms of C<ArrayRef> and C<HashRef> can be
accelerated only if their parameters are.

Certain type constraints may benefit partially from Mouse. For
example, C<RoleName> inherits from C<ClassName>, so part of the
type check will be conducted by Mouse.

The parameterized C<InstanceOf> and C<HasMethods> type constraints
will be accelerated. So will L<Type::Tiny::Class> and
L<Type::Tiny::Duck> objects.

=head2 Common Sense

The C<< HashRef[ArrayRef] >> type constraint can probably be checked
faster than C<< HashRef[ArrayRef[Num]] >>. If you find yourself using
very complex and slow type constraints, you should consider switching
to simpler and faster ones. (Though this means you have to place a
little more trust in your caller to not supply you with bad data.)

(A counter-intuitive exception to this: even though C<Int> is more
restrictive than C<Num>, in most circumstances C<Int> checks will run
faster.)

=head2 Inlining Type Constraints

If your type constraint can be inlined, this can not only speed up
Type::Tiny's own checks and coercions, it may also allow your type constraint
to be inlined into generated methods such as Moose attribute accessors.

All of the constraints from C<Types::Standard> can be inlined, as can enum,
class_type, role_type and duck_type constraints. Union and intersection
constraints can be inlined if their sub-constraints can be. So if you can
define your own types purely in terms of these types, you automatically
get inlining:

   declare HashLike, as union [
      Ref["HASH"],
      Overload["&{}"],
   ];

However, sometimes these base types are not powerful enough and you'll need
to write a constraint coderef:

   declare NonEmptyHash, as HashLike,
      where     { scalar values %$_ };

... and you've suddenly sacrificed a lot of speed.

Inlining to the rescue! You can define an inlining coderef which will be
passed two parameters: the constraint itself and a variable name as a string.
For example, the variable name might be C<< '$_' >> or C<< '$_[0]' >>.
Your coderef should return a Perl expression string, interpolating that
variable name.

   declare NonEmptyHash, as HashLike,
      where     { scalar values %$_ },
      inline_as {
         my ($constraint, $varname) = @_;
         return sprintf(
            '%s and scalar values %%{%s}',
            $constraint->parent->inline_check($varname),
            $varname,
         );
      };

The Perl expression could be inlined within a function or a C<if> clause or
potentially anywhere, so it really must be an expression, not a statement.
It should not C<return> or C<exit> and probably shouldn't C<die>. (If you
need loops and so on, you can output a C<do> block.)

Note that if you're subtyping an existing type constraint, your C<inline_as>
block is also responsible for checking the parent type's constraint. This
can be done quite easily, as shown in the example above.

Note that defining a type constraint in terms of a constraint coderef and an
inlining coderef can be a little repetitive. L<Sub::Quote> provides an
alternative that reduces repetition (though the inlined code might not be as
compact/good/fast).

   declare NonEmptyHash, as HashLike,
      constraint => quote_sub q{ scalar values %$_ };

Aside: it's been pointed out that "might not be as fast" above is a bit
hand-wavy. When Type::Tiny does inlining from Sub::Quote coderefs, it needs
to inline all the ancestor type constraints, and smush them together with
C<< && >>. This may result in duplicate checks. For example, if 'MyArray'
inherits from 'MyRef' which inherits from 'MyDef', the inlined code might
end up as:

   defined($_)              # check MyDef
   && ref($_)               # check MyRef
   && ref($_) eq 'ARRAY'    # check MyArray

When just the last check would have been sufficient. A custom C<inline_as>
allows you finer control over how the type constraint is inlined.

=head2 Optimizing Coercions

Coercions are often defined using coderefs:

   PathTiny->plus_coercions(
      Str,   sub { "Path::Tiny"->new($_) },
      Undef, sub { "Path::Tiny"->new("/etc/myapp/default.conf") },
   );

But you can instead define them as strings of Perl code operating on
C<< $_ >>:

   PathTiny->plus_coercions(
      Str,   q{ "Path::Tiny"->new($_) },
      Undef, q{ "Path::Tiny"->new("/etc/myapp/default.conf") },
   );

The latter will run faster, so is preferable at least for simple
coercions.

This makes the most difference when used with L<Moo>, which supports
inlining of coercions. L<Moose> does not inline coercions, but
providing coercions as strings still allows Type::Tiny to optimize the
coercion coderef it provides to Moose.

=head1 AUTHOR

Toby Inkster E<lt>tobyink@cpan.orgE<gt>.

=head1 COPYRIGHT AND LICENCE

This software is copyright (c) 2013-2014, 2017-2019 by Toby Inkster.

This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.

=head1 DISCLAIMER OF WARRANTIES

THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.

=cut
