跳转至

zram

来源: https://pengzhangdev.github.io/zram/

简介

在linux内核的mm子系统中, 包含三个支持内存压缩的组件, 分别是zram, zcache 和 zswap.
zswap: 前提条件是, 系统中存在swap设备, 内核版本>3.5, 因为依赖了frontswap的接口, 用于管理swap page. zswap是一个轻量化的后端架构, 将进程各种交换出的页面进行压缩, 并存储在一个基于RAM的内缓冲池中. 缓冲池不预分配, 有最大值, 且当缓冲池满了, LRU的内存页会被写入swap设备. 依赖swap分区, 不适用hdtv.
zcache: 与zswap类似, 依赖frontswap接口和cleacache接口, 内核版本>3.5. 与zswap一样, 将进程交换出的页面和文件系统的缓冲进行压缩并存储在RAM的内存缓冲池中, 在缓冲池满后, 将页面存入swap分区或者 RAMster (一种基于网络的内存池共享技术, 可以共享网络中其余硬件的内存). zswap可以理解为是zcache的子集. 不适用hdtv.
zram: 内存中的swap设备, 内核>3.14, 可以理解为zcache的RAM缓冲池. 优先级最高, 不建议与其他swap一起使用.不依赖真实的swap设备(Nand或者网络), 倾向于在低内存的嵌入式设备中使用.

综上, zram更适合hdtv这个项目

zram

zram是将系统中的内存的一部分内存隔离成zram设备(swap设备),对于小内存的设备, 在使用上会感内存被扩大了.

linux内核中为zram提供了各种压缩算法, 默认的是LZO, 该算法在大部分情况下都能很好地平衡压缩率和速度. LZO主要是在速度上优化.在一些比较正式的文档上, LZO的压缩率平均为50%. 下面是针对内核源码(50.6M)压缩的一个图表, 网上提供的.

lzop: 1.62 seconds to compress, 18.0 MB, ratio=.355
gzip: 19.13 seconds, 10.8 MB, ratio=.213
bzip2: 60.81 seconds, 8.46 MB, ratio=.176
xz: 311.65 seconds, 7.33 MB, ratio=.145


压缩率计算: 相关数据/sys/block/zram0/{mem_used_total,orig_data_size,compr_data_size}

compression radio = orig_data_size / compr_data_size
real compr. ratio  = orig_data_size / mem_used_total


优点

  • 内存扩大, 平均压缩比为2.0
  • 由于在实现上, 如果某个PAGE的压缩率大于PAGE_SIZE * 4 / 3 (kernel 3.10), 则不进行压缩, 在读取时, 也就省下解压的消耗. 从而提高速度,降低CPU占用,但是会降低压缩率.

缺点

  • CPU占用
  • 功耗增加
  • 由于占用一部分内存作为zram设备, 则如果zram压缩速度不够快, 反而更容易引发OOM.(github上的一个情况, 无数据信息, 在ZX2000上未出现该情况)

调整方法

CPU占用查看kswapd(kswap)

CPU占用调整:

  • /sys/block/zram0/max_comp_streams 设置压缩流个数. 默认为每个CPU分配1个.
  • /sys/block/zram0/comp_algorithm 切换压缩算法, 建议使用默认的LZO, 速度快


测试


以下数据是 在ZX2000上, UI切换和启动新应用换, 抓取的数据.
压缩率相关的三个值分别为

# busybox free                               
             total       used       free     shared    buffers     cached
Mem:        427400     365136      62264        408        348      72148
-/+ buffers/cache:     292640     134760
Swap:       204796     103784     101012


orig_data_size: 90275840
mem_used_total:  25862144
compr_data_size: 21846958
zero_pages:      1903


压缩率为:

compression radio(orig_data_size / compr_data_size) = 90275840 / 21846958 = 4.13
real compr. ratio (orig_data_size / mem_used_total) = 90275840 / 25862144 = 3.49

cpu 占用数据:

   44  1   0% S     1      0K      0K  fg root     kswapd0
   44  3   2% S     1      0K      0K  fg root     kswapd0
   44  3   0% S     1      0K      0K  fg root     kswapd0
   44  0   5% S     1      0K      0K  fg root     kswapd0
   44  3   1% S     1      0K      0K  fg root     kswapd0

5%是在启动新Activity的时候, 初步结论是, CPU占用不高.


以下数据是 在ZX1800上, UI切换和启动新应用换, 抓取的数据.

# ./busybox free                                  
             total       used       free     shared    buffers     cached
Mem:        427396     422720       4676       1688          0      42412
-/+ buffers/cache:     380308      47088
Swap:       204796     112748      92048

orig_data_size:  106278912
mem_used_total:  25624576
compr_data_size: 23845230
zero_pages:      3273


压缩率为:

compression radio(orig_data_size / compr_data_size) = 106278912 / 23845230 = 4.45
real compr. ratio (orig_data_size / mem_used_total) = 106278912 / 25624576 = 4.14

