문제

저는 크기가 100GB 이상이 될 수 있는 파일을 처리하는 프로그램을 개발 중입니다.파일에는 가변 길이 레코드 세트가 포함되어 있습니다.첫 번째 구현을 실행 중이며 이제 성능 향상을 모색하고 있습니다. 특히 입력 파일이 여러 번 스캔되므로 I/O를 보다 효율적으로 수행하는 데 중점을 두고 있습니다.

사용에 대한 경험 법칙이 있습니까? mmap() C++를 통해 블록 단위로 읽는 것과 비교 fstream 도서관?내가 하고 싶은 일은 디스크에서 버퍼로 큰 블록을 읽고, 버퍼에서 완전한 레코드를 처리한 다음, 더 많은 것을 읽는 것입니다.

그만큼 mmap() 코드가 잠재적으로 매우 지저분해질 수 있습니다. mmap'd 블록은 페이지 크기 경계(내 이해)에 있어야 하며 레코드는 페이지 경계를 넘어 잠재적으로 좋아할 수 있습니다.와 함께 fstream예를 들어, 페이지 크기 경계에 있는 블록을 읽는 데만 국한되지 않기 때문에 레코드의 시작 부분을 찾아서 다시 읽기 시작할 수 있습니다.

실제로 완전한 구현을 먼저 작성하지 않고 이 두 가지 옵션 중에서 어떻게 결정할 수 있습니까?모든 경험 법칙(예: mmap() 2배 더 빠름) 아니면 간단한 테스트인가요?

도움이 되었습니까?

해결책

Linux에서의 mmap/읽기 성능에 대한 최종 단어를 찾으려고 노력하던 중에 멋진 게시물을 발견했습니다(링크) Linux 커널 메일링 리스트에 있습니다.2000년부터이니 그 이후로 커널의 IO와 가상 메모리에 많은 개선이 있었지만, 그 이유를 친절히 설명하고 있습니다. mmap 또는 read 더 빠를 수도 있고 느릴 수도 있습니다.

  • 전화 mmap 것보다 오버헤드가 더 많습니다. read (처럼 epoll 것보다 오버헤드가 더 많습니다. poll, 이는 다음보다 더 많은 오버헤드를 갖습니다. read).가상 메모리 매핑을 변경하는 것은 서로 다른 프로세스 간을 전환하는 데 비용이 많이 드는 것과 같은 이유로 일부 프로세서에서는 상당히 비용이 많이 드는 작업입니다.
  • IO 시스템은 이미 디스크 캐시를 사용할 수 있으므로 파일을 읽으면 어떤 방법을 사용하더라도 캐시에 도달하거나 놓칠 수 있습니다.

