KasmVNC/tests/decperf.cxx
2020-09-20 12:16:44 +00:00

241 lines
5.2 KiB
C++

/* Copyright 2015 Pierre Ossman <ossman@cendio.se> for Cendio AB
*
* This 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.
*
* This software 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 software; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
* USA.
*/
/*
* This program reads files produced by TightVNC's/TurboVNC's
* compare-encodings. It is basically a dump of the RFB protocol
* from the server side from the ServerInit message and forward.
* It is assumed that the client is using a bgr888 (LE) pixel
* format.
*/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <sys/time.h>
#include <rdr/Exception.h>
#include <rdr/FileInStream.h>
#include <rfb/CConnection.h>
#include <rfb/CMsgReader.h>
#include <rfb/PixelBuffer.h>
#include <rfb/PixelFormat.h>
#include "util.h"
// FIXME: Files are always in this format
static const rfb::PixelFormat filePF(32, 24, false, true, 255, 255, 255, 0, 8, 16);
class CConn : public rfb::CConnection {
public:
CConn(const char *filename);
~CConn();
virtual void setDesktopSize(int w, int h);
virtual void setPixelFormat(const rfb::PixelFormat& pf);
virtual void setCursor(int, int, const rfb::Point&, const rdr::U8*);
virtual void framebufferUpdateStart();
virtual void framebufferUpdateEnd();
virtual void setColourMapEntries(int, int, rdr::U16*);
virtual void bell();
virtual void serverCutText(const char*, rdr::U32);
public:
double cpuTime;
protected:
rdr::FileInStream *in;
};
CConn::CConn(const char *filename)
{
cpuTime = 0.0;
in = new rdr::FileInStream(filename);
setStreams(in, NULL);
// Need to skip the initial handshake
setState(RFBSTATE_INITIALISATION);
// That also means that the reader and writer weren't setup
setReader(new rfb::CMsgReader(this, in));
}
CConn::~CConn()
{
delete in;
}
void CConn::setDesktopSize(int w, int h)
{
CConnection::setDesktopSize(w, h);
setFramebuffer(new rfb::ManagedPixelBuffer(filePF, cp.width, cp.height));
}
void CConn::setPixelFormat(const rfb::PixelFormat& pf)
{
// Override format
CConnection::setPixelFormat(filePF);
}
void CConn::setCursor(int, int, const rfb::Point&, const rdr::U8*)
{
}
void CConn::framebufferUpdateStart()
{
CConnection::framebufferUpdateStart();
startCpuCounter();
}
void CConn::framebufferUpdateEnd()
{
CConnection::framebufferUpdateEnd();
endCpuCounter();
cpuTime += getCpuCounter();
}
void CConn::setColourMapEntries(int, int, rdr::U16*)
{
}
void CConn::bell()
{
}
void CConn::serverCutText(const char*, rdr::U32)
{
}
struct stats
{
double decodeTime;
double realTime;
};
static struct stats runTest(const char *fn)
{
CConn *cc;
struct timeval start, stop;
struct stats s;
gettimeofday(&start, NULL);
try {
cc = new CConn(fn);
} catch (rdr::Exception& e) {
fprintf(stderr, "Failed to open rfb file: %s\n", e.str());
exit(1);
}
try {
while (true)
cc->processMsg();
} catch (rdr::EndOfStream& e) {
} catch (rdr::Exception& e) {
fprintf(stderr, "Failed to run rfb file: %s\n", e.str());
exit(1);
}
gettimeofday(&stop, NULL);
s.decodeTime = cc->cpuTime;
s.realTime = (double)stop.tv_sec - start.tv_sec;
s.realTime += ((double)stop.tv_usec - start.tv_usec)/1000000.0;
delete cc;
return s;
}
static void sort(double *array, int count)
{
bool sorted;
int i;
do {
sorted = true;
for (i = 1;i < count;i++) {
if (array[i-1] > array[i]) {
double d;
d = array[i];
array[i] = array[i-1];
array[i-1] = d;
sorted = false;
}
}
} while (!sorted);
}
static const int runCount = 9;
int main(int argc, char **argv)
{
int i;
struct stats runs[runCount];
double values[runCount], dev[runCount];
double median, meddev;
if (argc != 2) {
printf("Syntax: %s <rfb file>\n", argv[0]);
return 1;
}
// Warmup
runTest(argv[1]);
// Multiple runs to get a good average
for (i = 0;i < runCount;i++)
runs[i] = runTest(argv[1]);
// Calculate median and median deviation for CPU usage
for (i = 0;i < runCount;i++)
values[i] = runs[i].decodeTime;
sort(values, runCount);
median = values[runCount/2];
for (i = 0;i < runCount;i++)
dev[i] = fabs((values[i] - median) / median) * 100;
sort(dev, runCount);
meddev = dev[runCount/2];
printf("CPU time: %g s (+/- %g %%)\n", median, meddev);
// And for CPU core usage
for (i = 0;i < runCount;i++)
values[i] = runs[i].decodeTime / runs[i].realTime;
sort(values, runCount);
median = values[runCount/2];
for (i = 0;i < runCount;i++)
dev[i] = fabs((values[i] - median) / median) * 100;
sort(dev, runCount);
meddev = dev[runCount/2];
printf("Core usage: %g (+/- %g %%)\n", median, meddev);
return 0;
}