cpu 占用数据:

   44  3   1% S     1      0K      0K  fg root     kswapd0
   44  3   0% S     1      0K      0K  fg root     kswapd0
   44  0   4% S     1      0K      0K  fg root     kswapd0
   44  3   1% S     1      0K      0K  fg root     kswapd0
   44  0   0% S     1      0K      0K  fg root     kswapd0
   44  1   2% S     1      0K      0K  fg root     kswapd0
   44  3   0% S     1      0K      0K  fg root     kswapd0
   44  3   0% S     1      0K      0K  fg root     kswapd0
   44  3   0% S     1      0K      0K  fg root     kswapd0
   44  3   0% S     1      0K      0K  fg root     kswapd0
   44  3   0% S     1      0K      0K  fg root     kswapd0
   44  3   0% S     1      0K      0K  fg root     kswapd0
   44  3   1% S     1      0K      0K  fg root     kswapd0


内存申请测试

以下是在zx2000设备上, 通过每秒分配1M内存的方式, 内存中数据为urandom设备数据, 测试实际申请内存增加的值. 统计的数据如下.


在系统停留在主界面后, 小窗口播台, 在内存申请使用了167M后, 触发OOM. CPU占用如下, 触发OOM时, CPU占用达到8%

   44  0   0% S     1      0K      0K  fg root     kswapd0
   44  3   0% S     1      0K      0K  fg root     kswapd0
   44  3   0% S     1      0K      0K  fg root     kswapd0
   44  2   0% S     1      0K      0K  fg root     kswapd0
   44  1   0% S     1      0K      0K  fg root     kswapd0
   44  3   8% S     1      0K      0K  fg root     kswapd0
   44  1   8% S     1      0K      0K  fg root     kswapd0
   44  1   8% S     1      0K      0K  fg root     kswapd0

内存占用如下:

# busybox free     #开始时内存                                             
             total       used       free     shared    buffers     cached
Mem:        427400     423752       3648       2224       2540     105892
-/+ buffers/cache:     315320     112080
Swap:       204796          0     204796

# busybox free     # OOM 时内存                              
             total       used       free     shared    buffers     cached
Mem:        427400     422908       4492        292        200      11464
-/+ buffers/cache:     411244      16156
Swap:       204796     204796          0

zram信息如下:

# 开始使用zram不久
==============================================
status: enabled
compression: 1
disksize: 200M
compr_data_size: 1175K
orig_data_size: 2232K
mem_used_total: 2232K
max_comp_streams: 1
comp_algorithm: [lzo]

# 一段时间后
==============================================
status: enabled
compression: 2
disksize: 200M
compr_data_size: 3675K
orig_data_size: 11800K
mem_used_total: 4648K
max_comp_streams: 1
comp_algorithm: [lzo]

==============================================
status: enabled
compression: 3
disksize: 200M
compr_data_size: 15230K
orig_data_size: 55040K
mem_used_total: 16624K
max_comp_streams: 1
comp_algorithm: [lzo] 

==============================================
status: enabled
compression: 2
disksize: 200M
compr_data_size: 44355K
orig_data_size: 120788K
mem_used_total: 46196K
max_comp_streams: 1
comp_algorithm: [lzo] 

==============================================
status: enabled
compression: 1
disksize: 200M
compr_data_size: 137773K
orig_data_size: 200280K
mem_used_total: 144096K
max_comp_streams: 1
comp_algorithm: [lzo]

可以看到, 压缩比, 从1 -> 3 ->1, 也就是说在swap空或者快满是, 压缩比是最低的, 从最后触发OOM看, 通过压缩节省的内存有60M左右. 并且由于1M填充的都是urandom设备的内容, 本身导致压缩比比正常使用时偏低(几乎无0页, 数据随机).


在不启用zram的情况下, 内存申请在95 M的时候, 触发了OOM.

 # busybox free       # 开始内存申请时的内存                                             
             total       used       free     shared    buffers     cached
Mem:        427400     422932       4468       2224       2524     103748
-/+ buffers/cache:     316660     110740
Swap:            0          0          0


# busybox free       # OOM 时的内存                                            
             total       used       free     shared    buffers     cached
Mem:        427400     424052       3348       2224         36      16608
-/+ buffers/cache:     407408      19992
Swap:            0          0          0



以下是在zx1800设备上, 通过每秒分配1M内存的方式, 内存中数据为urandom设备数据, 测试实际申请内存增加的值. 统计的数据如下.

在启用zram的情况下, 申请内存达到166M时, 触发OOM. CPU 占用

   44  2   0% S     1      0K      0K  fg root     kswapd0
   44  2   1% S     1      0K      0K  fg root     kswapd0
   44  1   0% S     1      0K      0K  fg root     kswapd0
   44  2   0% S     1      0K      0K  fg root     kswapd0
   44  2   0% S     1      0K      0K  fg root     kswapd0
   44  3   0% S     1      0K      0K  fg root     kswapd0
   44  3   0% S     1      0K      0K  fg root     kswapd0
   44  3   0% S     1      0K      0K  fg root     kswapd0
   44  2   0% S     1      0K      0K  fg root     kswapd0
   44  2   0% R     1      0K      0K  fg root     kswapd0
   44  1   0% S     1      0K      0K  fg root     kswapd0
   44  3   0% S     1      0K      0K  fg root     kswapd0
   44  1   0% S     1      0K      0K  fg root     kswapd0
   44  1   0% S     1      0K      0K  fg root     kswapd0
   44  0   0% S     1      0K      0K  fg root     kswapd0
   44  1   2% R     1      0K      0K  fg root     kswapd0

