
/*
 * --- GSMP-COPYRIGHT-NOTE-BEGIN ---
 * 
 * This copyright note is auto-generated by ./scripts/Create-CopyPatch.
 * Please add additional copyright information _after_ the line containing
 * the GSMP-COPYRIGHT-NOTE-END tag. Otherwise it might get removed by
 * the ./scripts/Create-CopyPatch script. Do not edit this copyright text!
 * 
 * GSMP: pcm/tests/Bench.cc
 * General Sound Manipulation Program is Copyright (C) 2000 - 2004
 *   Valentin Ziegler and René Rebe
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2. A copy of the GNU General
 * Public License can be found in the file LICENSE.
 * 
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANT-
 * ABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
 * Public License for more details.
 * 
 * --- GSMP-COPYRIGHT-NOTE-END ---
 */

#include <iostream>
#include <iomanip>

#include "Types.hh"

#include "Timer.hh"

using namespace std;
using namespace GSMP;

class basic_bench {
 public:
  basic_bench ()
  {
    static bool run;

    if (!run) {
      std::cout << std::left << std::setw (f1) << "Type"
		<< std::right
		<< std::setw (f2) << "time"
		<< std::setw (f3) << "rate"
		<< std::setw (f4) << "read"
		<< std::setw (f5) << "write"
	        << std::setw (f6) << "sum"
		<< std::endl;
      
      std::cout << std::left << std::setw (f1) << ""
		<< std::right
		<< std::setw (f2) << "ticks"
		<< std::setw (f3) << "MS/s"
		<< std::setw (f4) << "MB/s"
		<< std::setw (f5) << "MB/s"
                << std::setw (f6) << "MB/s"
		<< std::endl;
      print_line ();
      run = true;
    }
  }
  
  void print_line ()
  {
    std::cout << std::string (f1 + f2 + f3 + f4 + f5 + f6, '-') << std::endl;
  }
  
 protected:
  static const int f1 = 36;
  static const int f2 = 8;
  static const int f3 = 8;
  static const int f4 = 8;
  static const int f5 = 8;
  static const int f6 = 8;
};

template <typename T1, typename E1, typename T2, typename E2>
class bench : public basic_bench {
 public:
  bench (const std::string& i_info, int i_runs, T1 i_value = 120)
    : info (i_info), value (i_value), runs (i_runs)
  {
    runs -= runs % 4; // allign for unrolled loop ...
    
    no_access_run ();
    no_access_unrolled_run ();
    real_access_run ();
    real_access_unrolled_run ();
    
    print_line ();
  }

  void no_access_run ()
  {
    long long delta;
    double seconds;
    
    T2 v;
    
    timer.Reset ();
    for (int i = 0; i < runs; ++i) {
      v = PcmConvert<T1, T2, E1, E2>::Convert (value);
    }
    
    delta = timer.Delta ();
    seconds = (double) delta / timer.PerSecond ();
    
    print_results (info + " no acc.", delta, seconds);
  };
  
  void no_access_unrolled_run ()
  {
    long long delta;
    double seconds;
    
    T2 v;
    
    timer.Reset ();
    for (int i = 0; i < runs/4; ++i) {
      v = PcmConvert<T1, T2, E1, E2>::Convert (value);
      v = PcmConvert<T1, T2, E1, E2>::Convert (value);
      v = PcmConvert<T1, T2, E1, E2>::Convert (value);
      v = PcmConvert<T1, T2, E1, E2>::Convert (value);
    }
    delta = timer.Delta ();
    seconds = (double) delta / timer.PerSecond ();

    print_results (info + " no acc. unr.", delta, seconds);
  };
  
  void real_access_run ()
  {
    long long delta;
    double seconds;
    
    T1* src = new T1 [runs];
    T2* dest = new T2 [runs];

    timer.Reset ();
    for (int i = 0; i < runs; ++i)
      dest[i] = PcmConvert<T1, T2, E1, E2>::Convert (src[i]);
    delta = timer.Delta ();
    seconds = (double) delta / timer.PerSecond ();
    
    print_results (info + " real", delta, seconds);
    
    delete [] src;
    delete [] dest;
  };
  
