# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#
#   Name: performance.sh - part of the BeakerLib project
#   Description: Performance measuring routines
#
#   Author: Petr Muller <pmuller@redhat.com>
#
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#
#   Copyright (c) 2008-2010 Red Hat, Inc. All rights reserved.
#
#   This copyrighted material is made available to anyone wishing
#   to use, modify, copy, or redistribute it subject to the terms
#   and conditions of the GNU General Public License version 2.
#
#   This program is distributed in the hope that it will be
#   useful, but WITHOUT ANY WARRANTY; without even the implied
#   warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
#   PURPOSE. See the GNU General Public License for more details.
#
#   You should have received a copy of the GNU General Public
#   License along with this program; if not, write to the Free
#   Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
#   Boston, MA 02110-1301, USA.
#
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


: <<'=cut'
=pod

=head1 NAME

BeakerLib - performance - Performance measuring routines

=head1 DESCRIPTION

This is a library of helpers and shortcut for performance monitoring
of applications. It provides various means of measuring time
and memory performance of programs.

=head1 FUNCTIONS

=cut

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# rlPerfTime_RunsInTime
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
: <<'=cut'
=pod

=head2 Time Performance

=head3 rlPerfTime_RunsInTime

Measures, how many runs of some commands can be done in specified time.
This approach is suitable for short-time running tasks (up to few seconds),
where averaging few runs is not precise. This is done several times, and
the final result is the average of all runs. It prints the number on stdout,
so it has to be captured.

    rlPerfTime_RunsInTime command [time] [runs]

=over

=item command

Command to run.

=item time

Time in seconds (optional, default=30).

=item runs

Number of averaged runs (optional, default=3).

=back

=cut

rlPerfTime_RunsInTime(){
    local command=$1
    local time=${2:-"30"}
    local runs=${3:-"3"}
    local RES=$((0))
    local TOTAL=$((0))
    local DONE_RUNS=$((1))
    local PID=$$
    rlLog "Measuring how much runs we'll make in $time seconds"
    rlLog "Command: '$command'"
    rlLog "The result is an average of $runs rounds"
    trap '  TOTAL=$((TOTAL+RES));\
            rlLog "Round $DONE_RUNS finished, made $RES runs in it";\
            if [ "$DONE_RUNS" == "$runs" ];\
            then\
                rlLog "Done, the average ($TOTAL/$DONE_RUNS) is $((TOTAL/DONE_RUNS)) runs";\
                echo $((TOTAL/DONE_RUNS));\
                return 0;\
            else\
                RES=$((0));\
                DONE_RUNS=$((DONE_RUNS+1));\
                eval "sleep $time; kill -USR1 $PID" &
            fi' SIGUSR1

    eval "sleep $time; kill -USR1 $$" &
    while true ; do
        eval "$1"
        RES=$((RES+1))
    done
}

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# rlPerfTime_AvgFromRuns
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
: <<'=cut'
=pod

=head3 rlPerfTime_AvgFromRuns

Measures the average time of running some task. This approach is suitable
for long-time running tasks (tens of seconds and more), where it is
precise enough. Measured runs can be preceded by dry run, which is not
measured and it's purpose is to warm up various caches.
It prints the number on stdout, so it has to be captured.
Or, result is then stored in special rl_retval variable.

    rlPerfTime_AvgFromRuns command [count] [warmup]

=over

=item command

Command to run.

=item count

Times to run (optional, default=3).

=item warmup

Warm-up run, run if this option is not "warmup" (optional, default="warmup")

=back

=cut

rlPerfTime_AvgFromRuns(){
    local command="$1"
    local runs=${2:-"3"}
    local warmup=${3:-"warmup"}
    local total=0
    rlLog "Measuring the average time of runnning command '$command'"
    rlLog "The result will be an average of $runs runs"

    if [ "$warmup" == "warmup" ]; then
        rlLog "Doing non-measured warmup run"
        eval "$command"
    fi
    local __INTERNAL_TIMER=$(mktemp) # no-reboot

    local cnt
    for cnt in $(seq $runs); do
        /usr/bin/time -o $__INTERNAL_TIMER -f "bt=\"%U + %S\"" $command
        . $__INTERNAL_TIMER
        rlLog "Run $cnt took $bt seconds"
        total="$(echo "scale=5; $total + $bt" | bc)"
    done
    rlLog "The average of $runs runs was $(echo "scale=5; $total / $runs" | bc) seconds"
    echo "$(echo "scale=5; $total / $runs" | bc)"
    export rl_retval="$(echo "scale=5; $total / $runs" | bc)"
    rm -f $__INTERNAL_TIMER
}

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# AUTHORS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
: <<'=cut'
=pod

=head1 AUTHORS

=over

=item *

Petr Muller <pmuller@redhat.com>

=back

=cut
