C: Как прочитать часть файла кусками
Я должен реализовать для назначения курса алгоритм шифрования и дешифрования Хаффмана сначала классическим способом, затем я должен попытаться сделать его параллельным, используя различные методы (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
байтов из входного файла, а затем шифрует их. Я использовал memset
вызов для случая, когда bytes_read < MAX_BUFF_SZ
(возможно, существует более чистое решение).
Тем не менее, для параллельной версии (например, с использованием openMP) я хочу, чтобы каждый поток анализировал только часть файла, но чтение должно выполняться по частям. Зная, что каждый поток имеет и идентификатор 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
сделает всю работу и 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] = '\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-связанный, конец истории, анализ завершен".
1 ответ
Видимо, мне просто нужно было взять ручку и бумагу и сделать небольшую схему. Поиграв с некоторыми индексами, я получил следующий код (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] = '\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 МБ, параллельный начинает бить последовательный с 35-45%. Спасибо всем за совет.
Ура!