Jerasure_encode&decode源码解析

encoder

Jerasure源码

「探秘」Jerasure那些事 - Durant Thorvalds 的米奇妙妙屋

//			inputfile								 k	m     tech	     w p_size b_size 
./encoder "/home/han/data/Jerasure/demo_data/pic.jpg" 4 2 "reed_sol_van" 8 8 1024 
参数

(argv[0]是指向可执行文件的文件名,argv[1-8]是参数,argc是8)

  • inputfile:需要被编码的文件。
  • k:将文件划分的数据块的数量
  • m:根据数据块生成的校验块的数量
  • coding_technique:
    • 使用的编码算法。可选的有"reed_sol_van", “reed_sol_r6_op”, “cauchy_orig”, “cauchy_good”, “liberation”, “blaum_roth”, “liber8tion”, “no_coding”,在这里我们使用"reed_sol_van"
  • w:bit-word size。
    • 范得蒙德算法的w只能从{8, 16, 32}中选取,这里我们选择8
  • packetsize:
    • 以packetsize bytes大小的包为单位进行编码, packetsize个word。决定了packet的大小这里我们选择8
  • buffer size:
    • 每次处理的数据大小,这里我们选择1024 .buffersize是源数据在编码分片时设置的缓冲区大小,设置缓冲区的目的是当用户编码的文件过大,读入内存的数据过多会导致编码效率下降,所以选择合适大小的分段缓存可以让编码效率最高。
    • 另一个作用是用户节点在下载文件时解码也要用相同的大小buffersize才能正确聚合文件,在一定程度上保护了用户数据的隐私性。这个数值在编码运行时会自动调整,调整到适当的大小,目的是更快地(以最少次数readins)将文件读入内存。
处理参数
对buffersize向上取整
  • buffersize%(sizeof(long)*w*k*packetsize) != 0)
    // buffersize应该是 8*w*k*packetsize的整数倍 ----- 为什么???
    
    
    // 在这里,我的buffersize被向上取整到  2048 
        han@han-virtual-machine:~/data/Jerasure/Examples$ ./encoder "/home/han/data/Jerasure/demo_data/pic.jpg" 4 2 "reed_sol_van" 8 8 1024 
        00 >> buffersize = 1024
        sizeof(long)*w*k*packetsize = 8 * 8 * 4 * 8  = 2048 
        11  >> buffersize = 2048
        >> blocksize = 512
        22 >> buffersize = 2048
    
确定size大小
stat(argv[1], &status);	
size = status.st_size; // 确定 size的大小 - 文件的size
确定newsize

newsize取整后的size, 便于分割成k个block

// newsize 需要是 k*w *packetsize *8 的整数倍
while (newsize%(k*w*packetsize*sizeof(long)) != 0) 
    newsize++;
// 同时 newsize需要是 buffersize的整数倍
if (buffersize != 0) {
    while (newsize%buffersize != 0) {
        newsize++;
    }
}
确定blocksize
// blocksize 是对于文件分片之后的大小
// 是一次读入buffer后的处理单位 - block
blocksize = newsize/k;
考虑到size小于buffersize和size大于buffersize的情况
if (size > buffersize && buffersize != 0) { // size大于 buffersize的情况
    // 确定readins的大小 - 即从外存读入内存的次数
    // 需要分多次将file读入到data中
    if (newsize%buffersize != 0) {
        readins = newsize/buffersize;
    }
    else {
        readins = newsize/buffersize;
    }
    // 这里的block是 k个block的合集而不是单个block,严谨来说,应该是 blocks
    block = (char *)malloc(sizeof(char)*buffersize);
    blocksize = buffersize/k;
}
else {	// size小于buffersize的情况 - buffer收缩
    readins = 1;
    buffersize = size;	// buffersize收缩到size大小
    block = (char *)malloc(sizeof(char)*newsize);
}
编码过程
// 一次编码过程 是以 buffersize为单位对源文件进行划分
	// 一次读入buffersize大小 
	// 然后对buffer分割成block
