파일 내용을 다시 읽을 것으로 예상되지 않고 상자에 메모리 압박이 있기 때문에 읽은 데이터를 페이지 캐시에 저장하지 않고 파일에서 순차적으로 데이터를 읽을 수 있어야 합니다(귀중한 메모리를 유용한 용도로 사용하고 싶음). 디스크 I/O 캐시).
내 질문은 이러한 읽기를 최적화하는 방법입니다. 읽고 있는 데이터가 디스크에 순차적으로(조각화 제외) 배치된다는 것을 알고 있으므로 미리 읽을 수 있기를 원하지만(/sys/block/sda/queue/read_ahead_kb를 늘려서) 이것이 가능할지 확실하지 않습니다. 읽는 데이터가 페이지 캐시에 저장되는 것을 방지하려면 posix_fadvise(POSIX_FADV_DONTNEED 플래그 포함)를 사용해야 한다는 이점이 있습니다.
미리 읽기 데이터는 페이지 캐시에서 데이터를 제거하라는 메시지로 간단히 삭제됩니까?
답변1
사용직접 IO:
직접 I/O는 운영 체제 읽기 및 쓰기 캐시를 우회하여 애플리케이션에서 저장 장치로 직접 파일을 읽고 쓰는 파일 시스템의 기능입니다. 직접 I/O는 자체 캐시(예: 데이터베이스)를 관리하는 애플리케이션에서만 사용됩니다.
애플리케이션은 이 플래그가 있는 파일을 열어 직접 I/O를 호출합니다
O_DIRECT
.
예를 들어:
int fd = open( filename, O_RDONLY | O_DIRECT );
Linux의 직접 IO는 이상하고 몇 가지 제한 사항이 있습니다. 애플리케이션 IO 버퍼는 페이지 정렬되어야 하며 일부 파일 시스템에서는 각 IO 요청이 페이지 크기의 정확한 배수여야 합니다. 이 마지막 제한으로 인해 파일의 마지막 부분을 읽고 쓰는 것이 어려울 수 있습니다.
애플리케이션에서 미리 읽기를 처리하는 코딩하기 쉬운 방법은 fdopen
대규모 페이지 정렬 버퍼를 사용하고 설정하여 수행 할 수 있습니다 posix_memalign
.setvbuf
// should really get page size using sysconf()
// but beware of systems with multiple page sizes
#define ALIGNMENT ( 4UL * 1024UL )
#define BUFSIZE ( 1024UL * 1024UL )
char *buffer;
...
int fd = open( filename, O_RDONLY | O_DIRECT );
FILE *file = fdopen( fd, "rb" );
int rc = posix_memalign( &buffer, ALIGNMENT, BUFSIZE );
rc = setvbuf( file, buffer, _IOFBF, BUFSIZE );
mmap()
이를 사용하여 버퍼에 사용할 익명 메모리를 얻을 수도 있습니다 . 이것의 장점은 자연스러운 페이지 정렬입니다.
...
char *buffer = mmap( NULL, BUFSIZE, PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0 );
rc = setvbuf( file, buffer, _IOFBF, BUFSIZE );
그런 다음 스트림 에서 읽으려는 유형에 대해 fread()
/ fgets()
또는 읽기 기능을 사용하십시오 .FILE *
file
strace
예를 들어 실제 시스템 호출이 페이지 정렬 및 페이지 크기 버퍼로 수행되는지 여부 를 확인하려면 도구를 사용해야 합니다. 스트림 처리를 기반으로 하는 일부 C 라이브러리 구현은 IO 버퍼링에만 지정된 버퍼를 read
사용하지 않으므로 정렬 변형 패턴이나 사이즈가 발생할 수 있습니다. Linux/glibc에서는 이 작업을 수행하지 않을 것 같지만 확인하지 않고 크기 및/또는 정렬을 해제하면 IO 호출이 실패합니다.FILE *
setvbuf
다시 말하지만 Linux 직접 IO는 이상할 수 있습니다. 특정 파일 시스템만 직접 IO를 지원하며 그 중 일부는 다른 파일 시스템보다 더 전문적입니다. 시험사용하기로 결정했다면 철저하게 사용하십시오.
게시된 코드는 스트림의 버퍼를 채워야 할 때마다 1MB의 미리 읽기를 수행합니다. 스레드를 사용하여 더 복잡한 미리 읽기를 구현할 수도 있습니다. 한 스레드는 버퍼를 채우고 다른 스레드는 전체 버퍼에서 읽습니다. 이렇게 하면 미리 읽기가 완료될 때 "더벅거림"을 방지할 수 있지만 상대적으로 복잡한 멀티 스레드 코드가 많이 발생합니다.
답변2
이 질문은 아주 잘 답변되었습니다이 StackOverflow 질문에서.
이전 파일 오프셋 추적
read()
그 후에 데이터 범위를
read()
호출합니다 [...]fadvise(POSIX_FADV_DONTNEED)
최상의 결과를 얻으려면 페이지 정렬 블록의 데이터를 읽어야 합니다. I/O 캐시는 페이지 기반이며
fadvise()
지정된 데이터 범위를 페이지 목록에 매핑합니다. 정렬이 잘못되면 추가read()
s가 발생하고 성능이 저하되지만 그 외에는 무해합니다.
너 공부하기 싫구나첫 번째메모리를 최적화한다면. 당신은 단지 읽고 싶어합니다. 읽으면 디스크가 순차적으로 작동하므로 데이터를 스트리밍하라고 지시할 필요가 없습니다.