하지만,

  • 메모리 맵은 일반적으로 무작위 액세스의 경우 더 빠릅니다. 특히 액세스 패턴이 희박하고 예측할 수 없는 경우에는 더욱 그렇습니다.
  • 메모리 맵을 사용하면 다음을 수행할 수 있습니다. 유지하다 완료될 때까지 캐시의 페이지를 사용합니다.즉, 오랜 기간 동안 파일을 많이 사용한 다음 해당 파일을 닫았다가 다시 열면 페이지가 계속 캐시됩니다.와 함께 read, 귀하의 파일은 오래 전에 캐시에서 플러시되었을 수 있습니다.파일을 사용하고 즉시 폐기하는 경우에는 적용되지 않습니다.(당신이 시도한다면 mlock 페이지를 캐시에 보관하기 위해 디스크 캐시를 능가하려고 시도하는 것이며 이러한 종류의 바보짓은 시스템 성능에 거의 도움이 되지 않습니다.
  • 파일을 직접 읽는 것은 매우 간단하고 빠릅니다.

mmap/read에 대한 논의는 두 가지 다른 성능 논의를 생각나게 합니다.

  • 일부 Java 프로그래머는 비차단 I/O가 차단 I/O보다 느린 경우가 많다는 사실을 알고 충격을 받았습니다. 이는 비차단 I/O에 더 많은 syscall이 필요하다는 사실을 알고 있다면 완벽하게 이해되는 사실입니다.

  • 일부 다른 네트워크 프로그래머들은 이 사실을 알고 충격을 받았습니다. epoll 종종 다음보다 느립니다. poll, 관리하는 방법을 알고 있다면 완벽하게 이해됩니다. epoll 더 많은 syscall을 만들어야 합니다.

결론: 데이터에 무작위로 액세스하거나 오랫동안 보관하거나 다른 프로세스와 공유할 수 있는 경우 메모리 맵을 사용합니다(MAP_SHARED 실제 공유가 없으면 별로 흥미롭지 않습니다.)데이터에 순차적으로 액세스하는 경우 파일을 정상적으로 읽거나 읽은 후 삭제합니다.그리고 두 방법 모두 프로그램을 덜 복잡하게 만든다면 다음을 수행하십시오. 저것.많은 실제 사례의 경우 벤치마크가 아닌 실제 애플리케이션을 테스트하지 않고는 더 빠르다는 것을 보여줄 수 있는 확실한 방법이 없습니다.

(이 질문을 무시해서 죄송합니다. 답변을 찾고 있었는데 이 질문이 Google 검색결과 상단에 계속 표시되었습니다.)

다른 팁

주요 성능 비용은 디스크 I/O가 될 것입니다."mmap()"은 확실히 istream보다 빠르지만 디스크 I/O가 런타임을 지배하기 때문에 차이가 눈에 띄지 않을 수 있습니다.

나는 Ben Collins의 코드 조각(위/아래 참조)을 시도하여 "mmap()이 방법 더 빠르게" 측정 가능한 차이를 발견하지 못했습니다.그의 답변에 대한 내 의견을 참조하십시오.

나는 확실히 ~ 아니다 "레코드"가 크지 않는 한 각 레코드를 개별적으로 mmap'하는 것이 좋습니다. 이렇게 하면 엄청나게 느려지고 각 레코드에 대해 2번의 시스템 호출이 필요하며 디스크 메모리 캐시에서 페이지가 손실될 수 있습니다.

귀하의 경우에는 mmap(), istream 및 저수준 open()/read() 호출이 모두 거의 동일할 것이라고 생각합니다.다음과 같은 경우에는 mmap()을 권장합니다.

  1. 파일 내에 무작위 액세스(순차 액세스 아님)가 있고, 그리고
  2. 모든 것이 메모리에 편안하게 맞거나 특정 페이지를 매핑하고 다른 페이지를 매핑할 수 있도록 파일 내에 참조 위치가 있습니다.이렇게 하면 운영 체제가 사용 가능한 RAM을 사용하여 최대한의 이점을 얻습니다.
  3. 또는 여러 프로세스가 동일한 파일을 읽고 작업하는 경우 프로세스가 모두 동일한 물리적 페이지를 공유하므로 mmap()이 환상적입니다.

(btw - 저는 mmap()/MapViewOfFile()을 좋아합니다).

mmap은 방법 더 빠르게.이를 증명하기 위해 간단한 벤치마크를 작성할 수 있습니다.

char data[0x1000];
std::ifstream in("file.bin");

while (in)
{
  in.read(data, 0x1000);
  // do something with data
}

대:

const int file_size=something;
const int page_size=0x1000;
int off=0;
void *data;

int fd = open("filename.bin", O_RDONLY);

while (off < file_size)
{
  data = mmap(NULL, page_size, PROT_READ, 0, fd, off);
  // do stuff with data
  munmap(data, page_size);
  off += page_size;
}

분명히 세부 사항은 생략하겠습니다(예: 파일이 배수가 아닌 경우 파일 끝에 도달한 시점을 확인하는 방법). page_size, 예를 들어) 그러나 실제로는 이보다 훨씬 더 복잡해서는 안 됩니다.

가능하다면 데이터를 부분이 아닌 전체로 mmap()할 수 있는 여러 파일로 나눌 수 있습니다(훨씬 더 간단함).