内存容器 data and coding

data和coding相当于是encoder的内存容器

  • coding是每次malloc为0的
  • data是直接指向jread进内存的block

在一次encode过程中,

	/* Allocate data and coding */
		// data是 k 个一维数组
	data = (char **)malloc(sizeof(char*)*k);	
	coding = (char **)malloc(sizeof(char*)*m);

	for (i = 0; i < m; i++) {
        // coding是 初始化(malloc)出来的
        	// 用于存放编码之后的结果
		coding[i] = (char *)malloc(sizeof(char)*blocksize);
                if (coding[i] == NULL) { perror("malloc"); exit(1); }
	}
...



...
    
    if (total < size && total+buffersize <= size) {
        // 一次读入一个buffersize大小到 block中
        total += jfread(block, sizeof(char), buffersize, fp);
    }
...
    
    /* Set pointers to point to file data */
    	// data直接指向block
    for (i = 0; i < k; i++) {
        data[i] = block+(i*blocksize); 
    }

在这里插入图片描述

生成coding_matrix
// 生成一个matrix 对应于 tech, 这里生成的是范德蒙德矩阵
	// 可以打印一下这个矩阵来看具体是什么样的
matrix = reed_sol_vandermonde_coding_matrix(k, m, w);
printf("打印matrix: \n");
for(int i = 0; i < m; i++)
{
    for(int j = 0; j<k; j++)                         
    {
        printf("%d ", matrix[i]);
    }
    printf("\n");
}
// 这里生成的范德蒙德矩阵很奇怪
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1

// 根据data中的数据块进行编码,结果放置在coding中
jerasure_matrix_encode(k, m, w, matrix, data, coding, blocksize);
写入文件
// 将data分片写入到各个文件中
	// 即 k文件中的相邻两个block在源文件中的间隔为 buffersize
fwrite(data[i-1], sizeof(char), blocksize, fp2);

// coding同理
fwrite(coding[i-1], sizeof(char), blocksize, fp2);

在这里插入图片描述

速度计算
// totalsec =  gen_matrix + encoding 算法层面的时间开销
printf("Encoding (MB/sec): %0.10f\n", (((double) size)/1024.0/1024.0)/totalsec);

// tsec 是程序全部的时间开销(系统的开销)
printf("En_Total (MB/sec): %0.10f\n", (((double) size)/1024.0/1024.0)/tsec);

对于storage而言,在算法层面的开销并不是我们努力的方向,我们应当针对系统的IO开销,并发度等角度进行优化,这样对于系统整体性能的提升更加有效

decoder

./decoder "/home/han/data/Jerasure/demo_data/pic.jpg"
读取meta文件中的参数
// encoder中写入meta文件的code
sprintf(fname, "%s/Coding/%s_meta.txt", curdir, s1);
fp2 = fopen(fname, "wb");

fprintf(fp2, "%s\n", argv[1]);	// filename
fprintf(fp2, "%d\n", size);		// file_size
fprintf(fp2, "%d %d %d %d %d\n", k, m, w, packetsize, buffersize); // 参数
fprintf(fp2, "%s\n", argv[4]);	// tech
fprintf(fp2, "%d\n", tech);		// (enum Coding_Technique)Reed_Sol_Van = 0
fprintf(fp2, "%d\n", readins);	// 读入buffer的次数

fclose(fp2);

// meta.txt 中的内容
/home/han/data/Jerasure/demo_data/pic.jpg
339206
4 2 8 8 2048
reed_sol_van
0
166
decode过程
open files

在循环的逻辑中可以看到,代码中用一个fp轮询地指向4个文件

所以在一次循环中,对于4个文件的偏移是相同的,

// erased erasures 是什么????
	// erased - 对k+m块的标记
	// erasures - 坏块索引
