在Qt

时间:2015-09-12 20:04:18

标签: c++ json performance qt

我需要使用JSON通过网络传输高容量数据,然后在高流量和高性能要求的情况下在运行时在Qt中解释它。基本上,需要处理数百甚至数千或更多JSON文档的任意大小和复杂性。我对如何做到这一点有点自由,但我正在仔细考虑我的选择,到目前为止,我不满足于它们似乎是什么。

主要关注的是查找和修改速度,而从/到JSON的转换在我的上下文中不太重要,因为我可以处理较慢的入口和/或输出,无论如何都是不稳定的。但是一旦数据通过,就需要随时可用。

我看到他们的选择:

1)选项1是直接使用Qt 5中的JSON支持类。但缺点是,为了在JSON数据树中的某处修改单个值,需要复制整个事物,然后再使用修改后的值再次创建JSON。对于单个修改,这可能是非常昂贵的。 QJsonValueRef在某种程度上有助于它,但不允许迭代整个结构,因为结构中的每个对象仍然需要被复制。

2)将JSON转换为QVariantMap(QMap)并使用它。除了来回转换的开销之外,它遇到与1)相同的问题,因为QAssociativeIterable不会返回对其项的引用,因此不能在不先复制每个QVariant的情况下进入结构。基本上它允许在QVariantMap的单个子级别中进行非复制迭代。它可能仍然比直接使用Qt的JSON类更好,但基本上它似乎与1)具有相同的限制。

3)将JSON转换为我自己的数据结构,该结构允许在不复制任何值的情况下通过结构和数据修改进行下降。每个"级别"将成为其父母将访问的对象,从而允许以任意深度非拷贝地访问其内容。这个解决方案会受到以下事实的影响:我很可能无法为每个"级别实现查找机制。这几乎和Qt / STL容器一样有效。但也许我可以将Qt的容器与这种方法结合起来,将子指针存储在其中尽可能快的运算符==重载...

4)将JSON转换为平面的二维数据结构,例如:

Level1/Level2/key : value
Level1/Level2/key2 : value2
Level1/key3 : value3

这种方法在数据存储方面存在大量冗余,并且与JSON的转换成本很高,但可以提供最大的查找/修改速度,因为它不需要进入任何数据结构。然而,这种好处的成本对我来说似乎太大了(冗余和慢速转换)。

我有点倾向于选项3,但也许还有其他选择。我想听听你对这个问题的想法和/或想法。或者,JSON首先不是通过网络进行数据传输的正确工具。

2 个答案:

答案 0 :(得分:2)

如果you look at the Qt source code您将意识到Qt json对象已经过优化:

  • 参考计数
  • 写入时复制。
  • 基本内存池机制。与动态数组一样,内存容量大于实际内存容量。
  • 数据的连续内存

要遍历结构,请使用QJsonObject类。

的开销
QJsonValue QJsonObject::value(const QString & key);
对象类型

是最小的。但正如您所指出的,它为字符串类型创建了一个新字符串;它还对数值执行“强制转换”。请注意,在解析了进一步处理之后,只有当您开始处理QJsonValue时,才会产生内存开销。

因此选项1并没有那么糟糕。根据你的条件,选项2很可怕。我没有看到任何优势。由于您自己指定的原因,选项4是噩梦。在取得一些进展后,你后悔的那种工作。

如果严格使用Qt json类是不够的,那么请转到更高效的库rapidjson

优点:

  • Json只是一个大字符数组,元素只是引用+类型。
  • 字符串管理:getstring返回json字符串中字符串的指针。零分配。插入字符串时,可以使用仅引用现有数据缓冲区的版本,也可以将字符串分配为Qt。
  • 对分配的总控制。 对象分配是移动操作。除非您明确要求复制,否则您保证不会有任何内存分配\移动。分配内存的方法有一个显式的文档分配器作为参数。
  • 由于移动设计,零参考计数器。
  • 明确评估Json值。

不便之处

  • 非直观的代码风格。仅限成年人。
  • 更容易插入缺陷并且有错误的代码。

它完成了你的选择3试图做的事情。

答案 1 :(得分:1)

我实现了一个便利库,以便于JSON查找和修改。我的库不是像QJson API那样被隐式共享,而是使用显式共享实现。这使得编辑变得更容易,但却失去了安全性。看看:

https://github.com/juangburgos/QJsExplicit

您可以这样做:

QJsDocument doc;

// create test object
QJsObject testobj = doc.createObject("testobj");
testobj.setAttribute("attr1", "hola mundo");
testobj.setAttribute("attr2", 3.1416);
testobj.setAttribute("attr3", false);

// create test array
QJsArray testarr = doc.createArray("testarr");
testarr.appendValue(2);
testarr.appendValue(4);
testarr.appendValue(6);
testarr.appendValue(8);

// create test array child object
QJsObject arrchildobj = testarr.createObject("whatever");
arrchildobj.setAttribute("xxx", true);
arrchildobj.setAttribute("yyy", 8);
arrchildobj.setAttribute("zzz", 7.8);

// orphan object append to test
QJsObject orphanobj;
orphanobj.setKeyName("orphanobj");
orphanobj.setAttribute("A", 1);
orphanobj.setAttribute("B", 2);
orphanobj.setAttribute("C", 3);
testobj.appendChild(orphanobj);

// edit through shallow copy of the document
QJsDocument doc2 = orphanobj.ownerDocument();
if (doc2.getChildByKey("testarr").isArray())
{
    QJsArray testarr2 = doc2.getChildByKey("testarr").toArray(); 
    testarr2.setValueAt(3, "ocho");                              
}                                                                

// orphan array append to test array
QJsArray orphanarr;
for (int i = 0; i < 3; i++)
{
    QJsObject tempObj;
    tempObj.setAttribute("id", i);
    orphanarr.appendObject(tempObj);
}
testarr.appendArray(orphanarr);

QString strJSON = doc.toJson(QJsNode::Indented);
// print original
ui->plainTextEdit->setPlainText(strJSON);    

哪个创造了这个:

{
    "testarr": [
        2,
        4,
        6,
        "ocho",
        {
            "xxx": true,
            "yyy": 8,
            "zzz": 7.7999999999999998
        },
        [
            {
                "id": 0
            },
            {
                "id": 1
            },
            {
                "id": 2
            }
        ]
    ],
    "testobj": {
        "attr1": "hola mundo",
        "attr2": 3.1415999999999999,
        "attr3": false,
        "orphanobj": {
            "A": 1,
            "B": 2,
            "C": 3
        }
    }
}