Compare commits

...

2 Commits

Author SHA1 Message Date
Srivats P
ed7bb8bd69 Fix build break due to incomplete changes 2019-07-02 19:18:14 +05:30
Srivats P
d827e43a11 Cache protocol frame value when possible
Caching reduces packet build aka "apply" time.

This is a first in a series of optimization experiments

These optimizations are potentially dangerous and may cause incorrect
packets to be generated unintentionally and therefore need rigorous
testing
2019-07-02 19:03:26 +05:30
8 changed files with 133 additions and 5 deletions

View File

@ -100,7 +100,7 @@ void NoMsgHandler(QtMsgType type, const QMessageLogContext &/*context*/,
const QString &msg)
{
if (type == QtFatalMsg) {
fprintf(stderr, qPrintable(msg));
fprintf(stderr, "%s\n", qPrintable(msg));
fflush(stderr);
abort();
}

View File

@ -82,6 +82,9 @@ AbstractProtocol::AbstractProtocol(StreamBase *stream, AbstractProtocol *parent)
protoSize = -1;
_hasPayload = true;
_cacheFlags |= FieldFrameBitOffsetCache;
// FIXME: temporary for testing only
_frameValueCache.isEnabled = qEnvironmentVariableIsSet("PFVCACHE");
}
/*!
@ -173,6 +176,36 @@ void AbstractProtocol::commonProtoDataCopyFrom(const OstProto::Protocol &protoco
this function. See the SampleProtocol for an example
*/
/*!
Update protocol's frame value cacheability based on current protocol
configuration
*/
void AbstractProtocol::updateCacheability()
{
if (!_frameValueCache.isEnabled
|| isProtocolFrameValueVariable()
|| isProtocolFrameSizeVariable()
|| isProtocolFrameHeaderValueVariable()
|| isProtocolFramePayloadValueVariable()
|| isProtocolFramePayloadSizeVariable())
{
// TODO: cache following cases
// * isProtocolFrameHeaderValueVariable AND !hasTcpUdpCksum
// * isProtocolFramePayloadValueVariable AND !hasPayloadDependentField
// * isProtocolFramePayloadSizeVariable AND !hasPayloadDependentField
// * External Variables AND !hasCksumField
// * streamIndex == 0 for variable frames (value/size)
qDebug("Stream %u %6s: FrameValue is NOT cacheable",
mpStream->id(), qPrintable(shortName()));
_cacheFlags &= ~FrameValueCache; // can't cache frame value
}
else {
qDebug("Stream %u %6s: FrameValue is cacheable",
mpStream->id(), qPrintable(shortName()));
_cacheFlags |= FrameValueCache; // frame value is cacheable
}
_frameValueCache.isValid = false;
}
/*!
Returns the full name of the protocol
@ -517,7 +550,7 @@ quint32 AbstractProtocol::payloadProtocolId(ProtocolIdType type) const
Returns the protocol's size in bytes
The default implementation sums up the individual field bit sizes and
returns it. The default implementation calculates the caches the size on
returns it. The default implementation calculates and caches the size on
the first invocation and subsequently returns the cached size.
If the subclass protocol has a varying protocol size, it MUST reimplement
@ -602,6 +635,17 @@ QByteArray AbstractProtocol::protocolFrameValue(int streamIndex, bool forCksum,
QByteArray proto, field;
uint bits, lastbitpos = 0;
FieldFlags flags;
bool cacheable = (_cacheFlags & FrameValueCache) && !forCksum;
qDebug("Stream %u %6s: In frameValue frameIdx %u forCksum %d cacheable %d",
mpStream->id(), qPrintable(shortName()),
streamIndex, forCksum, cacheable);
if (cacheable && _frameValueCache.isValid)
{
qDebug("Stream %u %6s: frameIdx %u using FV cache",
mpStream->id(), qPrintable(shortName()), streamIndex);
return _frameValueCache.value;
}
for (int i=0; i < fieldCount() ; i++)
{
@ -698,6 +742,17 @@ QByteArray AbstractProtocol::protocolFrameValue(int streamIndex, bool forCksum,
varyProtocolFrameValue(proto, streamIndex, vf);
}
if (cacheable)
{
qDebug("Stream %u %6s: frameIdx %d forCksum %d saving cache",
mpStream->id(), qPrintable(shortName()), streamIndex, forCksum);
_frameValueCache.value = proto;
_frameValueCache.isValid = true;
}
qDebug("Stream %u %6s: frameIdx %d forCksum %d return",
mpStream->id(), qPrintable(shortName()), streamIndex, forCksum);
return proto;
}
@ -777,6 +832,28 @@ bool AbstractProtocol::isProtocolFramePayloadValueVariable() const
return false;
}
/*!
Returns true if the header content for a protocol varies at run-time,
false otherwise
This is useful for subclasses which have fields dependent on header content
(e.g. UDP has a checksum field that varies if the IP header varies)
*/
bool AbstractProtocol::isProtocolFrameHeaderValueVariable() const
{
AbstractProtocol *p = prev;
if (p)
{
if (p->isProtocolFrameValueVariable())
return true;
}
if (parent && parent->isProtocolFrameHeaderValueVariable())
return true;
return false;
}
/*!
Returns true if the payload size for a protocol varies at run-time,
false otherwise

View File

@ -20,6 +20,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
#ifndef _ABSTRACT_PROTOCOL_H
#define _ABSTRACT_PROTOCOL_H
#include "cache.h"
#include <QByteArray>
#include <QFlags>
#include <QHash>
@ -53,6 +55,7 @@ private:
mutable int protoSize;
mutable QString protoAbbr;
mutable QHash<int, int> _fieldFrameBitOffset;
mutable Cache<QByteArray> _frameValueCache;
OstProto::Protocol _data;
protected:
@ -66,7 +69,8 @@ protected:
//! Caching Control Flags
enum CacheFlag {
FieldFrameBitOffsetCache = 0x1
FieldFrameBitOffsetCache = 0x1,
FrameValueCache = 0x2
};
quint32 _cacheFlags;
@ -124,6 +128,8 @@ public:
virtual void protoDataCopyInto(OstProto::Protocol &protocol) const = 0;
virtual void protoDataCopyFrom(const OstProto::Protocol &protocol) = 0;
void updateCacheability();
virtual QString name() const;
virtual QString shortName() const;
@ -157,6 +163,7 @@ public:
virtual bool isProtocolFrameValueVariable() const;
virtual bool isProtocolFrameSizeVariable() const;
virtual int protocolFrameVariableCount() const;
bool isProtocolFrameHeaderValueVariable() const;
bool isProtocolFramePayloadValueVariable() const;
bool isProtocolFramePayloadSizeVariable() const;
int protocolFramePayloadVariableCount() const;

31
common/cache.h Normal file
View File

@ -0,0 +1,31 @@
/*
Copyright (C) 2019 Srivats P.
This file is part of "Ostinato"
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 3 of the License, or
(at your option) any later version.
This program 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 program. If not, see <http://www.gnu.org/licenses/>
*/
#ifndef _CACHE_H
#define _CACHE_H
template <class T>
struct Cache
{
bool isEnabled{false};
bool isValid{false};
T value;
};
#endif