sprintf(fname, "%s/Coding/%s_k%0*d%s", curdir, cs1, md, i, extension); // k块 (k从1开始)
fp = fopen(fname, "rb");
对于坏块(擦除块)
if (fp == NULL) {	// 打开文件失败,意味着该块擦除
    erased[i-1] = 1;			// 标记	
    erasures[numerased] = i-1;	// 坏块索引
    numerased++;
    //printf("%s failed\n", fname);
}
打开成功

​ 对于没有丢失的块,如何判断将其作为decode所用的块? - 不考虑,这里是对坏块擦除之后的decode处理过程

malloc data容器 (幸存块对应的)

根据buffersize是否收缩过进行选择分支

else {	// 打开文件成功
    if (buffersize == origsize) {	// buffersize == origsize 意味着buffersize是收缩到origsize的
        stat(fname, &status);
        blocksize = status.st_size;
        data[i-1] = (char *)malloc(sizeof(char)*blocksize);
        // 将 k_file 读入 data容器中
        assert(blocksize == fread(data[i-1], sizeof(char), blocksize, fp));
    }
    else {	// 如果buffersize 不等于 初始size 
        		// (我这里的buffersize是调整之后的buffersize - 2048)
        	// 需要对 文件指针 进行 n*blocksize 的偏移
        fseek(fp, blocksize*(n-1), SEEK_SET); 
        // 这里的blocksize和 buffersize/k不同吗?
        	// 如果buffersize是收缩之后的, 用buffersize/k就会出错
        		// 但是为什么不统一使用 blocksize呢?
        			//因为在这个逻辑分支中,blocksize = 0 (丑) 	
        assert(buffersize/k == fread(data[i-1], sizeof(char), buffersize/k, fp));
    }
    fclose(fp);
}
data容器
malloc data容器 (擦除块对应的)
if (n == 1) {
    for (i = 0; i < numerased; i++) {
        if (erasures[i] < k) {
            // 幸存块 对应的data容器已经在上面的open_file流程中 malloc并fread好了
            // 擦除块 的data则需要malloc然后重新计算
            data[erasures[i]] = (char *)malloc(sizeof(char)*blocksize);
        }
        else {
            coding[erasures[i]-k] = (char *)malloc(sizeof(char)*blocksize);
        }
    }
}
Decoding
if (tech == Reed_Sol_Van || tech == Reed_Sol_R6_Op) {
    // 调库接口
    i = jerasure_matrix_decode(k, m, w, matrix, 1, erasures, data, coding, blocksize);
}
jerasure_matrix_decode

这里存在一个优化策略 - row_k_ones

如果matrix的第一行是全1,可以借此优化一下

因为如果是这种情况,易知 m1 = k1 + k2 + k3 + k4

如果此时的data块只擦除了一块,而且m1块没有擦除,显然是可以较容易地不需要decode_matrix就能够decode出来的