  void real_access_unrolled_run ()
  {
    long long delta;
    double seconds;
    
    T1* src = new T1 [runs];
    T2* dest = new T2 [runs];
        
    T1* s = src;
    T2* d = dest;
    
    timer.Reset ();
    
    for (int i = 0; i < runs;) {
      *d++ = PcmConvert<T1, T2, E1, E2>::Convert (*s++); ++i;
      *d = PcmConvert<T1, T2, E1, E2>::Convert (*s++); ++i;
      *d = PcmConvert<T1, T2, E1, E2>::Convert (*s++); ++i;
      *d = PcmConvert<T1, T2, E1, E2>::Convert (*s++); ++i;
    }
    
    delta = timer.Delta ();
    seconds = (double) delta / timer.PerSecond ();

    print_results (info + " real unr.", delta, seconds);
    
    delete [] src;
    delete [] dest;
  };
  

 private:
  void print_results (const string& full_info, long long delta, double seconds)
  {
    const int mb = 1024 * 1024;

    std::cout << std::setprecision (2) << std::fixed
	      << std::left << std::setw (f1) << full_info
	      << std::right
	      << std::setw (f2) << delta
	      << std::setw (f3) << runs / seconds / mb
	      << std::setw (f4)
	      << (double) PcmTraits<T1>::Size() * runs / seconds / mb
	      << std::setw (f5)
	      << (double) PcmTraits<T2>::Size() * runs / seconds / mb
              << std::setw (f6)
              << (double) ( PcmTraits<T1>::Size() + PcmTraits<T2>::Size() )
                 * runs / seconds / mb
	      << std::endl;
  }
  
  Utility::TickTimer timer;
  const string& info;
  const T1 value;
  int runs;
};

int main()
{
  const int runs = 10000000;
  
  bench<int8_t, HostEndianessTraits,
    int32_t, HostEndianessTraits> ("int8 -> int32", runs);

  bench<int8_t, BigEndianessTraits,
    int32_t, LittleEndianessTraits> ("int8 -> int32 (endian)", runs);

  bench<int8_t, HostEndianessTraits,
    int64_t, HostEndianessTraits> ("int8 -> int64", runs);

  bench<int8_t, BigEndianessTraits,
    int64_t, LittleEndianessTraits> ("int8 -> int64 (endian)", runs);

  bench<int8_t, HostEndianessTraits,
    double, HostEndianessTraits> ("int8 -> double", runs);

  bench<int16_t, HostEndianessTraits,
    int32_t, HostEndianessTraits> ("int16 -> int32", runs);

  bench<int16_t, BigEndianessTraits,
    int32_t, LittleEndianessTraits> ("int16 -> int32 (endian)", runs);

  bench<int16_t, HostEndianessTraits,
    int64_t, HostEndianessTraits> ("int16 -> int64", runs);

  bench<int16_t, BigEndianessTraits,
    int64_t, LittleEndianessTraits> ("int16 -> int64 (endian)", runs);

  bench<int16_t, HostEndianessTraits,
    double, HostEndianessTraits> ("int16 -> double", runs);

  bench<int32_t, HostEndianessTraits,
    int32_t, HostEndianessTraits> ("int32 -> int32", runs);

  bench<int32_t, BigEndianessTraits,
    int32_t, LittleEndianessTraits> ("int32 -> int32 (endian)", runs);

  bench<int32_t, HostEndianessTraits,
    int8_t, HostEndianessTraits> ("int32 -> int8", runs);

  bench<int32_t, BigEndianessTraits,
    int8_t, LittleEndianessTraits> ("int32 -> int8 (endian)", runs);

  bench<int32_t, HostEndianessTraits,
    int16_t, HostEndianessTraits> ("int32 -> int16", runs);

  bench<int32_t, BigEndianessTraits,
    int16_t, LittleEndianessTraits> ("int32 -> int16 (endian)", runs);

  bench<int32_t, HostEndianessTraits,
    double, HostEndianessTraits> ("int32 -> double", runs);

  bench<int64_t, HostEndianessTraits,
    int8_t, HostEndianessTraits> ("int64 -> int8", runs);

  bench<int64_t, BigEndianessTraits,
    int8_t, LittleEndianessTraits> ("int64 -> int8 (endian)", runs);

  bench<int64_t, BigEndianessTraits,
    double, BigEndianessTraits> ("int64 -> double", runs);

  return 0;
}
