[FFmpeg-devel] a new benchmarking option for ffmpeg - patch 1 of 1

Michael Niedermayer michaelni at gmx.at
Sat May 18 21:55:58 CEST 2013


On Thu, Apr 18, 2013 at 02:42:06PM +0200, "René J.V. Bertin" wrote:
> On Mar 13, 2013, at 15:31, René J.V. Bertin wrote:
> 
> > updated patch against current head:
> 
> Bump. Or maybe bang(ing) is more appropriate :)

[...]
> --- a/ffmpeg.c
> +++ b/ffmpeg.c
> @@ -103,6 +103,11 @@
>  
>  #include "libavutil/avassert.h"
>  
> +//#include "timing.h"

unneeded


> +// include libavutil/timer.h with the flag set to obtain the highres time functions
> +#define AVUTIL_HIGHRES_TIMERS_REQUIRED

this does not work, other headers can have included timer.h already
before and the header will not get processed a second time


> +#include "libavutil/timer.h"
> +
>  const char program_name[] = "ffmpeg";
>  const int program_birth_year = 2000;
>

> @@ -117,8 +122,16 @@ const char *const forced_keyframes_const_names[] = {
>      NULL
>  };
>  
> +//RJVB

unneeded


> +typedef struct BenchTimes {
> +    double user_time, system_time, total_time;
> +    double user_cum_time, system_cum_time, total_cum_time;
> +    size_t num_samples, num_obs;
> +} BenchTimes;

this should be in some header


> +BenchTimes flush_bench, vid_dec_bench, aud_dec_bench, vid_enc_bench, aud_enc_bench, ffmpeg_bench;
> +
>  static void do_video_stats(OutputStream *ost, int frame_size);
> -static int64_t getutime(void);
> +static int64_t getutime(BenchTimes *bin);
>  static int64_t getmaxrss(void);
>  
>  static int run_as_daemon  = 0;
> @@ -521,10 +534,17 @@ static void abort_codec_experimental(AVCodec *c, int encoder)
>      exit(1);
>  }
>  
> -static void update_benchmark(const char *fmt, ...)
> +/**
> + *  benchmarking updater/priming function. Primes for a new interval measurement if fmt==NULL, or determines
> + *  the duration of the current interval.
> + *  @param bin  the target benchmarking bin that will be updated or primed in benchmark_most mode.
> + *  @param fmt  If not NULL, a printf style format string that determines how the timing result is printed if in
> + *              benchmark_all mode.
> + */
> +static void update_benchmark( BenchTimes *bin, const char *fmt, ...)
>  {
>      if (do_benchmark_all) {
> -        int64_t t = getutime();
> +        int64_t t = getutime(NULL);
>          va_list va;
>          char buf[1024];
>  
> @@ -536,6 +556,64 @@ static void update_benchmark(const char *fmt, ...)
>          }
>          current_time = t;
>      }
> +    else if( do_benchmark_most && bin ){
> +        if( fmt ){
    
> +            double uT0 = bin->user_time, sT0 = bin->system_time, tT0 = bin->total_time;
> +            // collect data
> +            getutime(bin);
> +            bin->user_cum_time      += bin->user_time   -   uT0;
> +            bin->system_cum_time    += bin->system_time -   sT0;
> +            bin->total_cum_time     += bin->total_time  -   tT0;
> +            bin->num_samples        += 1;
> +            bin->num_obs            += 1;
> +        }
> +        else{
> +            // mark start of benchmarking interval
> +            getutime(bin);
> +        }
> +    }
> +}
> +
> +/**
> + *  prints the state of a benchmarking bin and optionally adds the results to a different bin
> + *  that will hold the cumulative timings across several input bins. Pass its address in the bin
> + *  and cum_bin arguments to print weighed averages.
> + *  @param fp   the destination file pointer
> + *  @param[in] bin  the bin with the data to be printed
> + *  @param[in] header   the string to prefix to the output
> + *  @param cum_bin  the bin that will hold the cumulative results, or NULL.
> + */
> +static void print_bench_bin( FILE *fp, BenchTimes *bin, const char *header, BenchTimes *cum_bin )
> +{ double cpu;
> +    if( bin && bin->num_samples ){
> +        if( cum_bin && cum_bin == bin ){
> +            char tots[32];
> +            bin->user_cum_time /= bin->num_samples;
> +            bin->system_cum_time /= bin->num_samples;
> +            bin->total_cum_time /= bin->num_samples;
> +            cpu = (bin->user_cum_time + bin->system_cum_time) * 100.0 / bin->total_cum_time;
> +            // output the number of samples over the number of observations using an intermediate
> +            // string (so that it prints in a fixed number of spaces without internal whitespace).
> +            snprintf( tots, sizeof(tots), "%g/%lu", (double) bin->num_samples, bin->num_obs );
> +            fprintf( fp, "%s\t%10s\t%10gs\t%10gs\t%10gs\t%10g%%\n", header,
> +                    tots, bin->user_cum_time, bin->system_cum_time, bin->total_cum_time,
> +                    cpu );
> +            cum_bin = NULL;
> +        }
> +        else{
> +            cpu = (bin->user_cum_time + bin->system_cum_time) * 100.0 / bin->total_cum_time;
> +            fprintf( fp, "%s\t%10ld\t%10gs\t%10gs\t%10gs\t%10g%%\n", header,
> +                    bin->num_samples, bin->user_cum_time, bin->system_cum_time, bin->total_cum_time,
> +                    cpu );
> +        }
> +        if( cum_bin ){
> +            cum_bin->num_samples += bin->num_samples;
> +            cum_bin->user_cum_time += bin->user_cum_time * bin->num_samples;
> +            cum_bin->system_cum_time += bin->system_cum_time * bin->num_samples;
> +            cum_bin->total_cum_time += bin->total_cum_time * bin->num_samples;
> +            cum_bin->num_obs += 1;
> +        }
> +    }
>  }