int jerasure_matrix_decode(int k, int m, int w, int *matrix, int row_k_ones, int *erasures,
                           char **data_ptrs, char **coding_ptrs, int size)
{
    // 通过 erasures 得到 erased, 坏块索引
    erased = jerasure_erasures_to_erased(k, m, erasures);	
    
    /* Find the number of data drives failed */
    lastdrive = k;
    edd = 0;
    for (i = 0; i < k; i++) {
        if (erased[i]) {
            edd++;			// 得到坏块数量
            lastdrive = i; 	// 得到最后一块坏块的编号
        }
    }

    // 如果 没有采用加速 或者是 编码块的第一块坏了, 则将last设置为k
    	// 换句话说, 如果可以加速, 则lastdrive不是k, 而是小于k的最后一个坏掉的数据块
    if (!row_k_ones || erased[k]) lastdrive = k;

    dm_ids = NULL;
    decoding_matrix = NULL;

    // 对于不能直接通关的情况 - 
    	// 只有在 可以加速而且 第一个编码块没有坏掉的情况下 才可以直接通关
    	// 直接通关不需要建立 decoding_matrix
    if (edd > 1 || (edd > 0 && (!row_k_ones || erased[k]))) {
        dm_ids = talloc(int, k);
        decoding_matrix = talloc(int, k*k);
        jerasure_make_decoding_matrix(k, m, w, matrix, erased, decoding_matrix, dm_ids);
    }

    // decode - k_data
    for (i = 0; edd > 0 && i < lastdrive; i++) {
        if (erased[i]) {
            printf(">> 111\n");
            jerasure_matrix_dotprod(k, w, decoding_matrix+(i*k), dm_ids, i, data_ptrs, coding_ptrs, size);
            edd--;
        }
    }

    // decode lastdrive  只要m1没有坏掉就会调用这里?
    if (edd > 0) {
        tmpids = talloc(int, k);
        for (i = 0; i < k; i++) {
            tmpids[i] = (i < lastdrive) ? i : i+1;
        }
        printf(">> 222\n");
        jerasure_matrix_dotprod(k, w, matrix, tmpids, lastdrive, data_ptrs, coding_ptrs, size);
    }

    // decode m_coding
    for (i = 0; i < m; i++) {
        if (erased[k+i]) {
            printf(">> 333\n");
            jerasure_matrix_dotprod(k, w, matrix+(i*k), NULL, i+k, data_ptrs, coding_ptrs, size);
        }
    }
}

decode过程

  • 先对 k 个data块

    •  jerasure_matrix_dotprod(k, w, decoding_matrix+(i*k), dm_ids, i, data_ptrs, coding_ptrs, size);
      
  • 对 lastdrive 对应的块

    • jerasure_matrix_dotprod(k, w, matrix, tmpids, lastdrive, data_ptrs, coding_ptrs, size);
      
  • 对 m 个decoding块

    • jerasure_matrix_dotprod(k, w, matrix+(i*k), NULL, i+k, data_ptrs, coding_ptrs, size);
      
  • k1 - lastdrive = 0 - 调用分支2

  • k2 - lastdrive = 1 - 2

  • k3 - lastdrive = 2 - 2

  • k4 - lastdrive = 3 - 2

  • m1 - lastdrive = 4 - 3

  • m2 - lastdrive = 4 - 3

  • k4,m1 - lastdrive = 4 - 1,3

    • 因为m1坏掉,所以lastdrive = k = 4
  • k1,k2 - lastdrive = 1 - 1,2

  • k2,k4 - lastdrive = 3 - 1,2

  • m1,m2 - 3

  • k3,m2 - lastdrive = 2, - 2,3

一些优化策略:

  • 如果只坏了 0个k块, 或者擦除1个而且 m1没有擦除,这种情况下不需要生成decodingmatrix矩阵,只需要用matrix就行

  • lastdrive的优化策略

    在m1幸存的条件下,lastdrive是幸存块的最后一块

    在m1擦除的情况下,lastdrive是m1

    对于lastdrive块的decode,可以独立于k块的decode,举个例子,如果是k1,k2坏掉,那么k2是lastdrive,此时k1通过 1 来复原, 而k2通过 2 来复原

    (如果lastdrive是m1的情况下,此时不会触发lastdrive逻辑分支,而是会转到 m块的decode逻辑分支)

Create decoded file

写入文件,只会还原出文件,但是不会还原 块文件

for (i = 0; i < k; i++) {
    if (total+blocksize <= origsize) { // 判断是整块还是最后一块
        fwrite(data[i], sizeof(char), blocksize, fp);	// 将data写入到文件中
        total+= blocksize;
    }
    else {
        for (j = 0; j < blocksize; j++) {
            if (total < origsize) {
                fprintf(fp, "%c", data[i][j]);	// 按字节将剩余的tail写入文件
                total++;
            }
            else {
                break;
            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值