instruments应用_使用Instruments优化应用启动并检测性能问题

instruments应用

A few years back I joined the top 1 online classifieds company in the world. One of the first tasks I was given, was to reduce the app launch below the 2 seconds mark. The users were leaving comments on the App Store because the app was “slow”.

几年前,我加入了全球排名第一的在线分类广告公司。 我获得的首要任务之一是将应用程序启动时间缩短到2秒以下。 由于该应用“运行缓慢”,因此用户在App Store上留下了评论。

The app, as you can imagine, had a mix of Swift and legacy Objective-C code, 4000+ files distributed in 8 repositories (main app and 7 internal libraries). In addition to this, around 30+ third-party libraries, everything linked using Cocoapods.

您可以想象,该应用程序混合了Swift和旧版Objective-C代码,在8个存储库(主应用程序和7个内部库)中分发了4000多个文件。 除此之外,大约有30多个第三方库,所有内容都使用Cocoapods链接在一起。

Image for post
Photo by Veri Ivanova on Unsplash
Veri IvanovaUnsplash拍摄的照片

主前与主后 (pre-main vs. post-main)

When talking about app launch optimizations, there are 2 areas to tackle, both have different challenges and possible solutions.

在讨论应用程序启动优化时,有两个领域需要解决,这两个领域都有不同的挑战和可能的解决方案。

There are many articles (1, 2, 3) already talking about what to do with the pre-main, usually is related to convert the dynamic libraries into static (libs will be linked at compilation time instead of runtime), or at least reduce the amount of dynamic libs.

有很多文章( 123 )已经在谈论如何处理前置主,通常是将动态库转换成静态相关(库将在编译的时候,而不是运行时被链接),或至少减少做动态库的数量。

But when I did the profiling, although there was some room for improvement in the pre-main section, the slowest part was the post-main (starting when didFinishLaunchingWithOptions is called)

但是,当我进行性能分析时,尽管在main之前的部分中仍有一些改进的余地,但是最慢的部分是post-main (从调用didFinishLaunchingWithOptions时开始)

I don’t need to mention that several third-party libraries were initialized, internal services were set up, network calls were being made… it wasn’t an easy task.

我不必提及初始化了几个第三方库,建立了内部服务,进行了网络调用……这不是一件容易的事。

扰流板警报! (Spoiler alert!)

The following code was the responsible for the slow post-main:

以下代码是造成后期维护缓慢的原因:

Can you spot the issue?

您能发现问题吗?

使用仪器 (Using Instruments)

Since I just joined the company and my knowledge of the codebase was limited, decided to use instruments to see if could find a hint of where to look.

由于我刚加入公司,并且我对代码库的了解有限,因此决定使用工具来查看是否可以找到查找的提示。

Using the Time Profiler, the heaviest stack revealed that had something to do with storing stuff in Realm.

使用Time Profiler ,最重的堆栈显示出与将内容存储在Realm中有关。

Image for post

Asking my colleagues, I discovered we were using Realm to persist the complete category tree (5000+ rows, because car make and models were included). You can find comparison between Realm and CoreData here, here and here.

问我的同事时,我发现我们正在使用Realm保留完整的类别树(5000多个行,因为其中包括汽车制造商和模型)。 您可以在此处此处此处找到Realm与CoreData之间的比较。

On the first install, and every time there was an update on the tree (more often than you would think), the full tree was downloaded from the server and stored using the code shared above. In case of failure to download, a JSON containing the latest tree embedded in the app was used as a fallback.

在第一次安装时,并且每次树上进行更新时(比您想像的都要多),整个树是从服务器下载并使用上面共享的代码存储的。 如果下载失败,则使用包含嵌入在应用程序中的最新树的JSON作为后备。

写交易 (Write Transaction)

I didn’t have experience with Realm by the time I joined that company, so decided to dig in the documentation, and found out that Realm.write() is synchronous and blocking, that’s fine because we want the delete operation to finish completely before starting with the insertions.

加入那个公司的时候我还没有Realm的经验,所以决定深入研究文档,发现Realm.write()同步且阻塞的 ,这很好,因为我们希望删除操作在此之前完成从插入开始。

But also the documentation states:

但文档也指出:

Since write transactions incur non-negligible overhead, you should architect your code to minimize the number of write transactions.

由于写事务会产生不可忽略的开销,因此您应该设计代码以最大程度地减少写事务的数量。

By looking at the first code shared, it’s obvious now that there is a write transaction for every category. Just imagine the overhead of having 5000+ database transactions queued while the user is watching clueless the loading screen.

通过查看共享的第一个代码,现在很明显每个类别都有一个写事务。 试想一下,当用户无知地看着加载屏幕时,让5000多个数据库事务排队的开销。

Refactoring the code resulted in:

重构代码导致:

This simple change of moving the second realm.write outside the forEach, improved the efficiency by 90%

将第二个realm.write forEach之外的简单更改将效率提高了90%

但是,等等,还有更多! (But wait, there’s more!)

There is still another potential error to solve. The second code shared is still violating the concept of atomicity in a transaction, by having 2 different transactions, one for delete, and one for the write.Imagine the event in which the delete is success but the write fails.

还有另一个潜在的错误要解决。 共享的第二个代码仍然违反了事务中原子性的概念,它具有2个不同的事务,一个事务用于删除,另一个事务用于写入。想象一下删除成功但写入失败的事件。

In order to avoid inconsistency on the database, both delete and write operations have to be grouped within the same transaction, and let Realm handle the rollback mechanism in case of error.

为了避免数据库上的不一致,删除和写入操作都必须在同一事务中进行分组,并在发生错误时让Realm处理回滚机制。

The code was refactored one more time as follows:

再次将代码重构如下:

Realm was used to store Categories only, therefore using the deleteAll() command didn’t present any immediate risk. However would require further refactoring to ensure deleting only the desired objects and not flushing the entire database by mistake, like so:

Realm仅用于存储类别,因此使用deleteAll()命令不会立即带来任何风险。 但是,将需要进行进一步的重构以确保仅删除所需的对象并且不会错误地刷新整个数据库,例如:

结论 (Conclusion)

One simple line out of place can have a tremendous negative impact for the user, affecting your app rating, losing potential new users. The first impression upon first install is key.

一条简单的生产线对用户可能会产生巨大的负面影响,从而影响您的应用评级,并失去潜在的新用户。 首次安装时的第一印象是关键。

We can always rely on tools like Instruments to analyze the app from different angles, time profiling, memory allocations, network usage, etc.

我们始终可以依靠Instruments之类的工具从不同角度,时间剖析,内存分配,网络使用情况等角度分析应用程序。

However I believe logic errors like the one described above should be caught during local testing, or during code review, and it’s important to understand how it managed to get merged into the main branch, in order to avoid similar scenarios.

但是,我相信应该在本地测试或代码审查期间捕获上述逻辑错误,并且重要的是要了解如何设法将其合并到主分支中,以避免类似的情况。

翻译自: https://medium.com/flawless-app-stories/optimizing-app-launch-detecting-performance-issues-using-instruments-bca421da43b

instruments应用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值