压缩比的数据与zx2000 emmc相同, 特别是在zram剩余大小不多时, 压缩比快速缩小到1.4(从3降到1)左右. 以下是free的输出.

# ./busybox free           #系统启动完成时                 
             total       used       free     shared    buffers     cached
Mem:        427396     422192       5204       2224          4     109536
-/+ buffers/cache:     312652     114744
Swap:       204796          0     204796


# ./busybox free        # OOM 
             total       used       free     shared    buffers     cached
Mem:        427396     423280       4116        264          0      13268
-/+ buffers/cache:     410012      17384
Swap:       204796     204796          0

在不开启zram的情况下, 申请的内存达到96M时触发OOM, 以下是free的输出.

# ./busybox free         # 系统启动完成                      
             total       used       free     shared    buffers     cached
Mem:        427396     411928      15468       2224          0     107552
-/+ buffers/cache:     304376     123020
Swap:            0          0          0


# ./busybox free         # 触发OOM                         
             total       used       free     shared    buffers     cached
Mem:        427396     419892       7504       2224          0      15372
-/+ buffers/cache:     404520      22876
Swap:            0          0          0

综上, 与zx2000相同, 可用内存增加70M左右.

OOM测试

基于zx2000 8月18日的autobuild系统, 在分别不启用和启用zram的情况下, 测试对OOM的影响.

执行操作如下:

  1. 重启系统.
  2. 通过UI界面分别启动本地视频, 本地音乐 和本地图片三个应用.
  3. 按遥控器退出键, 回到电视直播界面.

在未启用zram的情况下, 执行以上操作, 必定触发OOM. 在启用zram的情况下, 执行以上操作, 未触发OOM.

基于zx1800 8月18日的autobuild系统, 在分别不启用和启用zram的情况下, 测试对OOM的影响.

执行操作如下:

  1. 重启系统.
  2. 通过UI界面分别启动本地视频, 本地音乐 和本地图片三个应用.
  3. 按遥控器退出键, 回到电视直播界面.

在未启用zram的情况下, 执行以上操作, 必定触发OOM. 并且在以上步骤之后, 重复按导视键和退出键, 非常卡, 甚至黑屏几秒钟. 在启用zram的情况下, 执行以上操作, 不会触发OOM. 并且在以上步骤之后, 重复按导视键和退出键, 只有第一次导视键卡顿, 之后都不卡.

在Android启用方法

https://source.android.com/devices/tech/perf/low-ram
上面这个链接是Android官网给出来的对于低内存设备(512M)的内存优化建议(内核/系统/应用等), 其中有在Android上启用zram的整个配置流程.
在hisi提供的TVOS2.0代码中, 已经实现Android官方提供的内存优化建议, 进行了优化, 其中包括zram的配置, 可以直接参考也使用.
官方建议, 使用总内存的 30%-50% 作为zram, 按照压缩率为50%计算, 则内存增加30%-50%左右.

Swap to zRAM

zRAM swap can increase the amount of memory available in the system by compressing memory pages and putting them in a dynamically allocated swap area of memory.

Again, since this is trading off CPU time for a small increase in memory, you should be careful about measuring the performance impact zRAM swap has on your system.

Android handles swap to zRAM at several levels:

First, the following kernel options must be enabled to use zRAM swap effectively:
    CONFIG_SWAP
    CONFIG_CGROUP_MEM_RES_CTLR
    CONFIG_CGROUP_MEM_RES_CTLR_SWAP
    CONFIG_ZRAM
Then, you should add a line that looks like this to your fstab:

/dev/block/zram0 none swap defaults zramsize=<size in bytes>,swapprio=<swap partition priority>

    zramsize is mandatory and indicates how much uncompressed memory you want the zram area to hold. Compression ratios in the 30-50% range are usually observed.
    swapprio is optional and not needed if you don't have more than one swap area.

You should also be sure to label the associated block device as a swap_block_device in the device-specific sepolicy/file_contexts so that it is treated properly by SELinux.

/dev/block/zram0 u:object_r:swap_block_device:s0

By default, the Linux kernel swaps in 8 pages of memory at a time. When using ZRAM, the incremental cost of reading 1 page at a time is negligible and may help in case the device is under extreme memory pressure. To read only 1 page at a time, add the following to your init.rc:

write /proc/sys/vm/page-cluster 0

In your init.rc after the mount_all /fstab.X line, add:

swapon_all /fstab.X

The memory cgroups are automatically configured at boot time if the feature is enabled in kernel.
If memory cgroups are available, the ActivityManager will mark lower priority threads as being more swappable than other threads. If memory is needed, the Android kernel will start migrating memory pages to zRAM swap, giving a higher priority to those memory pages that have been marked by ActivityManager.


成功例子

  • chrome os
  • debian/ubuntu
  • 第三方Android ROM 和 TV

评论