View File

@ -108,6 +108,13 @@ void StreamBase::protoDataCopyFrom(const OstProto::Stream &stream)
iter->insert(proto);
}
iter->toFront();
while (iter->hasNext())
{
AbstractProtocol *p = iter->next();
p->updateCacheability();
}
delete iter;
}

View File

@ -406,9 +406,13 @@ _error_exit2:
return;
}
bool logsEnabled = false;
void RpcConnection::connIdMsgHandler(QtMsgType /*type*/,
const QMessageLogContext &/*context*/, const QString &msg)
{
if (!logsEnabled)
return;
if (connId.hasLocalData()) {
QString newMsg(*connId.localData());
newMsg.append(msg);

View File

@ -771,8 +771,10 @@ void AbstractPort::resolveDeviceNeighbors()
for (int i = 0; i < streamList_.size(); i++)
{
const StreamBase *stream = streamList_.at(i);
int frameCount = stream->frameVariableCount();
if (!stream->isEnabled())
continue;
int frameCount = stream->frameVariableCount();
for (int j = 0; j < frameCount; j++) {
// we need the packet contents only uptil the L3 header
int pktLen = stream->frameValue(pktBuf_, kMaxL3PktSize, j);

View File

@ -116,7 +116,7 @@ void NoMsgHandler(QtMsgType type, const QMessageLogContext &/*context*/,
const QString &msg)
{
if (type == QtFatalMsg) {
fprintf(stderr, qPrintable(msg));
fprintf(stderr, "%s\n", qPrintable(msg));
fflush(stderr);
abort();
}