QGIS/tests/bench/qgsbench.cpp

358 lines
10 KiB
C++
Raw Normal View History

2011-11-20 13:43:21 +01:00
/***************************************************************************
qgsbench.cpp - Benchmark
-------------------
begin : 2011-11-15
copyright : (C) 2011 Radim Blazek
email : radim dot blazek at gmail dot com
***************************************************************************/
/***************************************************************************
* *
* 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; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include <QtGlobal>
2011-11-20 13:43:21 +01:00
#include <cmath>
#include <ctime>
#include <iostream>
2018-06-05 11:18:59 +10:00
#include <cstdio>
#ifndef Q_OS_WIN
2011-11-20 13:43:21 +01:00
#include <sys/resource.h>
#endif
2018-06-05 11:18:59 +10:00
#include <ctime>
#include <cmath>
2011-11-20 13:43:21 +01:00
#include <QFile>
#include <QFileInfo>
#include <QPainter>
#include <QSettings>
#include <QString>
#include <QTextStream>
#include <QTime>
2013-04-06 10:53:53 +02:00
#ifndef QGSVERSION
#include "qgsversion.h"
#endif
2011-11-20 13:43:21 +01:00
#include "qgsbench.h"
#include "qgslogger.h"
#include "qgsmaprendererparalleljob.h"
#include "qgsmaprenderersequentialjob.h"
2011-11-20 13:43:21 +01:00
#include "qgsproject.h"
const char *pre[] = { "user", "sys", "total", "wall" };
2011-11-20 13:43:21 +01:00
#ifdef Q_OS_WIN
// slightly adapted from http://anoncvs.postgresql.org/cvsweb.cgi/pgsql/src/port/getrusage.c?rev=1.18;content-type=text%2Fplain
2015-06-10 13:59:08 +02:00
#include <winsock2.h>
#include <errno.h>
#define RUSAGE_SELF 0
struct rusage
{
struct timeval ru_utime; /* user time used */
struct timeval ru_stime; /* system time used */
};
/*-------------------------------------------------------------------------
*
* getrusage.c
* get information about resource utilisation
*
* Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/port/getrusage.c,v 1.18 2010-01-02 16:58:13 momjian Exp $
*
*-------------------------------------------------------------------------
*/
2017-03-03 09:31:05 +01:00
int getrusage( int who, struct rusage *rusage )
{
FILETIME starttime;
FILETIME exittime;
FILETIME kerneltime;
FILETIME usertime;
ULARGE_INTEGER li;
if ( who != RUSAGE_SELF )
{
/* Only RUSAGE_SELF is supported in this implementation for now */
errno = EINVAL;
return -1;
}
2015-12-16 12:15:00 +01:00
if ( !rusage )
{
errno = EFAULT;
return -1;
}
memset( rusage, 0, sizeof( struct rusage ) );
if ( GetProcessTimes( GetCurrentProcess(),
&starttime, &exittime, &kerneltime, &usertime ) == 0 )
{
// _dosmaperr(GetLastError());
return -1;
}
/* Convert FILETIMEs (0.1 us) to struct timeval */
memcpy( &li, &kerneltime, sizeof( FILETIME ) );
li.QuadPart /= 10L; /* Convert to microseconds */
rusage->ru_stime.tv_sec = li.QuadPart / 1000000L;
rusage->ru_stime.tv_usec = li.QuadPart % 1000000L;
memcpy( &li, &usertime, sizeof( FILETIME ) );
li.QuadPart /= 10L; /* Convert to microseconds */
rusage->ru_utime.tv_sec = li.QuadPart / 1000000L;
rusage->ru_utime.tv_usec = li.QuadPart % 1000000L;
return 0;
}
#endif
QgsBench::QgsBench( int width, int height, int iterations )
: mWidth( width )
2017-03-03 09:31:05 +01:00
, mHeight( height )
, mIterations( iterations )
, mSetExtent( false )
, mUserStart( 0.0 )
, mSysStart( 0.0 )
, mParallel( false )
2011-11-20 13:43:21 +01:00
{
QgsDebugMsg( QString( "mIterations = %1" ).arg( mIterations ) );
2017-04-05 09:53:50 +10:00
connect( QgsProject::instance(), &QgsProject::readProject,
this, &QgsBench::readProject );
2011-11-20 13:43:21 +01:00
}
2017-03-03 09:31:05 +01:00
bool QgsBench::openProject( const QString &fileName )
2011-11-20 13:43:21 +01:00
{
if ( ! QgsProject::instance()->read( fileName ) )
2011-11-20 13:43:21 +01:00
{
return false;
}
mLogMap.insert( QStringLiteral( "project" ), fileName );
2011-11-20 13:43:21 +01:00
return true;
}
void QgsBench::readProject( const QDomDocument &doc )
{
QDomNodeList nodes = doc.elementsByTagName( QStringLiteral( "mapcanvas" ) );
2011-11-20 13:43:21 +01:00
if ( nodes.count() )
{
QDomNode node = nodes.item( 0 );
mMapSettings.readXml( node );
2011-11-20 13:43:21 +01:00
}
else
{
fprintf( stderr, "Cannot read mapcanvas from project\n" );
}
}
2017-03-03 09:31:05 +01:00
void QgsBench::setExtent( const QgsRectangle &extent )
2011-11-20 13:43:21 +01:00
{
mExtent = extent;
mSetExtent = true;
}
void QgsBench::render()
{
QgsDebugMsg( "extent: " + mMapSettings.extent().toString() );
2011-11-20 13:43:21 +01:00
2017-03-03 09:31:05 +01:00
QMap<QString, QgsMapLayer *> layersMap = QgsProject::instance()->mapLayers();
2011-11-20 13:43:21 +01:00
mMapSettings.setLayers( layersMap.values() );
2011-11-20 13:43:21 +01:00
if ( mSetExtent )
{
mMapSettings.setExtent( mExtent );
2011-11-20 13:43:21 +01:00
}
// Maybe in future
//outputCRS = QgsCrsCache::instance()->crsByAuthId( crsId );
2011-11-20 13:43:21 +01:00
//mMapRenderer->setMapUnits( outputCRS.mapUnits() );
//mMapRenderer->setDestinationCrs( outputCRS );
2013-04-14 21:11:20 +02:00
// Enable labeling
mMapSettings.setFlag( QgsMapSettings::DrawLabeling );
2011-11-20 13:43:21 +01:00
mMapSettings.setOutputSize( QSize( mWidth, mHeight ) );
2011-11-20 13:43:21 +01:00
// TODO: do we need the other QPainter flags?
mMapSettings.setFlag( QgsMapSettings::Antialiasing, mRendererHints.testFlag( QPainter::Antialiasing ) );
2011-11-20 13:43:21 +01:00
for ( int i = 0; i < mIterations; i++ )
{
2017-03-03 09:31:05 +01:00
QgsMapRendererQImageJob *job = nullptr;
if ( mParallel )
job = new QgsMapRendererParallelJob( mMapSettings );
else
job = new QgsMapRendererSequentialJob( mMapSettings );
2011-11-20 13:43:21 +01:00
start();
job->start();
job->waitForFinished();
2011-11-20 13:43:21 +01:00
elapsed();
mImage = job->renderedImage();
delete job;
2011-11-20 13:43:21 +01:00
}
mLogMap.insert( QStringLiteral( "iterations" ), mTimes.size() );
mLogMap.insert( QStringLiteral( "revision" ), QGSVERSION );
2011-11-20 13:43:21 +01:00
// Calc stats: user, sys, total
2018-06-16 08:43:27 +10:00
double min[4] = {std::numeric_limits<double>::max()};
double max[4] = { std::numeric_limits<double>::lowest()};
2013-04-04 23:45:08 +02:00
double stdev[4] = {0.};
double maxdev[4] = {0.};
double avg[4] = {0.};
2011-11-20 13:43:21 +01:00
2013-04-04 23:45:08 +02:00
for ( int t = 0; t < 4; t++ )
2011-11-20 13:43:21 +01:00
{
for ( int i = 0; i < mTimes.size(); i++ )
{
avg[t] += mTimes.at( i )[t];
2011-11-20 13:43:21 +01:00
if ( i == 0 || mTimes.at( i )[t] < min[t] ) min[t] = mTimes.at( i )[t];
if ( i == 0 || mTimes.at( i )[t] > max[t] ) max[t] = mTimes.at( i )[t];
2011-11-20 13:43:21 +01:00
}
avg[t] /= mTimes.size();
}
QMap<QString, QVariant> timesMap;
2013-04-04 23:45:08 +02:00
for ( int t = 0; t < 4; t++ )
2011-11-20 13:43:21 +01:00
{
if ( mIterations > 1 )
{
for ( int i = 0; i < mTimes.size(); i++ )
{
double d = std::fabs( avg[t] - mTimes.at( i )[t] );
2017-08-25 02:53:39 +10:00
stdev[t] += std::pow( d, 2 );
2011-11-20 13:43:21 +01:00
if ( i == 0 || d > maxdev[t] ) maxdev[t] = d;
}
2017-08-25 03:22:15 +10:00
stdev[t] = std::sqrt( stdev[t] / mTimes.size() );
2011-11-20 13:43:21 +01:00
}
QMap<QString, QVariant> map;
map.insert( QStringLiteral( "min" ), min[t] );
map.insert( QStringLiteral( "max" ), max[t] );
map.insert( QStringLiteral( "avg" ), avg[t] );
map.insert( QStringLiteral( "stdev" ), stdev[t] );
map.insert( QStringLiteral( "maxdev" ), maxdev[t] );
2011-11-20 13:43:21 +01:00
timesMap.insert( pre[t], map );
}
mLogMap.insert( QStringLiteral( "times" ), timesMap );
2011-11-20 13:43:21 +01:00
}
2017-03-03 09:31:05 +01:00
void QgsBench::saveSnapsot( const QString &fileName )
2011-11-20 13:43:21 +01:00
{
// If format is 0, QImage will attempt to guess the format by looking at fileName's suffix.
mImage.save( fileName );
2011-11-20 13:43:21 +01:00
}
2017-03-03 09:31:05 +01:00
void QgsBench::printLog( const QString &printTime )
2011-11-20 13:43:21 +01:00
{
std::cout << "iterations: " << mLogMap[QStringLiteral( "iterations" )].toString().toAscii().constData() << std::endl;
2011-11-20 13:43:21 +01:00
bool validPrintTime = false;
for ( int x = 0; x < 4; ++x )
if ( printTime == pre[x] )
validPrintTime = true;
if ( !validPrintTime )
{
std::cout << "invalid --print option: " << printTime.toAscii().data() << std::endl;
return;
}
QMap<QString, QVariant> timesMap = mLogMap[QStringLiteral( "times" )].toMap();
QMap<QString, QVariant> totalMap = timesMap[printTime].toMap();
2011-11-20 13:43:21 +01:00
QMap<QString, QVariant>::iterator i = totalMap.begin();
while ( i != totalMap.end() )
{
QString s = printTime + '_' + i.key() + ": " + i.value().toString();
2011-11-20 13:43:21 +01:00
std::cout << s.toAscii().constData() << std::endl;
++i;
}
}
2017-03-03 09:31:05 +01:00
QString QgsBench::serialize( const QMap<QString, QVariant> &map, int level )
2011-11-20 13:43:21 +01:00
{
QStringList list;
QString space = QStringLiteral( " " ).repeated( level * 2 );
QString space2 = QStringLiteral( " " ).repeated( level * 2 + 2 );
QMap<QString, QVariant>::const_iterator i = map.constBegin();
while ( i != map.constEnd() )
2011-11-20 13:43:21 +01:00
{
switch ( static_cast< QMetaType::Type >( i.value().type() ) )
2011-11-20 13:43:21 +01:00
{
case QMetaType::Int:
list.append( space2 + '\"' + i.key() + "\": " + QStringLiteral( "%1" ).arg( i.value().toInt() ) );
2011-11-20 13:43:21 +01:00
break;
case QMetaType::Double:
list.append( space2 + '\"' + i.key() + "\": " + QStringLiteral( "%1" ).arg( i.value().toDouble(), 0, 'f', 3 ) );
2011-11-20 13:43:21 +01:00
break;
case QMetaType::QString:
list.append( space2 + '\"' + i.key() + "\": \"" + i.value().toString().replace( '\\', QLatin1String( "\\\\" ) ).replace( '\"', QLatin1String( "\\\"" ) ) + '\"' );
2011-11-20 13:43:21 +01:00
break;
2017-03-03 09:31:05 +01:00
//case QMetaType::QMap: QMap is not in QMetaType
2011-11-20 13:43:21 +01:00
default:
list.append( space2 + '\"' + i.key() + "\": " + serialize( i.value().toMap(), level + 1 ) );
2011-11-20 13:43:21 +01:00
break;
}
++i;
}
return space + "{\n" + list.join( QStringLiteral( ",\n" ) ) + '\n' + space + '}';
2011-11-20 13:43:21 +01:00
}
2017-03-03 09:31:05 +01:00
void QgsBench::saveLog( const QString &fileName )
2011-11-20 13:43:21 +01:00
{
QFile file( fileName );
if ( !file.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate ) )
2015-02-12 07:35:29 +11:00
return;
2011-11-20 13:43:21 +01:00
QTextStream out( &file );
out << serialize( mLogMap ).toAscii().constData() << '\n';
2011-11-20 13:43:21 +01:00
file.close();
}
void QgsBench::start()
{
struct rusage usage;
getrusage( RUSAGE_SELF, &usage );
mUserStart = usage.ru_utime.tv_sec + usage.ru_utime.tv_usec / 1000000.;
mSysStart = usage.ru_stime.tv_sec + usage.ru_stime.tv_usec / 1000000.;
2013-04-04 23:45:08 +02:00
mWallTime.start();
2011-11-20 13:43:21 +01:00
}
void QgsBench::elapsed()
{
struct rusage usage;
getrusage( RUSAGE_SELF, &usage );
double userEnd = usage.ru_utime.tv_sec + usage.ru_utime.tv_usec / 1000000.;
double sysEnd = usage.ru_stime.tv_sec + usage.ru_stime.tv_usec / 1000000.;
2013-04-04 23:45:08 +02:00
double *t = new double[4];
2011-11-20 13:43:21 +01:00
t[0] = userEnd - mUserStart;
t[1] = sysEnd - mSysStart;
t[2] = t[0] + t[1];
2013-04-04 23:45:08 +02:00
t[3] = mWallTime.elapsed() / 1000.;
2011-11-20 13:43:21 +01:00
mTimes.append( t );
}