Swift中的JSONSerialization.jsonObject性能

时间:2017-07-14 11:40:48

标签: swift

我有一个JSON文件(只是一个dicts数组),大60兆字节。在PHP中,解析需要2秒,但在Swift中,它只需要7秒钟。这是荒唐的。那是我做错了什么或者是什么? Swift代码:

let json = try! JSONSerialization.jsonObject(
    with: try! Data(
        contentsOf: URL(
            fileURLWithPath: "/some/file/path/to.json"
        )
    )
) as! [[AnyHashable: Any]]

我简化了代码,因此它是一个操作,但缓慢的部分是JSONSerialization.jsonObject,我明确地测量了它(从文件加载数据是如预期的那样快)。 PHP代码非常简单 - json_decode(file_get_contents())

值得一提的是,在发布模式下构建(优化)并没有改善这种情况。

UPD:在分析应用程序之后,我发现瓶颈是将结果转换为[[AnyHashable:Any]],将其更改为[[String:Any]]稍微改善了一下情况(从7秒到〜 5.3),但它仍然是羞耻和痛苦。

所以基本上现在的问题是:为什么投射速度如此之慢,是否有更快地处理大型JSON对象(或任何其他序列化数据)的方法?

1 个答案:

答案 0 :(得分:3)

我不会判断你在JSON中编码60MB ...好吧,我会稍微判断一下你。这是存储这么多数据的疯狂格式。从我的系统中得到了;让我们努力让它更快。

首先,你能直接跳到Swift 4吗?如果是这样,请删除JSONSerialization并直接转到新的JSONDecoder。它避免了很多类型的问题。也就是说,它可能会或可能不会更快。

让我们来看看“为什么会这么慢”问题。简单。铸造很快。你不是在施法。你正在转变。 AnyHashable是一种类型橡皮擦;它与String

完全不同的结构类型
public struct AnyHashable {

您必须将String加入AnyHashable结构中。实际上这很快(因为写入时的复制如何工作),但这意味着字典是一个完全不同的字典。你强迫它制作完整的副本。

我历史上处理大量JSON数组的方式是手动部分解析它们。扔掉第一个[,一次收集一个JSON对象,解析它,然后将结果放到一个数组上。这样你就不必将所有数据都存入内存,也不需要燃烧600MB的高水位线。如果您对输入JSON有一些控制权,这种技术显然效果最好。例如,我经常作弊并写下这样的JSON:

[
    { ... JSON ... },
    { ... JSON ... }
]

这使得解析记录非常快速和容易(仅在换行符上拆分)。 (我碰巧喜欢这个,因为它对命令行工具很友好,比如grep和awk,根本没有JSON解析)。它仍然是合法的JSON,但有一点特殊的知识,我可以更快地解析它。

对于基准测试,我还建议您在ObjC中构建它以将NSJSONSerialization与“桥接ObjC类型”分离为Swift。 NSJSONSerialization通常被认为是一个非常快速的解析器。如果你不是非常小心(如上所述),对Swift的桥接是昂贵的。 (我喜欢斯威夫特,但这是一种非常难以理解表现的语言。)

看起来这个空间中还有另一个名为JASON的玩家,但我还没有尝试过。 (曾经有一个名为JSONKit的非常着名的包装,通过玩ObjC技巧让你的皮肤爬行但是令人惊讶地工作非常好,所以必须原谅。但这些技巧最终赶上了它,并且非常快。我认为它甚至不再适用了。)