added micro-benchmark

This commit is contained in:
Matej Sekoranja
2013-02-13 14:18:42 +01:00
parent 97b1848ba3
commit aa1a67d6c1
8 changed files with 569 additions and 1 deletions

View File

@@ -92,9 +92,16 @@ LIBSRCS += bitSetUtil.cpp
SRC_DIRS += $(PVDATA)/monitor
INC += monitor.h
SRC_DIRS += $(PVDATA)/mb
INC += mb.h
LIBSRCS += mb.cpp
LIBRARY=pvData
PROD_HOST += mb_stat
mb_stat_CXXFLAGS += -DPV_MB
mb_stat_SRCS += mb_stat.cpp
mb_stat_LIBS += pvData Com
LIBRARY = pvData
pvData_LIBS += Com
include $(TOP)/configure/RULES

301
pvDataApp/mb/mb.cpp Normal file
View File

@@ -0,0 +1,301 @@
#include "mb.h"
#include <time.h>
#include <map>
#include <math.h>
#include <cstdlib>
#include <fstream>
#include <sstream>
#include <unistd.h>
#include <iomanip>
#include <epicsMutex.h>
#if defined(__APPLE__)
#include <mach/mach_time.h>
uint64_t MBTime()
{
return mach_absolute_time();
}
#else
uint64_t MBTime()
{
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
return static_cast<uint64_t>(ts.tv_sec) * 1000000000 + static_cast<uint64_t>(ts.tv_nsec);
}
#endif
void MBPointAdd(MBEntity &e, intptr_t id, uint8_t stage)
{
// no copy and no MBPoint init solution
const std::size_t ix = ATOMIC_GET_AND_INCREMENT(e.pos);
MBPoint& p = e.points[ix];
p.id = id; p.stage = stage;
p.time = MBTime();
}
void MBCSVExport(MBEntity &e, std::ostream &o)
{
const std::size_t len = ATOMIC_GET(e.pos);
for (std::size_t i = 0; i < len; i++)
{
MBPoint& p = e.points[i];
o << p.id << ',' << static_cast<uint32_t>(p.stage) << ',' << p.time << std::endl;
}
}
// NOTE: this method is not thread-safe
void MBCSVImport(MBEntity &e, std::istream &i)
{
std::string line;
char c;
e.points.resize(0);
size_t lc = 0;
while (getline(i,line))
{
lc++;
std::istringstream is(line);
MBPoint p;
is >> p.id >> c;
uint32_t s; is >> s >> c; p.stage = s;
is >> p.time;
if (is.good() || is.eof())
e.points.push_back(p);
else
{
std::cerr << "failed to parse line " << lc << ": \"" << line << "\"" << std::endl;
}
}
e.pos = e.points.size();
}
void MBNormalize(MBEntity &e)
{
std::map<intptr_t, uint64_t> lastTime;
const std::size_t len = ATOMIC_GET(e.pos);
for (std::size_t i = 0; i < len; i++)
{
MBPoint& p = e.points[i];
if (p.stage == 0)
lastTime[p.id] = p.time;
std::map<intptr_t, uint64_t>::iterator last = lastTime.find(p.id);
if (last == lastTime.end())
{
std::cerr << "no 0 stage for " << e.name << ", id = " << p.id << std::endl;
p.time = -1; // TODO error?!
}
else
{
uint64_t lt = last->second;
last->second = p.time;
p.time -= lt;
}
}
}
struct MBStatistics
{
std::size_t count;
uint64_t min;
uint64_t max;
uint64_t rms;
MBStatistics() :
count(0),
min(-1),
max(0),
rms(0.0)
{}
MBStatistics(uint64_t sample) :
count(1),
min(sample),
max(sample),
rms(sample*sample)
{
};
void addSample(uint64_t sample)
{
count++;
if (sample < min) min = sample;
if (sample > max) max = sample;
rms += sample*sample;
};
};
typedef std::map<uint8_t, MBStatistics> StatsMapPerStage;
void MBStats(MBEntity &e, std::ostream &o)
{
MBNormalize(e);
StatsMapPerStage stats;
const std::size_t len = ATOMIC_GET(e.pos);
for (std::size_t i = 0; i < len; i++)
{
MBPoint& p = e.points[i];
// first stage is start time, skip
if (p.stage == 0)
continue;
StatsMapPerStage::iterator s = stats.find(p.stage);
if (s == stats.end())
stats[p.stage] = MBStatistics(p.time);
else
s->second.addSample(p.time);
}
uint64_t smin = 0;
uint64_t smax = 0;
double srms = 0;
for (StatsMapPerStage::iterator i = stats.begin();
i != stats.end();
i++)
{
smin += i->second.min;
smax += i->second.max;
double rrms = sqrt(i->second.rms/(double)i->second.count);
srms += rrms;
o << "stage " << std::setw(4) << static_cast<uint32_t>(i->first)
<< ": min = " << std::setw(16) << i->second.min
<< ", max = " << std::setw(16) << i->second.max
<< ", rms = " << std::setw(16) << static_cast<uint64_t>(rrms) << std::endl;
}
o << std::string(82,'-') << std::endl;
o << "stage " << std::setw(4) << "sum"
<< ": min = " << std::setw(16) << smin
<< ", max = " << std::setw(16) << smax
<< ", rms = " << std::setw(16) << static_cast<uint64_t>(srms) << std::endl;
}
typedef std::vector<MBEntity*> EntitiesVector;
static int nifty_counter;
static epicsMutex* MBMutex;
// The counter is initialized at load-time, i.e., before any of the static objects are initialized.
MBMutexInitializer::MBMutexInitializer ()
{
if (0 == nifty_counter++)
{
// Initialize static members.
MBMutex = new epicsMutex();
}
}
MBMutexInitializer::~MBMutexInitializer ()
{
if (0 == --nifty_counter)
{
// Clean-up.
delete MBMutex;
}
}
class MutexLock {
public:
explicit MutexLock(epicsMutex &m) :
mutexPtr(m),
locked(true)
{
mutexPtr.lock();
}
~MutexLock()
{
unlock();
}
void lock()
{
if (!locked)
{
mutexPtr.lock();
locked = true;
}
}
void unlock()
{
if (locked)
{
mutexPtr.unlock();
locked = false;
}
}
private:
epicsMutex &mutexPtr;
bool locked;
};
void MBEntityRegister(MBEntity *e)
{
MutexLock lock(*MBMutex);
static EntitiesVector MBEntities;
if (e)
{
MBEntities.push_back(e);
}
else
{
for(EntitiesVector::const_iterator i = MBEntities.begin();
i != MBEntities.end();
i++)
{
// skip empty entities
if ((*i)->pos)
{
char fileName[128];
char* path = getenv("MB_OUTPUT_DIR");
if (path == 0) path = const_cast<char*>(".");
snprintf(fileName, 128, "%s/mb_%s_%d.csv", path, (*i)->name.c_str(), getpid());
std::ofstream out(fileName);
if (out.is_open())
{
MBCSVExport(*(*i), out);
out.close();
}
else
{
std::cerr << "failed to create a file " << fileName << ", skipping..." << std::endl;
}
}
}
}
}
void MBAtExit()
{
MBEntityRegister(0);
}
void MBInit()
{
MutexLock lock(*MBMutex);
static bool inited = false;
if (!inited)
{
inited = true;
atexit(MBAtExit);
}
}

