
=begin pod

=head1 NAME

ProcStats - dump rusage process statistics with optional label.

=head1 SYNOPSIS

    # print getrusage(2) statistics w/ added optinal
    # label, final summary.
    #
    # default is "Print" option.
    #
    # see example/bare-for-loop

    #!/usr/bin/env perl6
    use v6.d;
    use ProcStats;

    print-stats :label( $*PROGRAM );

    print-stats
    for 1 .. 3;

    print-stats( final => True );


    # output
    # output #0 has all parameters without any differences
    # (there is no prior sample to diff).
    # successive stats w/o prior will show only outputs with
    # differenes in stats other than user and wallclock time
    # using a leading '+' for the offset values.
    #
    # if 'final' is used then all stats are output, even if
    # they are zero, and Final => True is added to the output.
    #
    # label is output if provided.

    ---
    label    : ./exmaple/bare-for-loop
    output   : 0
    sample   : 0
    wtime    : 1563047524.627299    # timestamp of test
    utime    : 0.269591
    stime    : 0.038513
    maxrss   : 105852
    minflt   : 20822
    majflt   : 0
    inblock  : 0
    oublock  : 8
    ---
    output   : 1
    sample   : 1
    maxrss   : +760         # only stats with differnces
    minflt   : +173         # are listed.
    utime    : +0.018007    # utime and wtime will always grow,
    wtime    : +0.008687    # they are returned with other diff's.
    ---

    <snip... >

    ---
    Final    : True         # :final( True )
    output   : 4
    sample   : 4
    inblock  : +0
    majflt   : +0
    maxrss   : +1024
    minflt   : +247
    oublock  : +0
    stime    : +0.000745
    utime    : +0.038095
    wtime    : +0.019526

    # Output is only provided if there are differences in
    # the compared fields. If any of those are different
    # then the wallclock, user, and system times are added
    # the entire difference returned.
    #
    # see example/rand-user-table

    print-stats label => "$*PROGRAM";

    for 1 .. 100
    {
        (
            &user-add  => 0.10,
            &user-drop => 0.10,
            &user-op   => 0.80,
        ).Mix.roll( 1_000 )».()
        ;

        print-stats label => 'Keys: ' ~ %user-data.elems;
    }

    print-stats :final, label => 'Keys: ' ~ %user-data.elems;

    # output

    ---
    label    : exmaple/rand-user-table
    output   : 0                        # first sample:
    sample   : 0                        # output = sample = 0.
    wtime    : 1563048388.1687117
    utime    : 0.579475
    stime    : 0
    maxrss   : 116448
    minflt   : 23573
    majflt   : 0
    inblock  : 0
    oublock  : 8
    ---
    label    : Keys: 19
    output   : 1
    sample   : 1
    maxrss   : +3884
    minflt   : +901
    utime    : +0.100587
    wtime    : +0.052823
    ---
    <snip...>

    ---
    label    : Keys: 60
    output   : 14           # 1 .. 14 output have 1 .. 14
    sample   : 14           # samples: something changed each time.
    maxrss   : +264
    minflt   : +24
    utime    : +0.029787    # utime & wtime are alwyas output if
    wtime    : +0.020622    # there are other differences.
    ---
    label    : Keys: 117
    output   : 15           # 15th output has 18th sample:
    sample   : 18           # samples 15 .. 17 were unchanged.
    minflt   : +27          # minflt increased 27 in sample 18.
    utime    : +0.027277
    wtime    : +0.022936
    ---
    <snip...>

    ---
    label    : Keys: 129
    output   : 61           # 39 samples had no differences
    sample   : 100
    minflt   : +10
    utime    : +0.024304
    wtime    : +0.024276
    ---
    label    : Keys: 129
    Final    : True         # :final( True )
    output   : 62           # 62 changes.
    sample   : 101
    inblock  : +0
    majflt   : +0
    maxrss   : +9424
    minflt   : +2323
    oublock  : +0
    stime    : +0
    utime    : +2.361321
    wtime    : +2.202044





=head1 DESCRIPTION

...


=head2 Notes

=item Sample & Output

Comparing sample & output counts allows verifying
average rate at which parameters change:
output only increments when something changes,
%sample<sample> increments with every call
to extract-stats.

=item Wallclock Time (wtime)

This is derived from Perl's now() result (rounded to microseconds). 
Combined with the program name ($*PROGRAM), this makes a reasonable
candiate key for tracking execution history over mulitple tests.


=head2 Sample code.

=item rand-user-table

This performs a set of random operations on a hash by adding
keys, dropping keys, and incrementing the stored values.

It prints the stats every 1000 iterations of the trial:

    for 1 .. 1000
    {
        constant weighted_operations =
        (
            &user-add  => 0.10,
            &user-drop => 0.10,
            &user-op   => 0.80,
        ).Mix;

        weighted_operations.roll( 1_000 )».();

        print-stats label => 'Keys: ' ~ %user-data.elems;
    }

The output illustrates adding progress information to the labels,
and will [usually] show examples of the output count being lower
than the sample count due to the compared statistics not changing.

Also note here that the final sample shows wallclock time less 
than user time: MOAR is threaded! Use wtime for elapsed:

stime will grow due to output from print-stats and overhead from
calling getrusage(2). Where stime is very small it is probably
due to overhead from ProcStats.

    ---
    label    : Keys: 13
    Final    : True
    output   : 53
    sample   : 101
    inblock  : +0
    majflt   : +0
    maxrss   : +5768
    minflt   : +1467
    oublock  : +0
    stime    : +0.018914
    utime    : +2.24491     # 2.24 > 2.12 due to threading!
    wtime    : +2.123665


=head1 SEE ALSO

=item getrusage(2) getrusage(3p)

getrusage is POSIX (3p).

Thing is that Linux does not maintain values for all of the contents
(see getrusage(2) for descriptions).

    struct rusage
    {
       struct timeval ru_utime; /* user CPU time used */
       struct timeval ru_stime; /* system CPU time used */
       long   ru_maxrss;        /* maximum resident set size */
     * long   ru_ixrss;         /* integral shared memory size */
     * long   ru_idrss;         /* integral unshared data size */
     * long   ru_isrss;         /* integral unshared stack size */
       long   ru_minflt;        /* page reclaims (soft page faults) */
       long   ru_majflt;        /* page faults (hard page faults) */
     * long   ru_nswap;         /* swaps */
       long   ru_inblock;       /* block input operations */
       long   ru_oublock;       /* block output operations */
     * long   ru_msgsnd;        /* IPC messages sent */
     * long   ru_msgrcv;        /* IPC messages received */
     * long   ru_nsignals;      /* signals received */
       long   ru_nvcsw;         /* voluntary context switches */
       long   ru_nivcsw;        /* involuntary context switches */
    };

=item timeval (3bsd )

Struct timeval is a pair of ints for seconds + microseconds which
are dealt with by dividing the microseconcs and adding to get utime
and stime in the reported stats.
