diff --git a/src/dataencode.cpp b/src/dataencode.cpp index 01be436..4301418 100644 --- a/src/dataencode.cpp +++ b/src/dataencode.cpp @@ -218,32 +218,6 @@ void from_wire(Buffer& buf, std::vector& descs, TypeStore& cache, uns } } -namespace { -template -void to_wire(Buffer& buf, const shared_array& varr) -{ - auto arr = varr.castTo(); - to_wire(buf, Size{arr.size()}); - for(auto i : range(arr.size())) { - to_wire(buf, C(arr[i])); - } -} - -template -void from_wire(Buffer& buf, shared_array& varr) -{ - Size slen{}; - from_wire(buf, slen); - shared_array arr(slen.size); - for(auto i : range(arr.size())) { - C temp{}; - from_wire(buf, temp); - arr[i] = temp; - } - varr = arr.freeze().template castTo(); -} -} - // serialize a field and all children (if Compound) static void to_wire_field(Buffer& buf, const FieldDesc* desc, const std::shared_ptr& store) diff --git a/src/pvaproto.h b/src/pvaproto.h index a865487..d26482f 100644 --- a/src/pvaproto.h +++ b/src/pvaproto.h @@ -21,6 +21,7 @@ #include #include +#include #include "utilpvt.h" namespace pvxs {namespace impl { @@ -408,6 +409,105 @@ void from_wire(Buffer& buf, Status& sts) } } +template +static inline +void to_wire(Buffer& buf, const shared_array& varr) +{ + auto arr = varr.castTo(); + to_wire(buf, Size{arr.size()}); + + if(std::is_pod::value) { + // optimize handling of types with fixed element size + + auto src = reinterpret_cast(arr.data()); + + for(size_t nremain = arr.size()*sizeof(C); nremain;) { + if(!buf.ensure(sizeof(C))) { + buf.fault(__FILE__, __LINE__); + break; + } + + // rounds down to element size. requires sizeof(C) by a power of 2 + size_t nbytes = std::min(buf.size(), nremain)&~(sizeof(C)-1u); + + if(buf.be==hostBE) { // already in native order, just copy + memcpy(buf.save(), src, nbytes); + + } else { // must swap byte order + auto dest = buf.save(); + + for(size_t i=0; i +static inline +void from_wire(Buffer& buf, shared_array& varr) +{ + Size slen{}; + from_wire(buf, slen); + shared_array arr(slen.size); + + if(std::is_pod::value) { + // optimize handling of types with fixed element size + + auto dest = reinterpret_cast(arr.data()); + + for(size_t nremain = arr.size()*sizeof(C); nremain;) { + if(!buf.ensure(sizeof(C))) { + buf.fault(__FILE__, __LINE__); + break; + } + + // rounds down to element size. requires sizeof(C) by a power of 2 + size_t nbytes = std::min(buf.size(), nremain)&~(sizeof(C)-1u); + + if(buf.be==hostBE) { // already in native order, just copy + memcpy(dest, buf.save(), nbytes); + + } else { // must swap byte order + auto src = buf.save(); + + for(size_t i=0; i(); +} + /* PVA Message Header * * 0 1 2 3 diff --git a/test/Makefile b/test/Makefile index 3fa7bab..1a4b541 100644 --- a/test/Makefile +++ b/test/Makefile @@ -90,6 +90,13 @@ TESTPROD_HOST += test1000 test1000_SRCS += test1000.cpp TESTS += test1000 +ifdef BASE_7_0 + +TESTPROD_HOST += benchdata +benchdata_SRCS += benchdata.cpp + +endif + ifdef BASE_3_15 DBDDEPENDS_FILES += testioc.dbd$(DEP) diff --git a/test/benchdata.cpp b/test/benchdata.cpp new file mode 100644 index 0000000..a267473 --- /dev/null +++ b/test/benchdata.cpp @@ -0,0 +1,176 @@ +/** + * Copyright - See the COPYRIGHT that is included with this distribution. + * pvxs is distributed subject to a Software License Agreement found + * in file LICENSE that is included with this distribution. + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include "pvaproto.h" +#include + +#include + +#include +#include +#include + +namespace { +using namespace pvxs; + +struct Sampler +{ + size_t nsamp =0; + double min=0.0, max=0.0, first=0.0; + double sum=0.0, sum2=0.0; + + void reset() { + *this = {}; // zero members + } + + void sample(double val) { + if(nsamp==0u) { + min = max = first = val; + + } else { + if(max < val) + max = val; + else if(min > val) + min = val; + } + sum += val; + sum2 += val*val; + nsamp++; + } + + double mean() const { + return sum/nsamp; + } + + double std() const { + return sqrt(sum2/nsamp - (sum/nsamp)*(sum/nsamp)); + } +}; + +std::ostream& operator<<(std::ostream& strm, const Sampler& samp) +{ + Restore R(strm); + strm<<"N="< can(niter); + + Sampler S; + + for(auto n : range(niter)) { + StopWatch W; + + (void)W.click(); + can[n] = protoype.cloneEmpty(); + S.sample(W.click()); + } + + testShow()< +void benchArraySerDes(bool be, const shared_array& arr) +{ + testDiag("%s<%s>() endian=%s", __func__, typeid (E).name(), be==hostBE ? "Host" : "Swap"); + + constexpr size_t niter = 1000u; + + shared_array scratch; + + evbuf ebuf(evbuffer_new()); + + Sampler Tser, Tdes; + + for(auto n : range(niter)) { + (void)n; + StopWatch W; + + { + EvOutBuf buf(be, ebuf.get()); + auto varr(arr.template castTo()); + (void)W.click(); + to_wire(buf, varr); + Tser.sample(W.click()); + } + + { + EvInBuf buf(be, ebuf.get()); + (void)W.click(); + from_wire(buf, scratch); + Tdes.sample(W.click()); + } + + auto iarr(arr.template castTo()); + auto iscratch(scratch.castTo()); + + if(iarr.size()!=iscratch.size() + || !std::equal(iarr.begin(), iarr.end(), iscratch.begin())) + testArrEq(arr.template castTo(), scratch.castTo()); + } + + testShow()<<" Ser "< temp(nelem); + for(auto n : range(temp.size())) { + temp[n] = n; + } + shared_array arr(temp.freeze()); + benchArraySerDes(hostBE, arr); + benchArraySerDes(!hostBE, arr); + } + testDiag("baseline unoptimized for a variable size element"); + { + shared_array temp(nelem); + for(auto n : range(temp.size())) { + temp[n] = SB()<<"test"< arr(temp.freeze()); + benchArraySerDes(hostBE, arr); + benchArraySerDes(!hostBE, arr); + } + return testDone(); +}