zram类似压缩饼干,是将RAM中暂时不用的部分RAM使用压缩算法压缩后,在写会RAM中,以达到RAM释放的功能。zram是一个块驱动,源码在kernel根目录下的drivers/block/zram下,相关源码这里不做分析,这篇就简单做一下压缩算法性能的对比。这次主要对比的算法是lzo、lz4和自行移植的zstd,不过目前自行移植的zstd算法还是有问题,尝试了各种方法都没办法fixed,所以就放出来当做zram算法移植指导嘛。后面有时间再讲这个bug fix掉吧。
前言
要做对比,当然需要比较方法。这里用我司的手机作为测试工具了,手机的CPU为MSM8953,2G RAM,16G ROM。测试压缩读写速度的工具在AOSP的源码就有提供了,源码路径为system/extram/zram-perf,source lunch后通过mmm即可编译得到zram-perf工具。如果要比较算法压缩率的话,可以通过在手机上打开大型程序,然后查看/sys/block/zram0/mm_stat,进行比较。
读写速度的获取,通过连续执行zram-perf三次,获取平均值。压缩率通过开启一个大型程序后连续读取两次mm_stat的数值,然后将程序放到后台后再开启另一大型程序,再次读入mm_stat的数值。mm_stat的各个数值代表如下:
1 2 3 4 5 6 7 |
orig_data_size compr_data_size mem_used_total mem_limit mem_used_max zero_pages num_migrated |
第一个数值为原始数据大小,第二个数值我压缩后的数值大小,压缩率可以通过第一个数值除以第二个数值即可。
如果这种测试手段存在问题的话,还请各位指出。
lzo
获取到的数据如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
启动第一个大型程序后cat的数值: 412217344 156366519 169369600 0 233803776 7063 0 414195712 157096334 169889792 0 233803776 7071 0 启动第二个大型程序后cat的数值: 509906944 201929513 219283456 0 292343808 7523 0 515739648 204356988 221143040 0 292343808 7595 0 开机后放置一段时间后cat的数值: 640942080 274121046 287473664 0 287817728 7047 0 执行三次zram-perf获取的数据: read: 134.039MB/s write: 78.9562MB/s read: 140.399MB/s write: 84.6824MB/s read: 140.615MB/s write: 84.5762MB/s |
lz4
获取到的数据如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
启动第一个大型程序后cat的数值: 535990272 236916864 254332928 0 284073984 7496 0 538959872 239291561 256839680 0 284073984 7505 0 启动第二个大型程序后cat的数值: 467673088 187495555 211316736 0 339877888 7435 0 469475328 188378044 211808256 0 339877888 7435 0 开机后放置一段时间后cat的数值: 629051392 299621606 312655872 0 319049728 7012 0 执行三次zram-perf获取的数据: read: 132.627MB/s write: 87.2539MB/s read: 137.859MB/s write: 86.3278MB/s read: 134.418MB/s write: 86.6448MB/s |
lzo vs lz4
按照上面的数值来看,貌似两种压缩算法的读写速率相差不大,但是压缩率貌似是lzo更胜一筹。这里存疑,毕竟网上多有说lz4的性能更好,一方面可能是内核内置的lz4算法版本较低或者压缩级别较低,其次可能是因为测试手段有问题。
移植zstd
zstd是Facebook开发的新一代压缩算法,在不牺牲压缩读写速度的前提下,拥有更高的压缩率。而且源码是开源的,所以可以将其移植到Linux内核上。只是博主现在移植后的zstd并不能很好的工作,所以这里贴出来和大家讨论讨论,另外看有没有大神指点下问题出在哪里了。
要移植zstd首先是获取zstd的源码。zstd的源码,Facebook已经将其开源到GitHub上了,大家可以通过git clone https://github.com/facebook/zstd.git
来下载zstd的源码。然后在zstd源码路径contrib/linux-kernel下有6个patch文件,我们将0001、0002和0005这三个patch文件复制到内核源码的根路径下,然后通过下面三条指令将patch打上:
1 2 3 |
1. patch -p1 < 0001-lib-Add-xxhash-module.patch 2. patch -p1 < 0002-lib-Add-zstd-modules.patch 3. patch -p1 < 0005-crypto-Add-zstd-support.patch |
打上上面三个patch后,还要做如下修改:
1.对crypto/zstd.c做如下修改:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
diff --git a/crypto/zstd.c b/crypto/zstd.c index 9a76b3e..c1bc62d 100644 --- a/crypto/zstd.c +++ b/crypto/zstd.c @@ -20,7 +20,6 @@ #include <linux/net.h> #include <linux/vmalloc.h> #include <linux/zstd.h> -#include <crypto/internal/scompress.h> #define ZSTD_DEF_LEVEL 3 @@ -111,24 +110,6 @@ static int __zstd_init(void *ctx) return ret; } -static void *zstd_alloc_ctx(struct crypto_scomp *tfm) -{ - int ret; - struct zstd_ctx *ctx; - - ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return ERR_PTR(-ENOMEM); - - ret = __zstd_init(ctx); - if (ret) { - kfree(ctx); - return ERR_PTR(ret); - } - - return ctx; -} - static int zstd_init(struct crypto_tfm *tfm) { struct zstd_ctx *ctx = crypto_tfm_ctx(tfm); @@ -142,12 +123,6 @@ static void __zstd_exit(void *ctx) zstd_decomp_exit(ctx); } -static void zstd_free_ctx(struct crypto_scomp *tfm, void *ctx) -{ - __zstd_exit(ctx); - kzfree(ctx); -} - static void zstd_exit(struct crypto_tfm *tfm) { struct zstd_ctx *ctx = crypto_tfm_ctx(tfm); @@ -177,13 +152,6 @@ static int zstd_compress(struct crypto_tfm *tfm, const u8 *src, return __zstd_compress(src, slen, dst, dlen, ctx); } -static int zstd_scompress(struct crypto_scomp *tfm, const u8 *src, - unsigned int slen, u8 *dst, unsigned int *dlen, - void *ctx) -{ - return __zstd_compress(src, slen, dst, dlen, ctx); -} - static int __zstd_decompress(const u8 *src, unsigned int slen, u8 *dst, unsigned int *dlen, void *ctx) { @@ -205,13 +173,6 @@ static int zstd_decompress(struct crypto_tfm *tfm, const u8 *src, return __zstd_decompress(src, slen, dst, dlen, ctx); } -static int zstd_sdecompress(struct crypto_scomp *tfm, const u8 *src, - unsigned int slen, u8 *dst, unsigned int *dlen, - void *ctx) -{ - return __zstd_decompress(src, slen, dst, dlen, ctx); -} - static struct crypto_alg alg = { .cra_name = "zstd", .cra_flags = CRYPTO_ALG_TYPE_COMPRESS, @@ -224,18 +185,6 @@ static struct crypto_alg alg = { .coa_decompress = zstd_decompress } } }; -static struct scomp_alg scomp = { - .alloc_ctx = zstd_alloc_ctx, - .free_ctx = zstd_free_ctx, - .compress = zstd_scompress, - .decompress = zstd_sdecompress, - .base = { - .cra_name = "zstd", - .cra_driver_name = "zstd-scomp", - .cra_module = THIS_MODULE, - } -}; - static int __init zstd_mod_init(void) { @@ -243,18 +192,11 @@ static int __init zstd_mod_init(void) - int ret; - - ret = crypto_register_alg(&alg); - if (ret) - return ret; - - ret = crypto_register_scomp(&scomp); - if (ret) - crypto_unregister_alg(&alg); - - return ret; + return crypto_register_alg(&alg); } static void __exit zstd_mod_fini(void) { crypto_unregister_alg(&alg); - crypto_unregister_scomp(&scomp); } module_init(zstd_mod_init); |
2.其次在crypto的Makefile中查看是否有如下一行,如果没有就添加上
1 |
obj-$(CONFIG_CRYPTO_ZSTD) += zstd.o |
3.接着需要到driver/block/zram目录下给zram添加zstd算法了,需要添加的代码可以模仿lz4进行添加,将下面的一个文件保存到driver/block/zram目录下,命名为zcomp_zstd.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 |
/* * Copyright (C) 2014 Sergey Senozhatsky. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ #include <linux/kernel.h> #include <linux/slab.h> #include <linux/zstd.h> #include <linux/vmalloc.h> #include <linux/mm.h> #include "zcomp_zstd.h" struct zstd_ctx { ZSTD_CCtx *cctx; ZSTD_DCtx *dctx; void *cwksp; void *dwksp; }; struct zstd_ctx *ctx = NULL; #define ZSTD_DEF_LEVEL 3 static ZSTD_parameters zstd_params(void) { return ZSTD_getParams(ZSTD_DEF_LEVEL, 0, 0); } static int zstd_comp_init(struct zstd_ctx *ctx) { int ret = 0; const ZSTD_parameters params = zstd_params(); const size_t wksp_size = ZSTD_CCtxWorkspaceBound(params.cParams); ctx->cwksp = vzalloc(wksp_size); if (!ctx->cwksp) { ret = -ENOMEM; goto out; } ctx->cctx = ZSTD_initCCtx(ctx->cwksp, wksp_size); if (!ctx->cctx) { ret = -EINVAL; goto out_free; } out: return ret; out_free: vfree(ctx->cwksp); goto out; } static int zstd_decomp_init(struct zstd_ctx *ctx) { int ret = 0; const size_t wksp_size = ZSTD_DCtxWorkspaceBound(); ctx->dwksp = vzalloc(wksp_size); if (!ctx->dwksp) { ret = -ENOMEM; goto out; } ctx->dctx = ZSTD_initDCtx(ctx->dwksp, wksp_size); if (!ctx->dctx) { ret = -EINVAL; goto out_free; } out: return ret; out_free: vfree(ctx->dwksp); goto out; } static void zstd_comp_exit(struct zstd_ctx *ctx) { vfree(ctx->cwksp); ctx->cwksp = NULL; ctx->cctx = NULL; } static void zstd_decomp_exit(struct zstd_ctx *ctx) { vfree(ctx->dwksp); ctx->dwksp = NULL; ctx->dctx = NULL; } static int __zstd_init(void *ctx) { int ret; ret = zstd_comp_init(ctx); if (ret) return ret; ret = zstd_decomp_init(ctx); if (ret) zstd_comp_exit(ctx); return ret; } static int __zstd_compress(const u8 *src, unsigned int slen, u8 *dst, unsigned int *dlen, void *ctx) { size_t out_len; struct zstd_ctx *zctx = ctx; const ZSTD_parameters params = zstd_params(); out_len = ZSTD_compressCCtx(zctx->cctx, dst, *dlen, src, slen, params); if (ZSTD_isError(out_len)) { ZSTD_ErrorCode errcode = ZSTD_getErrorCode(out_len); printk("zram: compress error code2 = %d", errcode); return -EINVAL; } *dlen = out_len; printk("zram: slen = %u, out_len = %zu\n", slen, out_len); return 0; } static int __zstd_decompress(const u8 *src, unsigned int slen, u8 *dst, unsigned int *dlen, void *ctx) { size_t out_len; struct zstd_ctx *zctx = ctx; out_len = ZSTD_decompressDCtx(zctx->dctx, dst, *dlen, src, slen); if (ZSTD_isError(out_len)) { ZSTD_ErrorCode errcode = ZSTD_getErrorCode(out_len); printk("zram: decompress error code2 = %d", errcode); return -EINVAL; } *dlen = out_len; printk("zram: slen = %u, out_len = %zu\n", slen, out_len); return 0; } static void *zcomp_zstd_create(void) { void *ret; /* * This function can be called in swapout/fs write path * so we can't use GFP_FS|IO. And it assumes we already * have at least one stream in zram initialization so we * don't do best effort to allocate more stream in here. * A default stream will work well without further multiple * streams. That's why we use NORETRY | NOWARN. */ ret = kzalloc(sizeof(*ctx), GFP_NOIO | __GFP_NORETRY | __GFP_NOWARN); if (!ret) ret = __vmalloc(sizeof(*ctx), GFP_NOIO | __GFP_NORETRY | __GFP_NOWARN | __GFP_ZERO | __GFP_HIGHMEM, PAGE_KERNEL); if(ret) __zstd_init(ret); ctx = ret; return ret; } static void zcomp_zstd_destroy(void *private) { zstd_comp_exit(private); zstd_decomp_exit(private); kvfree(private); ctx = NULL; } static int zcomp_zstd_compress(const unsigned char *src, unsigned char *dst, size_t *dst_len, void *private) { /* return : Success if return 0 */ // lz4_compress(src, PAGE_SIZE, dst, dst_len, private); return __zstd_compress(src, PAGE_SIZE, dst, (unsigned int *)dst_len, ctx); } static int zcomp_zstd_decompress(const unsigned char *src, size_t src_len, unsigned char *dst) { size_t dst_len = PAGE_SIZE; /* return : Success if return 0 */ return __zstd_decompress(src, src_len, dst, (unsigned int *)&dst_len, ctx); } struct zcomp_backend zcomp_zstd = { .compress = zcomp_zstd_compress, .decompress = zcomp_zstd_decompress, .create = zcomp_zstd_create, .destroy = zcomp_zstd_destroy, .name = "zstd", }; |
将下面一个文件保存为zcomp_zstd.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
/* * Copyright (C) 2014 Sergey Senozhatsky. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ #ifndef _ZCOMP_ZSTD_H_ #define _ZCOMP_ZSTD_H_ #include "zcomp.h" extern struct zcomp_backend zcomp_zstd; #endif /* _ZCOMP_ZSTD_H_ */ |
然后还要对driver/block/zram目录下做如下修改:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
diff --git a/drivers/block/zram/Kconfig b/drivers/block/zram/Kconfig index 6489c0f..9cdbcec 100755 --- a/drivers/block/zram/Kconfig +++ b/drivers/block/zram/Kconfig @@ -15,6 +15,16 @@ config ZRAM See zram.txt for more information. +config ZRAM_ZSTD_COMPRESS + bool "Enable ZSTD algorithm support" + depends on ZRAM + select ZSTD_COMPRESS + select ZSTD_DECOMPRESS + default n + help + This option enables ZSTD compression algorithm support. Compression + algorithm can be changed using `comp_algorithm' device attribute. + config ZRAM_LZ4_COMPRESS bool "Enable LZ4 algorithm support" depends on ZRAM diff --git a/drivers/block/zram/Makefile b/drivers/block/zram/Makefile index be0763f..d10a35a 100755 --- a/drivers/block/zram/Makefile +++ b/drivers/block/zram/Makefile @@ -1,5 +1,6 @@ zram-y := zcomp_lzo.o zcomp.o zram_drv.o zram-$(CONFIG_ZRAM_LZ4_COMPRESS) += zcomp_lz4.o +zram-$(CONFIG_ZRAM_ZSTD_COMPRESS) += zcomp_zstd.o obj-$(CONFIG_ZRAM) += zram.o diff --git a/drivers/block/zram/zcomp.c b/drivers/block/zram/zcomp.c index 6fbb10c..5fcb3b5 100755 --- a/drivers/block/zram/zcomp.c +++ b/drivers/block/zram/zcomp.c @@ -19,6 +19,9 @@ #ifdef CONFIG_ZRAM_LZ4_COMPRESS #include "zcomp_lz4.h" #endif +#ifdef CONFIG_ZRAM_ZSTD_COMPRESS +#include "zcomp_zstd.h" +#endif /* * single zcomp_strm backend @@ -48,6 +51,9 @@ static struct zcomp_backend *backends[] = { #ifdef CONFIG_ZRAM_LZ4_COMPRESS &zcomp_lz4, #endif +#ifdef CONFIG_ZRAM_ZSTD_COMPRESS + &zcomp_zstd, +#endif NULL }; |
4.最后在对应的kernel defconfig文件中添加如下内容,然后编译boot.img即可
1 2 3 4 5 |
CONFIG_CRYPTO_ZSTD=y CONFIG_ZRAM_ZSTD_COMPRESS=y CONFIG_ZSTD_COMPRESS=y CONFIG_ZSTD_DECOMPRESS=y CONFIG_XXHASH=y |
5.启动手机后,可以通过cat /sys/block/zram0/comp_algorithm可以看到有zstd的出现,然后选中zstd即可。
zstd出现的问题
目前移植的zstd还是有点问题的,但是博主自己分析了一周也不知道问题点出现在哪里,表示很郁闷。出现问题的现象就是:启用zstd后,打开应用使用一段时间后,cat /sys/block/zram0/mm_stat后会发现第一个数值和第二个数值相差无几,这表明数据没有被压缩到。所以就觉得异常的奇怪,因为代码也是参考zstd中的代码进行编写的,也看不出哪里有问题,奔溃。希望哪位大神知道原因可以给我留言。
hi 博主,
我也准备在4.9的kernel上移植,不过有个问题,请教下:
编译出错
crypto/zstd.c:23:39: fatal error: crypto/internal/scompress.h: No such file or directory
#include
您这边是直接去掉scompress,这个主要作用是啥?