Skip to content

Instantly share code, notes, and snippets.

@barefootcoder
Last active December 28, 2015 04:29
Show Gist options
  • Save barefootcoder/7442547 to your computer and use it in GitHub Desktop.
Save barefootcoder/7442547 to your computer and use it in GitHub Desktop.
This is a quick benchmark I put together to compare command-line use of Moose vs MooseX::Declare. See below for details.

It's Moose vs MooseX::Declare in a speed showdown!!

This benchmark uses MooseX::App::Cmd to create some simple command-line apps, then uses Dumbbench to run them over and over to see which which one is fastest. Well, actually we already know that plain Moose is going to win. The real question is, is it faster enough to be significant in amongst all the overhead of starting up Perl, App::Cmd, loading Moose-guts, etc.

A few notes:

  • In the results, "MXD" indicates a command that uses MooseX::Declare at every level (app, base command, and command subclass). "NoMXD" indicates a command that does not use MooseX::Declare at all.

  • Since each run in our benchmark is spawning a shell, we're going to be comparing compile-time speed and run-time speed, but mostly compile-time (because our command doesn't do very much actual work). That's mainly what I wanted to get at with this benchmark.

  • Importantly, MooseX::Method::Signatures is not used with MXD, because that's what actually kills MXD's run-time speed. Instead, Method::Signatures::Modifiers is used for the MXD version (as recommended in my first Method::Signatures talk). Method::Signatures is used with the NoMXD version, mainly to insure that we compare apples to apples.

  • You may wonder why I have the commands spit out output, but then the benchmark just throws that output away. Having the output makes it easy to test each command individually to make sure they work before I benchmark them. However, the benchmark is going to run the commands over and over, and I don't want to drown in all that output. Of course, I could go back and comment out all the output, but leaving it in means the commands are doing some actual work, which, again, makes for a better benchmark.

  • If you're wondering why I've done anything else the way I've done it, it's because I was hacking code from an active project I'm working on, so it's similar to real working code. I cut most of the extranneous bits out, but I left some of the structure in to help this be a more realistic benchmark.

Here are the results:

                  Rate         MXD NoMXD
MXD   1.3791+-0.0013/s          -- -1.3%
NoMXD 1.3969+-0.0014/s 1.29+-0.14%    --

As you can see, we're talking about a difference of less than 1.5%, so I feel pretty comfortable that no one's going to notice that small an amount of slowdown. Certainly I feel like the added readability of my code is worth that tiny speed penalty.

The code I used for the benchmark is below. However, gists refuse to deal with directories, so, if you're trying to re-create this test, just make sure you replace any _ in a filename below with a /, and make directories as appropriate for those to land in.