130
pvDataApp/mb/mb.h Normal file
View File

@@ -0,0 +1,130 @@
#ifndef _MB_H_
#define _MB_H_
#include <string>
#include <vector>
#include <stdint.h>
#include <iostream>
#include <epicsVersion.h>
#if EPICS_VERSION_INT >= VERSION_INT(3,15,0,0)
//#include <epicsAtomic.h>
// epicsAtomic only support int and size_t type
// TODO fully fenced (not optimal)
#define ATOMIC_GET_AND_INCREMENT(VAR) __sync_fetch_and_add(&VAR, 1)
#define ATOMIC_SET_ZERO(VAR) __sync_fetch_and_and(&VAR, 0)
#define ATOMIC_GET(VAR) __sync_fetch_and_or(&VAR, 0)
#else
// try to get away with GCC internals, or build will simply fail
#define ATOMIC_GET_AND_INCREMENT(VAR) __sync_fetch_and_add(&VAR, 1)
#define ATOMIC_SET_ZERO(VAR) __sync_fetch_and_and(&VAR, 0)
#define ATOMIC_GET(VAR) __sync_fetch_and_or(&VAR, 0)
#endif
static class MBMutexInitializer {
public:
MBMutexInitializer ();
~MBMutexInitializer ();
} mbStaticMutexInitializer; // Note object here in the header.
struct MBPoint
{
intptr_t id;
uint8_t stage;
uint64_t time;
MBPoint() {}
MBPoint(intptr_t _id, uint8_t _stage) : id(_id), stage(_stage) {}
};
struct MBEntity;
extern void MBEntityRegister(MBEntity *e);
typedef std::vector<MBPoint> MBPointType;
struct MBEntity
{
std::string name;
MBPointType points;
volatile std::size_t pos;
volatile intptr_t auto_id;
MBEntity(const std::string &name_, std::size_t size) : name(name_)
{
// init vector at the beginning
points.resize(size);
ATOMIC_SET_ZERO(pos);
ATOMIC_SET_ZERO(auto_id);
MBEntityRegister(this);
}
};
extern uint64_t MBTime();
extern void MBPointAdd(MBEntity &e, intptr_t id, uint8_t stage);
extern void MBCSVExport(MBEntity &e, std::ostream &o);
extern void MBCSVImport(MBEntity &e, std::istream &i);
extern void MBStats(MBEntity &e, std::ostream &o);
extern void MBNormalize(MBEntity &e);
extern void MBInit();
#if PV_MB
#define MB_NAME(NAME) g_MB_##NAME
#define MB_DECLARE(NAME, SIZE) MBEntity MB_NAME(NAME)(#NAME, SIZE)
#define MB_DECLARE_EXTERN(NAME) extern MBEntity MB_NAME(NAME)
#define MB_POINT_ID(NAME, ID, STAGE) MBPointAdd(MB_NAME(NAME), ID, STAGE)
#define MB_INC_AUTO_ID(NAME) ATOMIC_GET_AND_INCREMENT(MB_NAME(NAME).auto_id)
#define MB_POINT(NAME, STAGE) MBPointAdd(MB_NAME(NAME), MB_NAME(NAME).auto_id, STAGE)
#define MB_POINT_CONDITIONAL(NAME, STAGE, COND) if (COND) MBPointAdd(MB_NAME(NAME), MB_NAME(NAME).auto_id, STAGE)
#define MB_NORMALIZE(NAME) MBNormalize(MB_NAME(NAME))
#define MB_STATS(NAME, STREAM) MBStats(MB_NAME(NAME), STREAM)
#define MB_CSV_EXPORT(NAME, STREAM) MBCSVExport(MB_NAME(NAME), STREAM)
#define MB_CSV_IMPORT(NAME, STREAM) MBCSVImport(MB_NAME(NAME), STREAM)
#define MB_PRINT(NAME, STREAM) MB_CSV_EXPORT(NAME, STREAM)
#define MB_INIT MBInit()
#else
#define MB_DECLARE(NAME, SIZE)
#define MB_DECLARE_EXTERN(NAME)
#define MB_POINT_ID(NAME, ID, STAGE)
#define MB_INC_AUTO_ID(NAME)
#define MB_POINT(NAME, STAGE)
#define MB_POINT_CONDITIONAL(NAME, STAGE, COND)
#define MB_NORMALIZE(NAME)
#define MB_STATS(NAME, STREAM)
#define MB_CSV_EXPORT(NAME, STREAM)
#define MB_CSV_IMPORT(NAME, STREAM)
#define MB_PRINT(NAME, STREAM)
#define MB_INIT
#endif
#endif

