Se a velocidade é sua principal preocupação, você pode achar que cat
não é rápido o suficiente nessa tarefa. Você pode escrever os arquivos constituintes para a saída em paralelo.
Eu criei uma versão rápida de um paralelo cat
com as seguintes advertências:
- todos os arquivos de entrada devem ser arquivos regulares (por isso, sabemos o tamanho antecipadamente).
- não escreva ou trunque os arquivos de entrada enquanto
fcat
estiver em execução
- o arquivo de saída ainda não existe (para evitar acidentes e também para evitar perder tempo lendo o que estamos prestes a sobrescrever).
Obviamente, esta é uma prova rápida de conceito, então pode ser mais robusta, mas aqui está a ideia:
fcat.c:
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
struct in_fd {
int fd;
int err;
off_t start;
struct stat s;
};
int main(int argc, char**argv)
{
char *outfile = argv[--argc];
if (argc < 2) {
fprintf(stderr, "Usage: %s INFILE... OUTFILE\n", argv[0]);
return 1;
}
struct in_fd *infiles = calloc(argc, sizeof *infiles);
#pragma omp parallel for
for (int i = 1; i < argc; ++i) {
struct in_fd *const input = infiles + i;
char const *const filename = argv[i];
input->err = 0;
if ((input->fd = open(filename, O_RDONLY)) < 0) {
perror(filename);
input->err = errno;
continue;
}
if (fstat(input->fd, &input->s)) {
perror(filename);
input->err = errno;
continue;
}
if (!S_ISREG(input->s.st_mode)) {
fprintf(stderr, "%s: not a regular file\n", filename);
input->err = EINVAL;
continue;
}
}
off_t total = 0;
for (int i = 1; i < argc; ++i) {
if (infiles[i].err)
return EXIT_FAILURE;
infiles[i].start = total;
total += infiles[i].s.st_size;
}
int out_fd = open(outfile, O_RDWR | O_CREAT | O_EXCL, 0666);
if (out_fd < 1) {
perror(outfile);
return 1;
}
if (ftruncate(out_fd, total)) {
perror(outfile);
return 1;
}
/* On Linux, you might wish to add MAP_HUGETLB */
char *out_mem = mmap(NULL, total, PROT_WRITE, MAP_SHARED, out_fd, 0);
if (out_mem == MAP_FAILED) {
perror(outfile);
return 1;
}
#pragma omp parallel for
for (int i = 1; i < argc; ++i) {
struct in_fd *const input = infiles + i;
char *p = out_mem + input->start;
char *end = p + input->s.st_size;
input->err = 0;
while (p < end) {
int r = read(input->fd, p, end-p);
if (r < 0) {
if (errno != EINTR) {
perror(argv[i]);
input->err = errno;
break;
}
} else {
p += r;
}
}
close(infiles->fd);
}
if (munmap(out_mem, total)) {
perror(outfile);
}
for (int i = 1; i < argc; ++i) {
if (infiles[i].err) {
unlink(outfile);
return EXIT_FAILURE;
}
}
return EXIT_SUCCESS;
}
Makefile:
CFLAGS += -Wall -Wextra
CFLAGS += -std=c99 -D_GNU_SOURCE
CFLAGS += -g -O2
CFLAGS += -fopenmp
all: fcat
.PHONY:all
Meus resultados de temporização com 12 encadeamentos mostram tempos decorridos de 0,2 segundos em comparação com 2,3 segundos para cat
(mediana de três execuções cada, com cache quente, em 48 arquivos totalizando 138M).