2年前 (2023-06-12)  相关技术 |   抢沙发  270 
文章评分 0 次,平均分 0.0

使用FlatBuffers改善Facebook在Android上的性能

在Facebook上,人们可以通过阅读状态更新和查看照片来与家人和朋友保持联系。在我们的后端,我们存储构成这些连接的社交图的所有数据。在移动客户端上,我们无法下载整个图,所以我们将节点及其一些连接下载为本地树结构。

下图说明了这是如何用于带有图片附件的故事的。在这个例子中,John创建了这个故事,然后他的朋友们喜欢它并评论它。图片的左侧是社交图,用来描述Facebook后台的关系。当安卓应用程序查询故事时,我们会得到一个从故事开始的树状结构,包括关于actor、反馈和附件的信息(如图右侧所示)。

使用FlatBuffers改善Facebook在Android上的性能

我们要解决的一个关键问题是如何在应用程序中表示和存储数据。在SQLite数据库中将所有这些数据规范化为单独的表是不现实的,因为我们有很多不同的方式从后端查询节点和相关的树结构。因此,我们直接存储树结构。一种选择是将树结构存储在JSON中,但这需要我们解析JSON并将其转换为Java对象,然后UI才能使用它。此外,JSON解析需要时间。我们曾经在Android上使用Jackson JSON解析器,但发现以下问题:

  • 解析速度 - 解析20 KB的JSON流需要35毫秒(Facebook的典型响应大小),这超过了16.6毫秒的UI帧刷新间隔。使用这种方法,我们无法在滚动时不观察到帧丢失(视觉断断续续)的情况下按需从磁盘缓存加载故事。
  • 分析器初始化 - JSON解析器在开始解析之前需要构建字段映射,这可能需要100到200毫秒的时间,这大大降低了应用程序的启动时间。
  • 垃圾收集 - 在JSON解析过程中创建了许多小对象,在我们的探索中,在解析20KB的JSON流时分配了大约100KB的临时内存,这给Java的垃圾收集器带来了巨大的压力。

我们想找到一种更好的存储格式来提高我们的Android应用程序的性能。

FlatBuffers

在我们探索替代格式的过程中,我们遇到了FlatBuffers,这是谷歌的一个开源项目。FlatBuffers是协议缓冲区的演变,其中包括对象元数据,允许直接访问数据的各个子组件,而不必预先反序列化整个对象(在本例中为树)。

想象一下,我们有一个简单的person类对象,它有四个字段:namefriendship statusspouselist<friends>spousefriends字段还包括person对象,因此这形成了一个树结构。下面是一个简化的例子,说明了这样一个物体是如何为一个人John和他的妻子Mary在FlatBuffer中放置的。

class Person {
    String name;
    int friendshipStatus;
    Person spouse;
    List<Person>friends;
    }

使用FlatBuffers改善Facebook在Android上的性能

在上面的布局中,您会注意到:

  • 每个对象分为两个部分:位于轴心点左侧的元数据部分(或vtable)和右侧的真实数据部分。
  • 每个字段对应于vtable中的一个槽,该槽存储该字段的实际数据的偏移量。例如,John的vtable的第一个槽的值为1,表示John的名称存储在John的轴心点右侧一个字节处。
  • 对于对象字段,vtable中的偏移将指向子对象的轴心点。例如,John vtable中的第三个槽指向Mary的轴心点。
  • 为了指示不存在数据,我们可以在vtable槽中使用偏移量0。

下面的代码片段显示了我们如何在上面显示的结构中查找John妻子的名字。

// Root object position is normally stored at beginning of flatbuffer.
int johnPosition = FlatBufferHelper.getRootObjectPosition(flatBuffer);
int maryPosition = FlatBufferHelper.getChildObjectPosition(
   flatBuffer,
   johnPosition, // parent object position
   2 /* field number for spouse field */);
String maryName = FlatBufferHelper.getString(
   flatBuffer,
   johnPosition, // parent object position
   2 /* field number for name field */);

请注意,不涉及中间对象创建,从而节省临时内存分配。我们可以通过将FlatBuffer数据直接存储在文件中并将其映射到内存中来进一步优化这一点。这意味着我们只加载文件中需要读取的部分,这进一步减少了整个内存占用。

此外,在读取字段之前,不需要反序列化对象树。这减少了存储层和UI之间的延迟,并提高了整体性能。

FlatBuffers上的改变

我们不时需要修改FlatBuffers内部的值。由于FlatBuffers在设计上是不可变的,因此没有直接的方法可以做到这一点。我们提出的解决方案是与最初的FlatBuffer并排跟踪突变。