50
pvDataApp/mb/mb_stat.cpp Normal file
View File

@@ -0,0 +1,50 @@
#include <iostream>
#include <fstream>
#include "mb.h"
#include <cstring>
MB_DECLARE(e, 64000);
// TODO command line options
int main(int argc, char** argv)
{
// norm hack
bool normalizeOnly = false;
if (argc == 3 && strcmp(argv[2],"-n")==0)
{
argc = 2;
normalizeOnly = true;
}
if (argc != 2)
{
std::cerr << "usage: " << argv[0] << " <mb CSV file>" << std::endl;
return -1;
}
char * fileName = argv[1];
std::ifstream in(fileName);
if (in.is_open())
{
MB_CSV_IMPORT(e, in);
in.close();
if (normalizeOnly)
{
MB_NORMALIZE(e);
MB_PRINT(e, std::cout);
}
else
MB_STATS(e, std::cout);
}
else
{
std::cerr << "failed to open a file " << fileName << ", skipping..." << std::endl;
return -1;
}
return 0;
}

View File

@@ -5,5 +5,6 @@ DIRS += pv
DIRS += property
DIRS += monitor
DIRS += capi
DIRS += mb
include $(TOP)/configure/RULES_DIRS

13
testApp/mb/Makefile Normal file
View File

