CoreData和RestKit导入非常大的数据集时的性能



我正在使用RestKit在各种端点(在iOS平台上)获取JSON数据。

有几个关于SO的问题都指向同一个方向:

在iPhone上使用CoreData导入大型数据集

但我的问题仍然是一个不同的,因为我知道,如果JSON文件变得太大,我必须把它切成块。我会的!

在RestKit中,CoreData是如何导入的?
似乎有一个父/子上下文设置,这是非常效率低下导入大型数据集时在尽可能短的时间内(也许在启动时一次完成)-不要批量/延迟导入!!) .

查看Florian Kugler在CoreData (Stacks)中导入性能的文章

我的问题是:我可以设置一个不同的上下文,除了已经与RestKit的父/子上下文设置,并运行一个完全异步导入的RKManagedObjectRequestOperation和在另一个上下文中。然后将上下文合并到mainContext中以获取…

我真的想坚持使用CoreData而不是切换到普通的SQLite,从CoreDataRestKit的组合中获得最大可能的性能。

我对你的专业答案感到兴奋。也许Blake也能直接回答我这个问题。

嗯,首先,如果你想要最大的性能,如果你真的需要,不要使用RestKit,不要使用AFNetworking,不要使用NSJSONSerialization。当处理数据集时,如果您的目标是保持适度内存占用和性能时,它们都受到设计选择的影响。

你应该有一个非常大的单个JSON(可能是一个JSON数组,其元素是JSON对象)作为一个单个连接的主体,以获得更好的性能。或者,您可以使用自定义传输格式,在一个连接中发送多个JSON(例如,一系列JSON对象,由"空白"分隔)。

有大量的连接肯定很慢。

当你努力实现最快的性能时,你应该同时下载、解析JSON、创建表示并将其保存到持久存储。

注意:

当并行执行这些操作时,您特别容易受到连接错误的攻击,并且保持一致和逻辑正确的数据集可能成为一项挑战。因此,如果您的连接质量不好且经常中断,您可以首先下载JSON文件并将其保存到临时文件(还支持HTTP范围标头,以便有机会挂起和恢复下载)。当然,你的性能会下降——但在这种情况下,你无论如何也不能让它变快。

所以,当您的目标是最大性能时,您应该利用所有cpu能力,即尽可能多地并行运行-这在连接速度很快的情况下尤其如此。

JSON解析器也应该能够解析NSData对象中包含的"块"——那是部分JSON,因为这是我们从connection:didReceiveData:中得到的。

当您收到JSON数据时,您需要将其"映射"为合适的表示形式。通常,已知的JSON解析器会创建一个"基础表示"。然而,更快的方法是直接从JSON创建最终所需的对象类型。这需要一个"SAX风格的API"——基本上是一个解析器的简化版本,它向委托或客户端发送"解析事件"——例如"get JSON- array begin"或"get JSON Boolean False"等,以及接收这些事件并动态构造所需对象的自定义代码。

这一切都需要一个JSON解析器,它具有NSJSONSerialization中找不到的功能:sax风格的API,"块解析",或解析输入是一系列JSON文档。

为了最大限度地利用CPU、磁盘和网络,您将"任务"分别划分为CPU绑定、I/o绑定和网络绑定的操作,并创建和并行运行尽可能多的操作,因为这对系统是合理的。这些任务基本上都是异步运行的,接受输入,处理输入,并产生输出,该输出是下一个异步任务的输入。第一个任务在完成时通知下一个任务,例如通过完成处理程序(块),并通过参数传递其输出。

处理传入的JSON数据"块",即解析和创建表示,是一个cpu限制的操作。然而,这通常是相当快的,我认为不值得通过并发队列的方式在所有可用的cpu上分配这些cpu密集型任务。

处理传入的JSON数据"块"基本上可以用两种方法实现,同样也有优缺点:

异步处理部分JSON数据

当你在connection:didReceiveData:中得到一个"chunk"时,你可以异步地将它调度到一个不同的队列上进行处理(即解析和创建表示),该队列运行在与委托不同的线程上。

优点:委托立即返回,因此不阻塞委托线程,这反过来导致最快的读取传入网络数据和适度小的网络缓冲区。在尽可能短的时间内完成连接。

缺点:如果处理速度比接收数据慢,可能会导致大量NSData对象在块中排队等待在串行调度队列中执行。这将保持分配给每个NSData对象的内存,并且系统RAM最终可能会耗尽,除非您采取适当的行动,否则您可能会收到内存警告或崩溃。

同步处理部分JSON数据

当接收到JSON的一个块,解析器将被调用同步相对于委托的线程。

优点:当数据处理比接收数据慢时,这避免了内存问题。然而,这可能最终会导致从网络读取数据的速度减慢(一旦内部接收缓冲区满了)。

缺点:如果处理缓慢并且内部网络缓冲区已满,这将增加连接处于活动状态的时间,从而增加连接断开的概率。

这两种方法都受益于快速的解析器/表示生成器,并且需要一个解析器,它可以将JSON的"块"处理为NSData对象,并在完成表示时异步通知客户端。可选地,它还应该有一个"SAX风格"的API。据我所知,有两个第三方JSON解析器可以满足这些要求:

jsonlite and this

JPJson

两者都非常快(比JSONKit和NSJSONSerialization快),支持SAX风格的解析,可以将JSON作为NSData对象块处理。JPJson还可以处理包含多个json的文件。

(披露:我是JPJson的作者)

创建表示后,下一步是创建和初始化托管对象(除非解析器直接生成管理对象),并将对象保存到持久存储中。这是一个受I/O和CPU限制的操作——但是当使用SSD存储时,可能会有更多的CPU限制。我会将此进程调度到一个单独的队列中,并检查它如何与其他CPU受限操作一起工作。根据网络的速度,网络变得更加CPU绑定更高的带宽。

考虑到好的和坏的连接,努力保持低内存占用和最大化性能的可扩展方法是相当难以实现的,尽管-并且是一个具有挑战性的编程任务。玩得开心!;)

最新更新