【问题标题】:C: How to read portion of a file in chunksC:如何以块的形式读取文件的一部分
【发布时间】:2018-11-22 19:57:42
【问题描述】:

我必须首先以经典方式实现 Huffman 加密和解密算法的课程作业,然后我必须尝试使用​​各种方法(openMPMPIphtreads)使其并行。该项目的范围不是让它必须更快,而是分析结果并讨论它们以及它们为什么会这样。

串行版本完美运行。但是,对于并行版本,我偶然发现了从文件读取问题。在串行版本中,我有一段代码如下所示:

char *buffer = calloc(1, MAX_BUFF_SZ);

while (bytes_read = fread(buffer, 1, MAX_BUFF_SZ, input) > 0) {
    compress_chunk(buffer, t, output);
    memset(buffer, 0, MAX_BUFF_SZ);
}

这最多从输入文件中读取MAX_BUFF_SZ 字节,然后对其进行加密。我使用memset 调用bytes_read < MAX_BUFF_SZ 时的情况(尽管可能存在更清洁的解决方案)。

但是,对于并行版本(例如使用 openMP),我希望每个线程仅分析文件的一部分,但仍以块的形式进行读取。知道每个线程都有和 id thread_id 并且最多有total_threads,我计算开始和结束位置如下:

int slice_size = (file_size + total_threads - 1) / total_threads;
int start = slice_size * thread_id;
int end = min((thread_id + 1) * slice_size, file_size);

我可以通过简单的fseek(input, start, SEEK_SET) 操作移动到起始位置。但是,我无法分块阅读内容。我尝试了以下代码(只是为了确保操作正常):

int total_bytes = 0;
while ((bytes_read = fread(buffer, 1, MAX_BUFF_SZ, input)) > 0) {
    total_bytes += bytes_read;

    if (total_bytes >= end) {
        int diff = total_bytes - end;
        buffer[diff] = '\0';
        break;
    }

    fwrite(buffer, 1, bytes_read, output);
    memset(buffer, 0, MAX_BUFF_SZ);
}

output 是每个线程的不同文件。即使我只尝试 2 个线程,它们也会缺少一些字符。我认为我已经接近正确的解决方案了,但我遇到了类似错误的情况。

所以问题是:我怎样才能读取文件的一个片段,但要分块?你能帮我找出上面代码中的错误并让它工作吗?

编辑: 如果MAX_BUFF_SZ 大于输入的大小并且我将拥有例如4 个线程,那么干净的代码应该如何确保T0 能够完成所有工作以及T1T2 和@ 987654338@什么都不做?

一些可以用来测试行为的简单代码如下(注意不是来自霍夫曼代码,是一些辅助代码来测试东西):

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <omp.h>

#define MAX_BUFF_SZ 32

#define min(a, b) \
   ({ __typeof__ (a) _a = (a); \
       __typeof__ (b) _b = (b); \
     _a < _b ? _a : _b; })

int get_filesize(char *filename) {
    FILE *f = fopen(filename, "r");
    fseek(f, 0L, SEEK_END);
    int size = ftell(f);
    fclose(f);

    return size;
}

static void compress(char *filename, int id, int tt) {
    int total_bytes = 0;
    int bytes_read;
    char *newname;
    char *buffer;
    FILE *output;
    FILE *input;
    int fsize;
    int slice;
    int start;
    int end;

    newname = (char *) malloc(strlen(filename) + 2);
    sprintf(newname, "%s-%d", filename, id);

    fsize = get_filesize(filename);
    buffer = calloc(1, MAX_BUFF_SZ);

    input = fopen(filename, "r");
    output = fopen(newname, "w");

    slice = (fsize + tt - 1) / tt;
    end = min((id + 1) * slice, fsize);
    start = slice * id;

    fseek(input, start, SEEK_SET);

    while ((bytes_read = fread(buffer, 1, MAX_BUFF_SZ, input)) > 0) {
        total_bytes += bytes_read;
        printf("%s\n", buffer);

        if (total_bytes >= end) {
            int diff = total_bytes - end;
            buffer[diff] = '\0';
            break;
        }

        fwrite(buffer, 1, bytes_read, output);
        memset(buffer, 0, MAX_BUFF_SZ);
    }

    fclose(output);
    fclose(input);
}

int main() {
    omp_set_num_threads(4);
    #pragma omp parallel
    {
        int tt = omp_get_num_threads();;
        int id = omp_get_thread_num();
        compress("test.txt", id, tt);
    }
}

您可以使用gcc test.c -o test -fopenmp 编译它。您可以生成一个文件test.txt,其中包含一些随机字符,超过 32 个(或更改最大缓冲区大小)。

编辑 2: 同样,我的问题是分块读取文件的一部分,而不是分析本身。我知道该怎么做。这是一门大学课程,我不能只说“IO 绑定,故事结束,分析完成”。

【问题讨论】:

  • 线程读取不会使其更快。完全没用。
  • 该项目的范围不是让它更快,而是分析结果并谈论为什么它不更快。此外,在示例中,我将只读线程用于调试目的,真实版本也并行进行加密,因此每个线程将加密文件的一部分,然后我将它们合并。请阅读整篇文章:)
  • buffer[diff] = '\0'; - 这是错误的。想想total_bytes 何时完全等于enddiff 将为零。因此,您想保留整个缓冲区,并且还想将其写入输出文件,而目前您不想这样做。
  • 另外,您希望将total_bytes + startend 进行比较,而不仅仅是total_bytes(假设您进行了最初的fseek)。
  • @kfx 是的,我忘了在if 中添加fwrite(以写入剩余字节);我还将检查更改为int diff = total_bytes - end; buffer[total_bytes - diff] = '\0';,但仍然存在一些问题。

标签: c file openmp chunks


【解决方案1】:

显然我只需要拿一支笔和一张纸来制定一个小计划。在玩弄了一些索引之后,我得出了以下代码(encbuffwritten_bits 是我使用的一些辅助变量,因为我实际上是在将位写入文件并且我使用中间缓冲区来限制写入):

while ((bytes_read = fread(buffer, 1, MAX_BUFF_SZ, input)) > 0) {
        total_bytes += bytes_read;

        if (start + total_bytes > end) {
            int diff = start + total_bytes - end;
            buffer[bytes_read - diff] = '\0';
            compress_chunk(buffer, t, output, encbuff, &written_bits);
            break;
        }

        compress_chunk(buffer, t, output, encbuff, &written_bits);
        memset(buffer, 0, MAX_BUFF_SZ);
}

我也完成了 openMP 版本的实现。对于小文件,串行文件更快,但从 25+MB 开始,并行文件开始以 35-45% 的速度击败串行文件。谢谢大家的建议。

干杯!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-04-17
    • 2015-05-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-06
    • 1970-01-01
    相关资源
    最近更新 更多