@@ -0,0 +1,13 @@
TOP=../..
include $(TOP)/configure/CONFIG
PROD_HOST += mb_test
mb_test_CXXFLAGS += -DPV_MB
mb_test_SRCS += mb_test.cpp
mb_test_LIBS += pvData Com
include $(TOP)/configure/RULES
#----------------------------------------
# ADD RULES AFTER THIS LINE

49
testApp/mb/README Normal file
View File

@@ -0,0 +1,49 @@
channelGet
0 - client channelGet->get()
1 - client channelGet->serialize (start)
2 - client channelGet->serialize (end)
3 - server channelGet->deserialize request (start)
4 - server channelGet->deserialize request (end)
5 - server channelGet->getDone()
6 - server channelGet->serialize response (start)
7 - server channelGet->serialize response (end)
8 - client channelGet->deserialize (start)
9 - client channelGet->deserialize (end), just before channelGet->getDone() is called
MB_DECLARE_EXPORT(channelGet);
# 10000 - max size
MB_DECLARE(channelGet, 10000);
# in main() to install atexit() hook
MB_INIT;
MB_INC_AUTO_ID(channelGet);
MB_POINT(channelGet, 0);
MB_POINT(channelGet, 1);
MB_POINT(channelGet, 2);
MB_INC_AUTO_ID(channelGet);
MB_POINT(channelGet, 3);
MB_POINT(channelGet, 4);
MB_POINT(channelGet, 5);
MB_POINT(channelGet, 6);
MB_POINT(channelGet, 7);
MB_POINT(channelGet, 8);
MB_POINT(channelGet, 9);
----
cat mb* | sort < mb.csv
mb_stat mb.csv
mb_stat mb.csv -n | grep ,2, | cut -d , -f 3 | gnuplot -p -e "plot '-'"
---

17
testApp/mb/mb_test.cpp Normal file
View File

@@ -0,0 +1,17 @@
#include <iostream>
#include <pv/mb.h>
MB_DECLARE_EXTERN(test);
MB_DECLARE(test, 1000);
int main()
{
MB_INIT;
for (int i = 0; i < 10; i++)
for (int j = 0; j < 100; j++)
MB_POINT_ID(test, i, j);
return 0;
}