몇 달 전에 저는 Boost_iostreams에 대한 슬라이딩 윈도우 mmap() 스트림 클래스를 어설프게 구현했지만 아무도 신경 쓰지 않았고 다른 일로 바빴습니다.가장 불행하게도 저는 몇 주 전에 완료되지 않은 오래된 프로젝트의 아카이브를 삭제했는데 그게 피해자 중 한 명이었습니다. :-(

업데이트:또한 Microsoft는 처음에 mmap으로 수행할 작업의 대부분을 수행하는 멋진 파일 캐시를 구현했기 때문에 이 벤치마크가 Windows에서 상당히 다르게 보일 것이라는 경고를 추가해야 합니다.즉, 자주 액세스하는 파일의 경우 std::ifstream.read()만 수행하면 mmap만큼 빠릅니다. 파일 캐시가 이미 메모리 매핑을 수행하고 투명하기 때문입니다.

최종 업데이트:보세요, 사람들은:OS와 표준 라이브러리, 디스크 및 메모리 계층 구조의 다양한 플랫폼 조합에서 시스템 호출이 어떻게 작동하는지 확실하게 말할 수는 없습니다. mmap, 블랙박스로 볼 때 항상 항상 훨씬 더 빠릅니다. read.비록 내 말이 그렇게 해석될 수 있다 하더라도 그것은 정확히 내 의도는 아니었습니다. 궁극적으로 내 요점은 메모리 매핑된 I/O가 일반적으로 바이트 기반 I/O보다 빠르다는 것입니다.이건 아직도 ​​사실이야.실험적으로 둘 사이에 차이가 없다는 것을 알게 된다면, 제가 보기에 합리적으로 보이는 유일한 설명은 귀하의 플랫폼이 호출 성능에 유리한 방식으로 내부적으로 메모리 매핑을 구현한다는 것입니다. read.이식 가능한 방식으로 메모리 매핑된 I/O를 사용하고 있는지 절대적으로 확신할 수 있는 유일한 방법은 다음을 사용하는 것입니다. mmap.이식성에 관심이 없고 대상 플랫폼의 특정 특성에 의존할 수 있다면 다음을 사용하세요. read 측정 가능한 성능 저하 없이 적합할 수 있습니다.

답변 목록을 정리하려면 편집하세요.@jbl:

슬라이딩 윈도우 MMAP가 흥미 롭습니다.그것에 대해 조금 더 말할 수 있습니까?

물론이죠. 저는 Git용 C++ 라이브러리(원한다면 libgit++)를 작성 중이었고 다음과 비슷한 문제에 직면했습니다.나는 큰(매우 큰) 파일을 열 수 있어야 했고 성능이 총체적이지 않아야 했습니다. std::fstream).

Boost::Iostreams 이미 mapped_file 소스가 있지만 문제는 그것이 mmap전체 파일을 핑하면 2^(워드 크기)로 제한됩니다.32비트 시스템에서는 4GB가 충분하지 않습니다.기대하는 것은 무리가 아니다 .pack 그보다 훨씬 더 커지는 Git의 파일 때문에 일반 파일 I/O에 의지하지 않고 파일을 덩어리로 읽어야 했습니다.다음의 표지 아래 Boost::Iostreams, 나는 소스 간의 상호 작용에 대한 다소 또 다른 관점인 소스를 구현했습니다. std::streambuf 그리고 std::istream.상속만으로 비슷한 접근 방식을 시도할 수도 있습니다. std::filebuf 으로 mapped_filebuf 그리고 마찬가지로 상속 std::fstream ~ 안으로 a mapped_fstream.제대로 이해하기 어려운 것은 둘 사이의 상호 작용입니다. Boost::Iostreams 일부 작업이 완료되었으며 필터와 체인에 대한 후크도 제공하므로 그런 식으로 구현하는 것이 더 유용할 것이라고 생각했습니다.

여기에는 이미 많은 핵심 사항을 다루는 좋은 답변이 많이 있으므로 위에서 직접 다루지 않은 몇 가지 문제만 추가하겠습니다.즉, 이 답변은 장단점을 포괄하는 것으로 간주되어서는 안 되며, 오히려 여기의 다른 답변에 대한 부록으로 간주되어야 합니다.

mmap은 마술처럼 보인다

파일이 이미 완전히 캐시된 경우를 생각해 보세요.1 기준으로2, mmap 꽤 비슷해 보일 수도 있겠네요 마법:

  1. mmap 전체 파일을 (잠재적으로) 매핑하려면 단 한 번의 시스템 호출만 필요하며, 그 이후에는 더 이상 시스템 호출이 필요하지 않습니다.
  2. mmap 커널에서 사용자 공간으로 파일 데이터 복사본이 필요하지 않습니다.
  3. mmap 컴파일러 자동 벡터화와 같이 메모리에 대해 수행할 수 있는 고급 기술을 사용하여 파일을 처리하는 것을 포함하여 "메모리로" 파일에 액세스할 수 있습니다. 심드 내장 함수, 프리페칭, 최적화된 메모리 내 구문 분석 루틴, OpenMP 등