#! /usr/bin/env perl
use 5.12.0;
use autodie qw< :all >;
use warnings FATAL => 'all';
use Dumbbench;
use Benchmark::Dumb qw< :all >;
cmpthese(10.001,
{
MXD => sub { system("$^X ./mxd command1 >/dev/null") },
NoMXD => sub { system("$^X ./nomxd command1 >/dev/null") },
}
);
use 5.012;
use autodie qw< :all >;
use warnings FATAL => 'all';
use MooseX::Declare;
use Method::Signatures::Modifiers;
class MXD extends MooseX::App::Cmd
{
use autodie qw< :all >;
}
1;
use 5.012;
use autodie qw< :all >;
use warnings FATAL => 'all';
use MooseX::Declare;
use Method::Signatures::Modifiers;
use MooseX::Attribute::ENV;
class MXD::Command extends MooseX::App::Cmd::Command
{
use autodie qw< :all >;
use MooseX::Has::Sugar;
use MooseX::Types::Moose qw< :all >;
# GLOBAL OPTIONS
# (apply to all commands)
has no_color => (
traits => [qw< Getopt ENV >],
documentation => "Don't use color output (default: use color when printing to a term).",
cmd_flag => 'no-color',
env_prefix => 'VCTOOLS',
ro, isa => Bool,
);
has color => (
traits => [qw< Getopt ENV >],
documentation => "Use color output (even when not printing to a term).",
env_prefix => 'VCTOOLS',
ro, isa => Bool,
);
has pretend => (
traits => [qw< Getopt ENV >],
documentation => "Don't actually run any destructive commands; just print them.",
cmd_aliases => 'p',
env_prefix => 'VCTOOLS',
ro, isa => Bool,
);
has echo => (
traits => [qw< Getopt ENV >],
documentation => "Print each command before performing it (like bash -x).",
cmd_aliases => 'x',
env_prefix => 'VCTOOLS',
ro, isa => Bool,
);
has interactive => (
traits => [qw< Getopt ENV >],
documentation => "Ask to confirm each command before performing it (like find -ok).",
cmd_aliases => 'i',
env_prefix => 'VCTOOLS',
ro, isa => Bool,
);
has policy => (
traits => [qw< Getopt ENV >],
documentation => "Only operate under the given policy.",
env_prefix => 'VCTOOLS',
ro, isa => Str,
);
# ACTION METHODS
# (augment these)
method validate_args ($opt, ArrayRef $args)
{
# pretend to verify some stuff
-d '/tmp' or die("no temp dir!");
`ls` or die("no files!");
inner();
}
method execute (...)
{
say "about to run:";
inner();
say "completed successfully";
}
}
1;
use 5.012;
use autodie qw< :all >;
use warnings FATAL => 'all';
use MooseX::Declare;
use Method::Signatures::Modifiers;
class MXD::Command::command1 extends MXD::Command
{
use autodie qw< :all >;
use Path::Class;
use MooseX::Has::Sugar;
use MooseX::Types::Moose qw< :all >;
method description
{
return "\n"
. "run some command\n"
. "\n"
;
}
augment validate_args ($opt, ArrayRef $args)
{
@$args == 0 or die("don't give me any args!");
}
augment execute (...)
{
say "I'm command #1";
}
}
1;
use 5.012;
use autodie qw< :all >;
use warnings FATAL => 'all';
package NoMXD
{
use autodie qw< :all >;
use Moose;
use Method::Signatures::Modifiers;
extends 'MooseX::App::Cmd';
no Moose;
__PACKAGE__->meta->make_immutable;
}
1;
use 5.012;
use autodie qw< :all >;
use warnings FATAL => 'all';
package MXDApp::Command
{
use autodie qw< :all >;
use MooseX::Declare;
use Method::Signatures::Modifiers;
use MooseX::Attribute::ENV;
extends MooseX::App::Cmd::Command;
use MooseX::Has::Sugar;
use MooseX::Types::Moose qw< :all >;
# GLOBAL OPTIONS
# (apply to all commands)
has no_color => (
traits => [qw< Getopt ENV >],
documentation => "Don't use color output (default: use color when printing to a term).",
cmd_flag => 'no-color',
env_prefix => 'VCTOOLS',
ro, isa => Bool,
);
has color => (
traits => [qw< Getopt ENV >],
documentation => "Use color output (even when not printing to a term).",
env_prefix => 'VCTOOLS',
ro, isa => Bool,
);
has pretend => (
traits => [qw< Getopt ENV >],
documentation => "Don't actually run any destructive commands; just print them.",
cmd_aliases => 'p',
env_prefix => 'VCTOOLS',
ro, isa => Bool,
);
has echo => (
traits => [qw< Getopt ENV >],
documentation => "Print each command before performing it (like bash -x).",
cmd_aliases => 'x',
env_prefix => 'VCTOOLS',
ro, isa => Bool,
);
has interactive => (
traits => [qw< Getopt ENV >],
documentation => "Ask to confirm each command before performing it (like find -ok).",
cmd_aliases => 'i',
env_prefix => 'VCTOOLS',
ro, isa => Bool,
);
has policy => (
traits => [qw< Getopt ENV >],
documentation => "Only operate under the given policy.",
env_prefix => 'VCTOOLS',
ro, isa => Str,
);
# ACTION METHODS
# (augment these)
method validate_args ($opt, ArrayRef $args)
{
# pretend to verify some stuff
-d '/tmp' or die("no temp dir!");
`ls` or die("no files!");
inner();
}
method execute (...)
{
say "about to run:";
inner();
say "completed successfully";
}
no Moose;
__PACKAGE__->meta->make_immutable;
}
1;
use 5.012;
use autodie qw< :all >;
use warnings FATAL => 'all';
package NoMXD::Command::command1
{
use autodie qw< :all >;
use Moose;
use Method::Signatures;
extends 'MXD::Command';
use Path::Class;
use MooseX::Has::Sugar;
use MooseX::Types::Moose qw< :all >;
method description
{
return "\n"
. "run some command\n"
. "\n"
;
}
augment validate_args => method ($opt, ArrayRef $args)
{
@$args == 0 or die("don't give me any args!");
};
augment execute => method (...)
{
say "I'm command #1";
};
no Moose;
__PACKAGE__->meta->make_immutable;
}
1;
#! /usr/bin/env perl
use strict;
use autodie;
use warnings;
# determine proper @INC using only core modules
# once we do that, we can proceed to load other modules
BEGIN
{
use Cwd;
use File::Spec;
use File::Basename;
my $base_dir = dirname Cwd::realpath($0);
my $lib_dir = File::Spec->catfile( $base_dir, 'lib' );
# now jam our lib/ into @INC
unshift @INC, $lib_dir;
}
use MXD;
MXD->run;
#! /usr/bin/env perl
use strict;
use autodie;
use warnings;
# determine proper @INC using only core modules
# once we do that, we can proceed to load other modules
BEGIN
{
use Cwd;
use File::Spec;
use File::Basename;
my $base_dir = dirname Cwd::realpath($0);
my $lib_dir = File::Spec->catfile( $base_dir, 'lib' );
# now jam our lib/ into @INC
unshift @INC, $lib_dir;
}
use NoMXD;
NoMXD->run;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment