쉘 스크립트를 사용하여 다음을 수행하고 싶습니다. (단순화를 위해 INPUT에 동일한 데이터를 사용하고 있습니다. 실제 생활에서는 루프 레이블 jj에 따라 데이터가 변경됩니다.)
#!/bin/sh
for jj in `seq 100`; do
cat INPUT.file >> OUTPUT.file
done
그러나 이는 파일을 열고 닫는 작업이 루프로 이루어지기 때문에 매우 비효율적입니다. INPUT.file이 크면 이 코드가 매우 느려질 수 있습니다. 그래서 C에서 미리 할당된 변수를 생성하는 것처럼 버퍼를 가지거나 생성할 수 있는 방법이 있는지 궁금합니다.
답변1
감사해요스티븐 차제라스답변"echo와 cat의 실행 시간에 왜 이렇게 큰 차이가 있나요?", 정답은무루한 번만 호출하면 cat
개선될 수 있습니다(그러나 데이터가 크고 루프 반복이 많으면 이 "약간의" 양이 많아질 수 있습니다. 내 시스템에서 이 스크립트는 루프 스크립트에 걸리는 시간의 약 75%를 차지합니다).
#!/bin/sh
yes INPUT.file | head -100 | xargs cat >> OUTPUT.file
답변2
리디렉션 루프 자체를 고려하십시오.
#!/bin/sh
for jj in seq 100; do
cat INPUT.file
done >> OUTPUT.file
답변3
속도가 주요 관심사라면 cat
이 작업을 충분히 빨리 완료할 수 없다는 것을 알게 될 수도 있습니다. 구성 요소 파일을 출력에 병렬로 쓸 수 있습니다.
cat
나는 다음과 같은 주의사항을 가지고 병렬 빠른 버전을 만들었습니다 :
- 모든 입력 파일은 일반 파일이어야 합니다(그래서 크기를 미리 알 수 있습니다).
fcat
실행 중에 입력 파일을 쓰거나 자르지 마십시오.- 출력 파일은 이미 존재할 수 없습니다(놀라움을 방지하고 곧 다룰 내용을 읽는 데 시간을 낭비하지 않기 위해).
분명히 이것은 빠른 개념 증명이므로 더 강력하게 만들 수 있지만 아이디어는 다음과 같습니다.
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;
}
파일 생성:
CFLAGS += -Wall -Wextra
CFLAGS += -std=c99 -D_GNU_SOURCE
CFLAGS += -g -O2
CFLAGS += -fopenmp
all: fcat
.PHONY:all
12개 스레드에 대한 내 타이밍은 0.2초의 런타임과 2.3초의 런타임을 보여주었습니다 cat
(각각 3개 실행, 핫 캐시 사용, 48개 파일, 총 138M).