파일이 이미 캐시에 있는 경우에는 이기는 것이 불가능해 보입니다.커널 페이지 캐시에 메모리로 직접 액세스하면 그보다 더 빨라질 수 없습니다.

글쎄요.

mmap은 실제로 마술이 아닙니다. 왜냐하면...

mmap은 여전히 ​​페이지별 작업을 수행합니다.

기본 숨겨진 비용 mmapread(2) (실제로는 OS 수준 시스템 호출과 비슷합니다. 독서 블록) 그 말은? mmap 페이지 오류 메커니즘에 의해 숨겨졌더라도 사용자 공간의 모든 4K 페이지에 대해 "일부 작업"을 수행해야 합니다.

예를 들어 일반적인 구현은 mmap100GB 파일을 읽으려면 전체 파일에 오류가 발생해야 하므로 100GB/4K = 2,500만 개의 오류가 발생합니다.이제 이것들은 사소한 결점, 그러나 250억 개의 페이지 오류는 여전히 매우 빠르지는 않습니다.사소한 결함으로 인한 비용은 아마도 최선의 경우 수백 나노에 이를 것입니다.

mmap은 TLB 성능에 크게 의존합니다.

이제 합격하시면 됩니다 MAP_POPULATE 에게 mmap 반환하기 전에 모든 페이지 테이블을 설정하도록 지시하여 액세스하는 동안 페이지 폴트가 없어야 합니다.이제 여기에는 전체 파일을 RAM으로 읽는 작은 문제가 있습니다. 100GB 파일을 매핑하려고 하면 폭발할 것입니다. 하지만 지금은 무시하겠습니다.3.커널이 해야 할 일 페이지별 작업 이러한 페이지 테이블을 설정합니다(커널 시간으로 표시됨).이는 결국 큰 비용이 됩니다. mmap 접근 방식이며 파일 크기에 비례합니다(즉, 파일 크기가 커져도 상대적으로 중요성이 떨어지지 않습니다).4.

마지막으로, 사용자 공간에서 액세스하는 경우에도 이러한 매핑은 정확히 무료가 아닙니다(파일 기반 메모리 버퍼에서 발생하지 않는 대용량 메모리 버퍼와 비교). mmap) - 페이지 테이블이 설정되더라도 새 페이지에 액세스할 때마다 개념적으로 TLB 누락이 발생합니다.부터 mmap파일을 저장한다는 것은 페이지 캐시와 해당 4K 페이지를 사용한다는 의미이며, 이 비용은 100GB 파일에 대해 2,500만 번 발생합니다.

이제 이러한 TLB 미스의 실제 비용은 최소한 하드웨어의 다음 측면에 크게 좌우됩니다.(a) 보유하고 있는 4K TLB 엔터티 수와 나머지 변환 캐싱 작동 방식 (b) 하드웨어 프리페치가 TLB를 얼마나 잘 처리합니까? 예를 들어 프리페치가 페이지 워크를 트리거할 수 있습니까?(c) 페이지 워킹 하드웨어가 얼마나 빠르고 병렬적인지.최신 고급 x86 Intel 프로세서에서 페이지 워킹 하드웨어는 일반적으로 매우 강력합니다.최소 2개의 병렬 페이지 워커가 있고, 지속적인 실행과 동시에 페이지 워크가 발생할 수 있으며, 하드웨어 프리페치가 페이지 워크를 트리거할 수 있습니다.따라서 TLB가 스트리밍 읽기 로드는 상당히 낮습니다. 이러한 로드는 페이지 크기에 관계없이 유사하게 수행되는 경우가 많습니다.그러나 다른 하드웨어는 일반적으로 훨씬 더 나쁩니다!

read()는 이러한 함정을 피합니다

그만큼 read() 일반적으로 C, C++ 및 기타 언어에서 제공되는 "블록 읽기" 유형 호출의 기초가 되는 syscall에는 모두가 잘 알고 있는 한 가지 주요 단점이 있습니다.

  • 모든 read() N 바이트 호출은 N 바이트를 커널에서 사용자 공간으로 복사해야 합니다.

반면에 위의 비용 대부분을 방지합니다. 즉, 2,500만 개의 4K 페이지를 사용자 공간에 매핑할 필요가 없습니다.일반적으로 할 수 있습니다 malloc 사용자 공간에 단일 버퍼 작은 버퍼를 두고 이를 모든 작업에 대해 반복적으로 재사용합니다. read 전화.커널 측에서는 모든 RAM이 일반적으로 몇 개의 매우 큰 페이지(예: x86의 1GB 페이지)를 사용하여 선형으로 매핑되므로 4K 페이지 또는 TLB 누락과 관련된 문제가 거의 없습니다. 따라서 페이지 캐시의 기본 페이지가 포함됩니다. 커널 공간에서 매우 효율적입니다.

