added micro-benchmark
This commit is contained in:
@@ -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
301
pvDataApp/mb/mb.cpp
Normal 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
130
pvDataApp/mb/mb.h
Normal 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
50
pvDataApp/mb/mb_stat.cpp
Normal 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;
|
||||
}
|
||||
|
||||
@@ -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
13
testApp/mb/Makefile
Normal 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
49
testApp/mb/README
Normal 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
17
testApp/mb/mb_test.cpp
Normal 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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user