C:如何分块读取文件的一部分
C: How to read portion of a file in chunks
我必须首先以经典方式为课程作业实现霍夫曼加密和解密算法,然后我必须尝试使用各种方法使其并行(openMP
,MPI
, phtreads
)。该项目的范围不是一定要让它更快,而是要分析结果并讨论它们以及为什么会这样。
连续版完美运行。但是,对于并行版本,我遇到了从文件读取问题。在串行版本中,我有一段代码如下所示:
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
个字节,然后对其进行加密。我在 bytes_read < MAX_BUFF_SZ
的情况下使用 memset
调用(尽管可能存在更清洁的解决方案)。
然而,对于并行版本(例如使用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] = '[=12=]';
break;
}
fwrite(buffer, 1, bytes_read, output);
memset(buffer, 0, MAX_BUFF_SZ);
}
output
是每个线程的不同文件。即使我只尝试使用 2 个线程,它们也会丢失一些字符。我想我已经接近正确的解决方案了,而且我遇到了一个错误的问题。
所以问题是:我怎样才能读取文件的一部分,但是是块?你能帮我找出上面代码中的错误并让它工作吗?
编辑:
如果 MAX_BUFF_SZ
大于输入的大小并且我将有例如 4 个线程,那么干净的代码应该如何确保 T0
将完成所有工作并且 T1
, T2
和 T3
什么都不做?
一些可用于测试行为的简单代码如下(注意不是来自霍夫曼代码,是一些用于测试的辅助代码):
#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] = '[=13=]';
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 bound, end of story, analysis complete"。
看来我只需要拿笔和纸做一个小方案就可以了。在玩弄了一些索引之后,我得出了以下代码(encbuff
和 written_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] = '[=10=]';
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% 的优势击败串行文件。谢谢大家的指点。
干杯!
我必须首先以经典方式为课程作业实现霍夫曼加密和解密算法,然后我必须尝试使用各种方法使其并行(openMP
,MPI
, phtreads
)。该项目的范围不是一定要让它更快,而是要分析结果并讨论它们以及为什么会这样。
连续版完美运行。但是,对于并行版本,我遇到了从文件读取问题。在串行版本中,我有一段代码如下所示:
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
个字节,然后对其进行加密。我在 bytes_read < MAX_BUFF_SZ
的情况下使用 memset
调用(尽管可能存在更清洁的解决方案)。
然而,对于并行版本(例如使用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] = '[=12=]';
break;
}
fwrite(buffer, 1, bytes_read, output);
memset(buffer, 0, MAX_BUFF_SZ);
}
output
是每个线程的不同文件。即使我只尝试使用 2 个线程,它们也会丢失一些字符。我想我已经接近正确的解决方案了,而且我遇到了一个错误的问题。
所以问题是:我怎样才能读取文件的一部分,但是是块?你能帮我找出上面代码中的错误并让它工作吗?
编辑:
如果 MAX_BUFF_SZ
大于输入的大小并且我将有例如 4 个线程,那么干净的代码应该如何确保 T0
将完成所有工作并且 T1
, T2
和 T3
什么都不做?
一些可用于测试行为的简单代码如下(注意不是来自霍夫曼代码,是一些用于测试的辅助代码):
#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] = '[=13=]';
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 bound, end of story, analysis complete"。
看来我只需要拿笔和纸做一个小方案就可以了。在玩弄了一些索引之后,我得出了以下代码(encbuff
和 written_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] = '[=10=]';
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% 的优势击败串行文件。谢谢大家的指点。
干杯!