1
0
Fork 0

WebSocket property-mirror: optimise JSON building

Work around some awful cJSON performance when building large arrays,
greatly speeding up remote-canvas updating
This commit is contained in:
James Turner 2018-06-24 23:20:19 +01:00
parent b2e8097b22
commit 7d84a03dea

View file

@ -140,7 +140,6 @@ using std::string;
virtual ~MirrorTreeListener() virtual ~MirrorTreeListener()
{ {
SG_LOG(SG_NETWORK, SG_INFO, "destroying MirrorTreeListener");
} }
virtual void valueChanged(SGPropertyNode* node) override virtual void valueChanged(SGPropertyNode* node) override
@ -224,18 +223,23 @@ using std::string;
cJSON* makeJSONData() cJSON* makeJSONData()
{ {
#if defined (MIRROR_DEBUG)
SGTimeStamp st; SGTimeStamp st;
st.stamp(); st.stamp();
cJSON* result = cJSON_CreateObject();
int newSize = newNodes.size(); int newSize = newNodes.size();
int changedSize = changedNodes.size(); int changedSize = changedNodes.size();
int removedSize = removedNodes.size(); int removedSize = removedNodes.size();
#endif
cJSON* result = cJSON_CreateObject();
if (!newNodes.empty()) { if (!newNodes.empty()) {
cJSON * newNodesArray = cJSON_CreateArray(); cJSON * newNodesArray = cJSON_CreateArray();
// cJSON_AddItemToArray performance is O(N) due to use of a linked
// list, which dominates the performance here. To fix this we maintan
// a point to the tail of the array, keeping appends O(1)
cJSON* arrayTail = nullptr;
for (auto prop : newNodes) { for (auto prop : newNodes) {
changedNodes.erase(prop); // avoid duplicate send changedNodes.erase(prop); // avoid duplicate send
cJSON* newPropData = cJSON_CreateObject(); cJSON* newPropData = cJSON_CreateObject();
@ -247,7 +251,15 @@ using std::string;
if (prop->getType() != simgear::props::NONE) { if (prop->getType() != simgear::props::NONE) {
cJSON_AddItemToObject(newPropData, "value", JSON::valueToJson(prop)); cJSON_AddItemToObject(newPropData, "value", JSON::valueToJson(prop));
} }
cJSON_AddItemToArray(newNodesArray, newPropData);
if (arrayTail) {
arrayTail->next = newPropData;
newPropData->prev = arrayTail;
arrayTail = newPropData;
} else {
cJSON_AddItemToArray(newNodesArray, newPropData);
arrayTail = newPropData;
}
} }
newNodes.clear(); newNodes.clear();
@ -266,19 +278,32 @@ using std::string;
if (!changedNodes.empty()) { if (!changedNodes.empty()) {
cJSON * changedNodesArray = cJSON_CreateArray(); cJSON * changedNodesArray = cJSON_CreateArray();
// see comment above about cJSON_AddItemToArray
cJSON* tail = nullptr;
for (auto prop : changedNodes) { for (auto prop : changedNodes) {
cJSON* propData = cJSON_CreateArray(); cJSON* propData = cJSON_CreateArray();
cJSON_AddItemToArray(propData, cJSON_CreateNumber(idForProperty(prop))); cJSON_AddItemToArray(propData, cJSON_CreateNumber(idForProperty(prop)));
cJSON_AddItemToArray(propData, JSON::valueToJson(prop)); cJSON_AddItemToArray(propData, JSON::valueToJson(prop));
cJSON_AddItemToArray(changedNodesArray, propData);
if (tail) {
tail->next = propData;
propData->prev = tail;
tail = propData;
} else {
cJSON_AddItemToArray(changedNodesArray, propData);
tail = propData;
}
} }
changedNodes.clear(); changedNodes.clear();
cJSON_AddItemToObject(result, "changed", changedNodesArray); cJSON_AddItemToObject(result, "changed", changedNodesArray);
} }
#if defined (MIRROR_DEBUG)
SG_LOG(SG_NETWORK, SG_INFO, "making JSON data took:" << st.elapsedMSec() << " for " << newSize << "/" << changedSize << "/" << removedSize); SG_LOG(SG_NETWORK, SG_INFO, "making JSON data took:" << st.elapsedMSec() << " for " << newSize << "/" << changedSize << "/" << removedSize);
#endif
recentlyRemoved.clear(); recentlyRemoved.clear();
return result; return result;
@ -366,7 +391,6 @@ MirrorPropertyTreeWebsocket::MirrorPropertyTreeWebsocket(const std::string& path
MirrorPropertyTreeWebsocket::~MirrorPropertyTreeWebsocket() MirrorPropertyTreeWebsocket::~MirrorPropertyTreeWebsocket()
{ {
SG_LOG(SG_NETWORK, SG_INFO, "shutting down MirrorPropertyTreeWebsocket");
} }
void MirrorPropertyTreeWebsocket::close() void MirrorPropertyTreeWebsocket::close()