2016-05-20 14:49:39 +00:00
// Copyright 2016 Ismael Jimenez Martinez. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Source project : https://github.com/ismaelJimenez/cpp.leastsq
2016-05-21 09:51:42 +00:00
// Adapted to be used with google benchmark
2016-05-20 14:49:39 +00:00
2016-05-25 20:57:52 +00:00
# include "benchmark/complexity.h"
2016-05-23 18:12:54 +00:00
# include "check.h"
2016-05-20 14:49:39 +00:00
# include <math.h>
2016-05-25 20:57:52 +00:00
namespace benchmark {
2016-05-20 14:49:39 +00:00
// Internal function to calculate the different scalability forms
2016-05-25 20:57:52 +00:00
std : : function < double ( int ) > FittingCurve ( BigO complexity ) {
2016-05-25 20:26:57 +00:00
switch ( complexity ) {
2016-05-25 20:57:52 +00:00
case oN :
2016-05-25 20:26:57 +00:00
return [ ] ( int n ) { return n ; } ;
2016-05-25 20:57:52 +00:00
case oNSquared :
2016-05-25 20:26:57 +00:00
return [ ] ( int n ) { return n * n ; } ;
2016-05-25 20:57:52 +00:00
case oNCubed :
2016-05-25 20:26:57 +00:00
return [ ] ( int n ) { return n * n * n ; } ;
2016-05-25 20:57:52 +00:00
case oLogN :
2016-05-25 20:26:57 +00:00
return [ ] ( int n ) { return log2 ( n ) ; } ;
2016-05-25 20:57:52 +00:00
case oNLogN :
2016-05-25 20:26:57 +00:00
return [ ] ( int n ) { return n * log2 ( n ) ; } ;
2016-05-25 20:57:52 +00:00
case o1 :
2016-05-25 20:26:57 +00:00
default :
return [ ] ( int ) { return 1 ; } ;
}
}
2016-05-25 20:57:52 +00:00
// Function to to return an string for the calculated complexity
std : : string GetBigOString ( BigO complexity ) {
2016-05-23 18:40:41 +00:00
switch ( complexity ) {
2016-05-25 20:57:52 +00:00
case oN :
2016-05-25 20:26:57 +00:00
return " * N " ;
2016-05-25 20:57:52 +00:00
case oNSquared :
2016-05-25 20:26:57 +00:00
return " * N**2 " ;
2016-05-25 20:57:52 +00:00
case oNCubed :
2016-05-25 20:26:57 +00:00
return " * N**3 " ;
2016-05-25 20:57:52 +00:00
case oLogN :
2016-05-25 20:26:57 +00:00
return " * lgN " ;
2016-05-25 20:57:52 +00:00
case oNLogN :
2016-05-25 20:26:57 +00:00
return " * NlgN " ;
2016-05-25 20:57:52 +00:00
case o1 :
2016-05-25 20:26:57 +00:00
return " * 1 " ;
2016-05-23 18:40:41 +00:00
default :
2016-05-25 20:26:57 +00:00
return " " ;
2016-05-23 18:40:41 +00:00
}
2016-05-20 14:49:39 +00:00
}
2016-05-25 20:26:57 +00:00
// Find the coefficient for the high-order term in the running time, by minimizing the sum of squares of relative error, for the fitting curve given on the lambda expresion.
// - n : Vector containing the size of the benchmark tests.
// - time : Vector containing the times for the benchmark tests.
// - fitting_curve : lambda expresion (e.g. [](int n) {return n; };).
2016-05-24 20:25:59 +00:00
// For a deeper explanation on the algorithm logic, look the README file at
// http://github.com/ismaelJimenez/Minimal-Cpp-Least-Squared-Fit
2016-05-20 14:49:39 +00:00
2016-05-25 20:57:52 +00:00
// This interface is currently not used from the oustide, but it has been provided
// for future upgrades. If in the future it is not needed to support Cxx03, then
// all the calculations could be upgraded to use lambdas because they are more
// powerful and provide a cleaner inferface than enumerators, but complete
// implementation with lambdas will not work for Cxx03 (e.g. lack of std::function).
// In case lambdas are implemented, the interface would be like :
// -> Complexity([](int n) {return n;};)
// and any arbitrary and valid equation would be allowed, but the option to calculate
// the best fit to the most common scalability curves will still be kept.
2016-05-25 20:26:57 +00:00
LeastSq CalculateLeastSq ( const std : : vector < int > & n ,
const std : : vector < double > & time ,
std : : function < double ( int ) > fitting_curve ) {
2016-05-23 18:40:41 +00:00
double sigma_gn = 0 ;
double sigma_gn_squared = 0 ;
double sigma_time = 0 ;
double sigma_time_gn = 0 ;
// Calculate least square fitting parameter
for ( size_t i = 0 ; i < n . size ( ) ; + + i ) {
2016-05-25 20:26:57 +00:00
double gn_i = fitting_curve ( n [ i ] ) ;
2016-05-23 18:40:41 +00:00
sigma_gn + = gn_i ;
sigma_gn_squared + = gn_i * gn_i ;
sigma_time + = time [ i ] ;
sigma_time_gn + = time [ i ] * gn_i ;
}
LeastSq result ;
2016-05-24 20:25:59 +00:00
// Calculate complexity.
2016-05-25 20:26:57 +00:00
result . coef = sigma_time_gn / sigma_gn_squared ;
2016-05-23 18:40:41 +00:00
// Calculate RMS
double rms = 0 ;
for ( size_t i = 0 ; i < n . size ( ) ; + + i ) {
2016-05-25 20:26:57 +00:00
double fit = result . coef * fitting_curve ( n [ i ] ) ;
2016-05-23 18:40:41 +00:00
rms + = pow ( ( time [ i ] - fit ) , 2 ) ;
}
2016-05-24 20:25:59 +00:00
// Normalized RMS by the mean of the observed values
2016-05-25 20:26:57 +00:00
double mean = sigma_time / n . size ( ) ;
2016-05-24 20:25:59 +00:00
result . rms = sqrt ( rms / n . size ( ) ) / mean ;
2016-05-23 18:40:41 +00:00
return result ;
2016-05-20 14:49:39 +00:00
}
2016-05-24 20:25:59 +00:00
// Find the coefficient for the high-order term in the running time, by
// minimizing the sum of squares of relative error.
2016-05-23 18:12:54 +00:00
// - n : Vector containing the size of the benchmark tests.
// - time : Vector containing the times for the benchmark tests.
2016-05-24 20:25:59 +00:00
// - complexity : If different than oAuto, the fitting curve will stick to
// this one. If it is oAuto, it will be calculated the best
// fitting curve.
LeastSq MinimalLeastSq ( const std : : vector < int > & n ,
const std : : vector < double > & time ,
2016-05-25 20:57:52 +00:00
const BigO complexity ) {
2016-05-23 18:40:41 +00:00
CHECK_EQ ( n . size ( ) , time . size ( ) ) ;
CHECK_GE ( n . size ( ) , 2 ) ; // Do not compute fitting curve is less than two benchmark runs are given
2016-05-25 20:57:52 +00:00
CHECK_NE ( complexity , oNone ) ;
2016-05-23 18:40:41 +00:00
2016-05-25 20:26:57 +00:00
LeastSq best_fit ;
2016-05-25 20:57:52 +00:00
if ( complexity = = oAuto ) {
std : : vector < BigO > fit_curves = {
oLogN , oN , oNLogN , oNSquared , oNCubed } ;
2016-05-23 18:40:41 +00:00
2016-05-24 20:25:59 +00:00
// Take o1 as default best fitting curve
2016-05-25 20:57:52 +00:00
best_fit = CalculateLeastSq ( n , time , FittingCurve ( o1 ) ) ;
best_fit . complexity = o1 ;
2016-05-23 18:40:41 +00:00
// Compute all possible fitting curves and stick to the best one
for ( const auto & fit : fit_curves ) {
2016-05-25 20:26:57 +00:00
LeastSq current_fit = CalculateLeastSq ( n , time , FittingCurve ( fit ) ) ;
2016-05-24 20:25:59 +00:00
if ( current_fit . rms < best_fit . rms ) {
2016-05-23 18:40:41 +00:00
best_fit = current_fit ;
2016-05-25 20:26:57 +00:00
best_fit . complexity = fit ;
2016-05-24 20:25:59 +00:00
}
2016-05-23 18:40:41 +00:00
}
2016-05-25 20:26:57 +00:00
} else {
best_fit = CalculateLeastSq ( n , time , FittingCurve ( complexity ) ) ;
best_fit . complexity = complexity ;
2016-05-23 18:40:41 +00:00
}
2016-05-24 20:25:59 +00:00
2016-05-25 20:26:57 +00:00
return best_fit ;
2016-05-24 20:25:59 +00:00
}
2016-05-25 20:57:52 +00:00
} // end namespace benchmark