this belongs into the timer code and not ffmpeg.c


>  
>  static void write_frame(AVFormatContext *s, AVPacket *pkt, OutputStream *ost)
> @@ -672,12 +750,12 @@ static void do_audio_out(AVFormatContext *s, OutputStream *ost,
>      ost->sync_opts = frame->pts + frame->nb_samples;
>  
>      av_assert0(pkt.size || !pkt.data);
> -    update_benchmark(NULL);
> +    update_benchmark(&aud_enc_bench, NULL);
>      if (avcodec_encode_audio2(enc, &pkt, frame, &got_packet) < 0) {
>          av_log(NULL, AV_LOG_FATAL, "Audio encoding failed (avcodec_encode_audio2)\n");
>          exit(1);
>      }
> -    update_benchmark("encode_audio %d.%d", ost->file_index, ost->index);
> +    update_benchmark(&aud_enc_bench, "encode_audio %d.%d", ost->file_index, ost->index);
>  
>      if (got_packet) {
>          if (pkt.pts != AV_NOPTS_VALUE)
> @@ -921,9 +999,9 @@ static void do_video_out(AVFormatContext *s,
>              av_log(NULL, AV_LOG_DEBUG, "Forced keyframe at time %f\n", pts_time);
>          }
>  
> -        update_benchmark(NULL);
> -        ret = avcodec_encode_video2(enc, &pkt, in_picture, &got_packet);
> -        update_benchmark("encode_video %d.%d", ost->file_index, ost->index);
> +        update_benchmark(&vid_enc_bench, NULL);
> +        ret = avcodec_encode_video2(enc, &pkt, &in_picture, &got_packet);
> +        update_benchmark(&vid_enc_bench, "encode_video %d.%d", ost->file_index, ost->index);
>          if (ret < 0) {
>              av_log(NULL, AV_LOG_FATAL, "Video encoding failed\n");
>              exit(1);
> @@ -1317,9 +1395,9 @@ static void flush_encoders(void)
>                  pkt.data = NULL;
>                  pkt.size = 0;
>  
> -                update_benchmark(NULL);
> +                update_benchmark(&flush_bench, NULL);
>                  ret = encode(enc, &pkt, NULL, &got_packet);
> -                update_benchmark("flush %s %d.%d", desc, ost->file_index, ost->index);
> +                update_benchmark(&flush_bench, "flush %s %d.%d", desc, ost->file_index, ost->index);
>                  if (ret < 0) {
>                      av_log(NULL, AV_LOG_FATAL, "%s encoding failed\n", desc);
>                      exit(1);
> @@ -1494,9 +1572,9 @@ static int decode_audio(InputStream *ist, AVPacket *pkt, int *got_output)
>          return AVERROR(ENOMEM);
>      decoded_frame = ist->decoded_frame;
>  
> -    update_benchmark(NULL);
> +    update_benchmark(&aud_dec_bench, NULL);
>      ret = avcodec_decode_audio4(avctx, decoded_frame, got_output, pkt);
> -    update_benchmark("decode_audio %d.%d", ist->file_index, ist->st->index);
> +    update_benchmark(&aud_dec_bench, "decode_audio %d.%d", ist->file_index, ist->st->index);
>  
>      if (ret >= 0 && avctx->sample_rate <= 0) {
>          av_log(avctx, AV_LOG_ERROR, "Sample rate %d invalid\n", avctx->sample_rate);
> @@ -1636,10 +1714,10 @@ static int decode_video(InputStream *ist, AVPacket *pkt, int *got_output)
>      decoded_frame = ist->decoded_frame;
>      pkt->dts  = av_rescale_q(ist->dts, AV_TIME_BASE_Q, ist->st->time_base);
>  
> -    update_benchmark(NULL);
> +    update_benchmark(&vid_dec_bench, NULL);
>      ret = avcodec_decode_video2(ist->st->codec,
>                                  decoded_frame, got_output, pkt);
> -    update_benchmark("decode_video %d.%d", ist->file_index, ist->st->index);
> +    update_benchmark(&vid_dec_bench, "decode_video %d.%d", ist->file_index, ist->st->index);
>  
>      if (*got_output || ret<0 || pkt->size)
>          decode_error_stat[ret<0] ++;
> @@ -3256,19 +3334,31 @@ static int transcode(void)
>      return ret;
>  }
>  
> -
> -static int64_t getutime(void)
> +static int64_t getutime(BenchTimes *bin)
>  {
>  #if HAVE_GETRUSAGE
>      struct rusage rusage;
>  
>      getrusage(RUSAGE_SELF, &rusage);
> +    if( bin ){
> +        bin->user_time   = rusage.ru_utime.tv_sec + rusage.ru_utime.tv_usec * 1e-6;
> +        bin->system_time = rusage.ru_stime.tv_sec + rusage.ru_stime.tv_usec * 1e-6;
> +        bin->total_time  = get_hr_time();
> +    }
>      return (rusage.ru_utime.tv_sec * 1000000LL) + rusage.ru_utime.tv_usec;
>  #elif HAVE_GETPROCESSTIMES
>      HANDLE proc;
>      FILETIME c, e, k, u;
>      proc = GetCurrentProcess();
>      GetProcessTimes(proc, &c, &e, &k, &u);
> +    if( bin ){
> +        ULARGE_INTEGER uT, kT;
> +        uT.LowPart = u.dwLowDateTime, uT.HighPart = u.dwHighDateTime;
> +        kT.LowPart = k.dwLowDateTime, kT.HighPart = k.dwHighDateTime;
> +        bin->user_time   = uT.QuadPart * 100e-9;
> +        bin->system_time = kT.QuadPart * 100e-9;
> +        bin->total_time  = get_hr_time();
> +    }
>      return ((int64_t) u.dwHighDateTime << 32 | u.dwLowDateTime) / 10;
>  #else
>      return av_gettime();

> @@ -3304,6 +3394,21 @@ int main(int argc, char **argv)
>  
>      atexit(exit_program);
>  
> +#if HAVE_GETRUSAGE || HAVE_GETPROCESSTIMES
> +    init_hr_time();
> +    memset( &ffmpeg_bench, 0, sizeof(BenchTimes) );
> +    // initialise the overall benchmarking bin. Since this is done before the user's options are parsed
> +    // we clamp do_benchmark_most to 1 before calling update_benchmark. Restoring it afterwards is
> +    // just done for good practices' sake.
> +    { int dbm = do_benchmark_most;
> +        do_benchmark_most = 1;
> +        update_benchmark( &ffmpeg_bench, NULL );
> +        do_benchmark_most = dbm;
> +    }
> +#endif

all #if based code belongs in *timer.* not ffmpeg.c
timer.c/h should provide a clean API not something that requires
ifdefs to be used

[...]

-- 
Michael     GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB

Democracy is the form of government in which you can choose your dictator
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 198 bytes
Desc: Digital signature
URL: <http://ffmpeg.org/pipermail/ffmpeg-devel/attachments/20130518/3ae65938/attachment.asc>


More information about the ffmpeg-devel mailing list