따라서 기본적으로 대용량 파일을 한 번 읽을 때 어느 것이 더 빠른지 결정하기 위해 다음과 같은 비교가 있습니다.

페이지당 추가 작업이 암시됩니까? mmap 커널에서 사용자 공간으로 파일 내용을 복사하는 바이트당 작업보다 비용이 더 많이 듭니다. read()?

많은 시스템에서 실제로는 거의 균형이 잡혀 있습니다.각각은 하드웨어 및 OS 스택의 완전히 다른 속성에 따라 확장됩니다.

특히, mmap 다음과 같은 경우 접근 방식이 상대적으로 빨라집니다.

  • OS에는 빠른 사소한 오류 처리 기능과 특히 오류 해결과 같은 사소한 오류 대량 최적화 기능이 있습니다.
  • OS가 좋다 MAP_POPULATE 예를 들어 기본 페이지가 물리적 메모리에서 연속되어 있는 경우 대규모 맵을 효율적으로 처리할 수 있는 구현입니다.
  • 하드웨어는 대형 TLB, 빠른 2차 레벨 TLB, 빠른 병렬 페이지 워커, 번역과의 우수한 프리페치 상호 작용 등과 같은 강력한 페이지 번역 성능을 갖추고 있습니다.

...동안 read() 다음과 같은 경우 접근 방식이 상대적으로 빨라집니다.

  • 그만큼 read() syscall은 복사 성능이 좋습니다.예를 들어, 좋다 copy_to_user 커널 측의 성능.
  • 커널에는 메모리를 매핑하는 효율적인(사용자 영역에 비해) 방법이 있습니다(예: 하드웨어 지원이 포함된 몇 개의 큰 페이지만 사용).
  • 커널에는 빠른 syscall이 있으며 syscall 전체에서 커널 TLB 항목을 유지하는 방법이 있습니다.

위의 하드웨어 요소는 다양합니다. 격렬하게 다양한 플랫폼, 심지어 동일한 제품군(예: x86 세대, 특히 시장 부문 내) 내에서도, 확실히 아키텍처 전체(예: ARM 대 x86 대 PPC)에 걸쳐 적용됩니다.

OS 요소도 계속 변하고 있으며 양쪽의 다양한 개선으로 인해 한 접근 방식 또는 다른 접근 방식의 상대 속도가 크게 향상됩니다.최근 목록에는 다음이 포함됩니다.

  • 위에서 설명한 결함 우회 기능을 추가하면 mmap 없는 경우 MAP_POPULATE.
  • 빠른 경로 추가 copy_to_user 메소드 arch/x86/lib/copy_user_64.S, 예를 들어 다음을 사용하여 REP MOVQ 속도가 빠르면 실제로 도움이 됩니다. read() 사례.

스펙터와 멜트다운 이후 업데이트

Spectre 및 Meltdown 취약점에 대한 완화로 인해 시스템 호출 비용이 상당히 증가했습니다.내가 측정한 시스템에서 "아무 것도 하지 않는" 시스템 호출(호출에 의해 수행된 실제 작업을 제외하고 시스템 호출의 순수 오버헤드 추정치)의 비용은 일반적인 시스템에서 약 100ns였습니다. 최신 Linux 시스템의 속도는 약 700ns입니다.또한 시스템에 따라 페이지 테이블 격리 Meltdown을 위한 수정 사항은 TLB 항목을 다시 로드해야 하기 때문에 직접적인 시스템 호출 비용과는 별도로 추가적인 다운스트림 효과를 가질 수 있습니다.

이 모든 것은 상대적인 단점이다. read() 기반 방법에 비해 mmap 기반 방법, 이후 read() 메소드는 각 "버퍼 크기" 상당의 데이터에 대해 하나의 시스템 호출을 수행해야 합니다.큰 버퍼를 사용하면 일반적으로 L1 크기를 초과하여 지속적으로 캐시 누락이 발생하므로 성능이 저하되므로 이 비용을 상환하기 위해 버퍼 크기를 임의로 늘릴 수 없습니다.