FlatBuffer中的每一条数据都可以通过其在FlatPuffer中的绝对位置进行唯一标识。我们支持改变,这样我们就不必重新下载整个故事来反映小的更新——比如友谊状态的变化。为了举例说明,以下是如何使用它来跟踪两个改变的概念可视化:

  • 在FlatBuffer的绝对索引2处的vtable槽指出了John的友谊状态。要更改John的状态,我们只需要记录绝对索引2对应的数据现在是1(表示朋友),而不是2(表示不是朋友,但发送了友谊请求)。
  • 玛丽的名字(“Mary”)由绝对索引13处的vtable槽指示。类似地,要更改Mary的名称,我们只需要记录与绝对索引13相对应的新String值。

最后,我们可以将所有改变打包到改变缓冲区中。改变缓冲区由两部分组成:改变指数和改变数据。改变索引记录从基本缓冲区中的绝对索引到新数据位置的映射。改变数据以FlatBuffer格式存储新数据。

使用FlatBuffers改善Facebook在Android上的性能

在FlatBuffers中查询一条数据时,我们可以计算出数据的绝对位置,查询改变缓冲区,看看是否发生了改变并返回,否则返回基本缓冲区中的数据。

平面模型

FlatBuffers不仅可以用于存储,还可以用于网络,以及应用程序中的内存格式。这消除了从服务器响应到UI显示的任何数据转换。这使我们能够朝着更清洁的平面模型体系结构迈进,从而消除了UI和存储层之间的额外复杂性。

当JSON被用作存储格式时,我们需要添加一个内存缓存来解决反序列化中的性能问题。我们最终还在UI和存储层之间添加了应用程序和网络逻辑。

使用FlatBuffers改善Facebook在Android上的性能

尽管这种三层架构在iOS和桌面上非常流行,但在Android上也存在一些重大问题:

  • 内存缓存通常意味着我们将在内存中保留比显示UI所需的多得多的内存。市场上许多安卓设备的每个应用程序内存限制仍然在48MB或以下。当您添加Java垃圾收集器的开销时,这可能会对性能产生影响。
  • 应用程序逻辑需要处理内存缓存、UI和存储,但通常与UI和存储相关的代码发生在不同的线程上。在大型应用程序中保持线程模型的简单性可能很困难。
  • UI通常从多个源接收数据,例如存储中的缓存数据、来自网络的新数据、来自应用程序逻辑的本地数据改变等等。这要求UI必须处理不同类型的数据更改场景,并可能导致UI透支。

使用平面模型方法,可以更容易地集成UI和存储层,如下图所示。

使用FlatBuffers改善Facebook在Android上的性能

UI使用标准的Android游标直接构建在存储之上,由于存储到UI是大多数Android应用程序中最热门的执行路径,这有助于保持UI的响应。

应用程序逻辑和网络组件已移动到存储层之下,允许所有逻辑在后台线程上进行,并确保结果首先反映在存储中。然后可以通过使用标准的Android内容提供商通知来通知UI重新绘制。

这种体系结构允许在UI和应用程序逻辑层之间进行清晰的分离,并且我们可以简化每一层的逻辑。UI组件只需要反映存储的状态,应用程序逻辑只需要将最终(正确的)信息写入存储层。UI和应用程序逻辑层位于不同的线程上,它们从不需要直接相互通信。

结论

FlatBuffers是一种数据格式,无需在存储和UI之间进行数据转换。在采用它的过程中,我们还推动了我们的应用程序(如平面模型)的额外架构改进。我们在FlatBuffers之上构建的可变扩展允许我们在单个结构中跟踪服务器数据、变化和本地状态,这使我们能够简化数据模型并向UI组件公开统一的API。

在过去的六个月里,我们已经将Android上的大部分Facebook转换为使用FlatBuffers作为存储格式。一些性能改进数字包括:

  • 从磁盘缓存加载故事的时间从每个故事35毫秒减少到4毫秒。
  • 瞬态内存分配减少了75%。
  • 冷启动时间提高了10-15%。
  • 我们已将存储空间缩减了15%。

令人兴奋的是,数据格式的选择允许人们花更多的时间阅读朋友的更新和查看家人的照片

原文链接:https://engineering.fb.com/2015/07/31/android/improving-facebook-s-performance-on-android-with-flatbuffers/

 

除特别注明外,本站所有文章均为老K的Java博客原创,转载请注明出处来自https://javakk.com/2848.html

关于

发表评论

表情 格式

暂无评论

登录

忘记密码 ?

切换登录

注册