반면에, mmap, 다음을 사용하여 대규모 메모리 영역에 매핑할 수 있습니다. MAP_POPULATE 단 한 번의 시스템 호출 비용으로 효율적으로 액세스할 수 있습니다.


1 여기에는 파일이 처음부터 완전히 캐시되지 않았지만 OS 미리 읽기가 그렇게 나타나도록 하는 데 충분한 경우도 포함됩니다(예: 페이지는 일반적으로 원하는 시간에 캐시됩니다). 그것).이는 미리 읽기가 작동하는 방식이 종종 서로 상당히 다르기 때문에 미묘한 문제입니다. mmap 그리고 read 통화에 설명된 대로 "조언" 통화를 통해 추가로 조정할 수 있습니다. 2.

2 ...왜냐하면 파일이 ~ 아니다 캐시되면 귀하의 액세스 패턴이 기본 하드웨어에 얼마나 공감하는지를 포함하여 귀하의 행동은 IO 문제에 의해 완전히 지배될 것입니다. 그리고 귀하의 모든 노력은 그러한 액세스가 가능한 한 동정적이 되도록 보장해야 합니다.사용을 통해 madvise 또는 fadvise 호출(및 액세스 패턴을 개선하기 위해 수행할 수 있는 모든 애플리케이션 수준 변경).

3 예를 들어 순차적으로 이 문제를 해결할 수 있습니다. mmap100MB와 같이 더 작은 크기의 창에서 실행됩니다.

4 실제로, MAP_POPULATE 접근 방식은 (적어도 하나의 하드웨어/OS 조합) ​​사용하지 않는 것보다 약간 더 빠릅니다. 아마도 커널이 문제 해결 - 따라서 실제 사소한 결함 수는 16배 정도 감소합니다.

벤 콜린스(Ben Collins)가 슬라이딩 윈도우 mmap 소스 코드를 잃어버려서 안타깝습니다.Boost에 있으면 좋을 것 같습니다.

예, 파일 매핑이 훨씬 빠릅니다.기본적으로 OS 가상 메모리 하위 시스템을 사용하여 메모리와 디스크를 연결하고 그 반대의 경우도 마찬가지입니다.이렇게 생각해 보세요.OS 커널 개발자가 더 빠르게 만들 수 있다면 그렇게 할 것입니다.그렇게 하면 거의 모든 것이 더 빨라지기 때문입니다.데이터베이스, 부팅 시간, 프로그램 로드 시간 등.

슬라이딩 윈도우 접근 방식은 여러 개의 연속 페이지를 한 번에 매핑할 수 있으므로 실제로 그렇게 어렵지 않습니다.따라서 단일 레코드 중 가장 큰 레코드가 메모리에 맞는 한 레코드의 크기는 중요하지 않습니다.중요한 것은 장부를 관리하는 것입니다.

레코드가 getpagesize() 경계에서 시작되지 않는 경우 매핑은 이전 페이지에서 시작되어야 합니다.매핑된 영역의 길이는 레코드의 첫 번째 바이트(필요한 경우 getpagesize()의 가장 가까운 배수로 반올림)부터 레코드의 마지막 바이트(getpagesize()의 가장 가까운 배수로 반올림)까지 확장됩니다.레코드 처리가 끝나면 unmap()하고 다음으로 넘어갈 수 있습니다.

이 모든 작업은 Windows에서도 CreateFileMapping() 및 MapViewOfFile()(및 GetSystemInfo()를 사용하여 SYSTEM_INFO.dwAllocationGranularity를 ​​가져오는 데 --- SYSTEM_INFO.dwPageSize가 아님)을 사용하여 잘 작동합니다.

mmap이 더 빨라야 하는데 얼마나 빠른지는 모르겠습니다.코드에 따라 크게 달라집니다.mmap을 사용한다면 전체 파일을 한 번에 mmap하는 것이 가장 좋습니다. 그러면 작업이 훨씬 쉬워집니다.한 가지 잠재적인 문제는 파일이 4GB보다 큰 경우(또는 실제로는 제한이 더 낮으며 대개 2GB인 경우) 64비트 아키텍처가 필요하다는 것입니다.따라서 32 환경을 사용하고 있다면 아마도 사용하고 싶지 않을 것입니다.

하지만 성능을 향상시키는 더 나은 방법이 있을 수 있습니다.당신은 말했다 입력 파일이 여러 번 스캔됩니다., 한 번에 읽어서 완료할 수 있다면 잠재적으로 훨씬 더 빠를 수 있습니다.

나는 mmap'd 파일 I/O가 더 빨라질 것이라는 점에 동의합니다. 그러나 코드를 벤치마킹하는 동안 반대 예는 다음과 같아야 합니다. 약간 최적화?

벤 콜린스는 다음과 같이 썼습니다.

char data[0x1000];
std::ifstream in("file.bin");

while (in)
{
    in.read(data, 0x1000);
    // do something with data 
}

나는 또한 시도해 볼 것을 제안합니다:

char data[0x1000];
std::ifstream iifle( "file.bin");
std::istream  in( ifile.rdbuf() );

while( in )
{
    in.read( data, 0x1000);
    // do something with data
}

그리고 그 외에도 0x1000이 컴퓨터의 가상 메모리 한 페이지 크기가 아닌 경우 버퍼 크기를 가상 메모리 한 페이지와 동일한 크기로 만들 수도 있습니다.IMHO mmap'd 파일 I/O가 여전히 승리하지만 이로 인해 상황이 더 가까워질 것입니다.

아마도 파일을 사전 처리하여 각 레코드가 별도의 파일에 있도록(또는 적어도 각 파일이 mmap 가능 크기가 되도록) 해야 할 것입니다.

또한 다음 레코드로 이동하기 전에 각 레코드에 대한 모든 처리 단계를 수행할 수 있습니까?어쩌면 IO 오버헤드를 일부 피할 수 있을까요?

내 생각에는 mmap()을 "그냥" 사용하면 개발자가 자신의 캐싱 코드를 작성하지 않아도 되는 부담이 줄어듭니다.간단한 "파일을 한 번만 읽는" 경우에는 어렵지 않을 것입니다(mlbrock이 지적했듯이 메모리 복사본을 프로세스 공간에 계속 저장하지만). 파일에서 앞뒤로 이동하거나 비트를 건너뛰는 등의 문제로 인해 커널 개발자는 아마 내가 할 수 있는 것보다 캐싱을 더 잘 구현했습니다...

몇 년 전에 트리 구조를 포함하는 거대한 파일을 메모리에 매핑했던 기억이 납니다.트리 노드 할당 및 포인터 설정과 같이 메모리에서 많은 작업을 수행하는 일반적인 역직렬화에 비해 속도가 놀라웠습니다.실제로 나는 단일 호출을 MMAP (또는 Windows의 상대방)를 운영자 신입 및 생성자 호출에 대한 많은 (많은) 호출과 비교했습니다.이러한 종류의 작업에서는 mmap이 역직렬화에 비해 탁월합니다.물론 이에 대한 재배치 가능한 포인터 부스트를 조사해야 합니다.

이것은 멀티스레딩에 좋은 사용 사례처럼 들립니다.한 스레드가 데이터를 읽는 동안 다른 스레드가 데이터를 처리하도록 쉽게 설정할 수 있다고 생각합니다.이는 인지된 성능을 획기적으로 높이는 방법일 수 있습니다.그냥 생각입니다.

mmap의 가장 큰 장점은 다음을 사용한 비동기 읽기의 가능성이라고 생각합니다.

    addr1 = NULL;
    while( size_left > 0 ) {
        r = min(MMAP_SIZE, size_left);
        addr2 = mmap(NULL, r,
            PROT_READ, MAP_FLAGS,
            0, pos);
        if (addr1 != NULL)
        {
            /* process mmap from prev cycle */
            feed_data(ctx, addr1, MMAP_SIZE);
            munmap(addr1, MMAP_SIZE);
        }
        addr1 = addr2;
        size_left -= r;
        pos += r;
    }
    feed_data(ctx, addr1, r);
    munmap(addr1, r);

문제는 이 메모리가 파일에서 최대한 빨리 동기화되어야 한다는 힌트를 제공하는 올바른 MAP_FLAGS를 찾을 수 없다는 것입니다.MAP_POPULATE가 mmap에 대한 올바른 힌트를 제공하기를 바랍니다(예:호출에서 반환되기 전에 모든 내용을 로드하려고 시도하지는 않지만 비동기식으로 수행합니다.Feed_data 포함).최소한 이 플래그를 사용하면 2.6.23 이후로 MAP_PRIVATE 없이는 아무 작업도 수행하지 않는다고 설명서에 명시된 경우에도 더 나은 결과를 얻을 수 있습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top