여기에 많은 주요 요점을 다루는 좋은 답변이 이미 많이 있으므로 직접 위에서 언급하지 않은 몇 가지 문제를 추가하겠습니다. 즉,이 답변은 장단점의 포괄적 인 것으로 간주되어서는 안되며 다른 답변에 대한 부록으로 간주되어야합니다.
mmap은 마술처럼 보인다
파일이 이미 완전히 캐시되는 경우 촬영 일을 기준으로 2 , mmap처럼 거의 보일 수도 마법 :
mmap 전체 파일을 (잠재적으로) 매핑하기 위해 한 번의 시스템 호출 만 있으면됩니다. 그 후에는 더 이상 시스템 호출이 필요하지 않습니다.
mmap 커널에서 사용자 공간으로 파일 데이터의 사본이 필요하지 않습니다.
mmap컴파일러 자동 벡터화, SIMD 내장 함수, 프리 페치, 최적화 된 메모리 내 구문 분석 루틴, OpenMP 등과 같이 메모리에 대해 수행 할 수있는 고급 트릭으로 파일을 처리하는 것을 포함하여 파일을 "메모리로"액세스 할 수 있습니다 .
파일이 이미 캐시에있는 경우에는 이길 수없는 것처럼 보입니다. 커널 페이지 캐시에 메모리로 직접 액세스하면 그보다 더 빠를 수 없습니다.
글쎄요
mmap은 실제로 마술이 아닙니다 ...
mmap은 여전히 페이지 당 작업을 수행합니다.
기본 숨겨진 비용 mmap대 vs read(2)(실제로 비슷한 OS 수준의 시스템 콜입니다) 블록 읽는 )은 mmap사용자 공간의 모든 4K 페이지에 대해 "일부 작업"을 수행해야한다는 것입니다. 페이지 결함 메커니즘.
예를 들어 mmap, 전체 파일에 대한 일반적인 구현은 100GB 파일을 읽기 위해 100GB / 4K = 2,500 만 개의 결함이 필요합니다. 자, 이것들은 사소한 결함 이지만 250 억 페이지의 결함은 여전히 빠르지 않을 것입니다. 사소한 결함의 비용은 아마도 가장 좋은 경우 100의 나노에있을 것입니다.
mmap은 TLB 성능에 크게 의존합니다
이제 리턴하기 전에 모든 페이지 테이블을 설정 MAP_POPULATE하도록 mmap지시 할 수 있으므로 액세스하는 동안 페이지 결함이 없어야합니다. 자, 이것은 전체 파일을 RAM으로 읽는다는 작은 문제가 있습니다 .100GB 파일을 매핑하려고하면 폭발 할 것입니다. 그러나 지금은 무시하십시오 3 . 커널은 이러한 페이지 테이블을 설정하기 위해 페이지 당 작업 을 수행해야합니다 (커널 시간으로 표시됨). 이는 mmap접근 방식 에서 주요 비용이되며 파일 크기에 비례합니다 (즉, 파일 크기가 커질수록 상대적으로 덜 중요하지 않음) 4 .
마지막으로, 사용자 공간에서 액세스 할 때조차도 이러한 매핑이 완전히 자유롭지는 않습니다 (파일 기반이 아닌 대용량 메모리 버퍼와 비교 mmap). 페이지 테이블이 설정되면 새 페이지에 대한 각 액세스는 개념적으로 TLB 미스가 발생합니다. 이후mmap 페이지 캐시와 4K 페이지를 사용하여 파일 수단을 보내고, 다시 100GB의 파일이 비용 25 만 번이 발생.
이제 이러한 TLB 누락의 실제 비용은 최소한 다음 하드웨어 측면에 크게 좌우됩니다. (a) 보유하고있는 4K TLB 수 및 나머지 번역 캐싱 작동 방식 (b) 하드웨어 프리 페치 처리 방식 프리 페치가 페이지 워크를 트리거 할 수 있습니까? (c) 페이지 워킹 하드웨어의 속도와 병렬 속도 최신 하이 엔드 x86 인텔 프로세서에서 페이지 워킹 하드웨어는 일반적으로 매우 강력합니다. 최소 2 개의 병렬 페이지 워커가 있고, 페이지 워킹이 계속 실행되면서 동시에 발생할 수 있으며, 하드웨어 프리 페치가 페이지 워킹을 트리거 할 수 있습니다. 따라서 스트리밍 읽기로드 에 대한 TLB 영향 은 상당히 낮으며 이러한로드는 종종 페이지 크기에 관계없이 비슷하게 수행됩니다. 그러나 다른 하드웨어는 일반적으로 훨씬 나쁩니다!
read ()는 이러한 함정을 피합니다
read()일반적으로 기초가 무엇 콜, 유형 호출 C, C ++에, 예를 들면 제공되며 다른 언어 모두가 잘 인식 것을 하나의 기본 단점이있다 "블록 읽기"
read()N 바이트를 호출 할 때마다 N 바이트를 커널에서 사용자 공간으로 복사해야합니다.
반면에, 위의 비용을 대부분 피할 수 있습니다. 2,500 만 개의 4K 페이지를 사용자 공간에 매핑 할 필요가 없습니다. 일반적으로 malloc사용자 공간에서 단일 버퍼 소형 버퍼를 사용할 수 있으며 모든 read통화에 반복적으로이를 재사용 할 수 있습니다 . 커널 쪽에서는 4K 페이지 또는 TLB 누락 문제가 거의 없습니다. 모든 RAM은 대개 매우 큰 페이지 (예 : x86의 1GB 페이지)를 사용하여 선형으로 매핑되므로 페이지 캐시의 기본 페이지가 포함됩니다 커널 공간에서 매우 효율적입니다.
따라서 기본적으로 다음과 같은 비교를 통해 큰 파일을 한 번 읽을 때 어느 쪽이 더 빠른지 결정합니다.
이 mmap방법 을 사용하면 파일 내용을 커널에서 사용자 공간으로 복사하는 바이트 단위 작업보다 비용이 많이 드는 방식 으로 페이지 당 추가 작업이 필요 read()합니까?
많은 시스템에서 실제로는 거의 균형을 이룹니다. 각각은 완전히 다른 하드웨어 및 OS 스택 속성으로 확장됩니다.
특히 다음과 같은 경우 mmap접근 방식이 상대적으로 빨라집니다.
- OS는 빠른 사소한 결함 처리, 특히 결함 해결과 같은 사소한 결함 벌크 최적화를 갖추고 있습니다.
- OS는
MAP_POPULATE예를 들어 기본 페이지가 물리적 메모리에서 연속적인 경우 큰 맵을 효율적으로 처리 할 수 있는 좋은 구현을 가지고 있습니다.
- 하드웨어는 큰 TLB, 빠른 2 차 레벨 TLB, 빠른 페이지 병렬 및 병렬 페이지 워커, 번역과의 프리 페치 상호 작용 등과 같은 강력한 페이지 변환 성능을 가지고 있습니다.
... 다음과 같은 경우 read()접근 방식이 상대적으로 빨라집니다.
read()콜 좋은 복사 성능을 가지고있다. 예를 들어, copy_to_user커널 쪽에서 좋은 성능.
- 커널은 예를 들어 하드웨어를 지원하는 몇 개의 큰 페이지 만 사용하여 메모리를 매핑하는 효율적인 (사용자 영역에 비해) 방법이 있습니다.
- 커널에는 빠른 syscall과 syscall 전체에서 커널 TLB 항목을 유지하는 방법이 있습니다.
위의 하드웨어 요소는 크게 다릅니다 는 동일한 제품군 (예 : x86 세대 및 특히 시장 세그먼트 내) 및 아키텍처 (예 : ARM vs x86 vs PPC) 내에서도 플랫폼 .
OS 요소도 계속 변하고 있으며, 양쪽의 다양한 개선으로 인해 한 접근 방식 또는 다른 접근 방식의 상대 속도가 크게 향상되었습니다. 최근 목록에는 다음이 포함됩니다.
- 위에 설명 된 장애 해결 기능이 추가되어 실제로
mmap사례가없는 경우에 도움이됩니다 MAP_POPULATE.
- 예를 들어, 빠른 경우를 사용하여 빠른 경로
copy_to_user방법을 추가 하면 실제로 도움이 됩니다.arch/x86/lib/copy_user_64.SREP MOVQread()
스펙터 및 멜트 다운 후 업데이트
스펙터 및 멜트 다운 취약점에 대한 완화 조치로 시스템 호출 비용이 상당히 증가했습니다. 필자가 측정 한 시스템에서 "아무것도하지 않는"시스템 호출 비용 (통화에 의해 수행 된 실제 작업을 제외하고 시스템 호출의 순수한 오버 헤드 추정값)은 일반적으로 약 100ns에서 약 700ns의 최신 Linux 시스템. 또한 시스템에 따라 Meltdown 전용 페이지 테이블 격리 수정 프로그램은 TLB 항목을 다시로드해야하기 때문에 직접 시스템 호출 비용 외에 추가적인 다운 스트림 영향을 미칠 수 있습니다.
이 모든 것은 다음 read()과 비교할 때 기반 방법에 대한 상대적 단점입니다mmap 때문에 기반 방법 read()방법은 각 데이터 "버퍼 크기"가치가 하나의 시스템 호출을 수행한다. 큰 버퍼를 사용하면 일반적으로 L1 크기를 초과하므로 성능이 저하되어 캐시 누락이 계속 발생하므로 버퍼 크기를 임의로 늘려이 비용을 상각 할 수 없습니다.
반면을 사용하면 단일 시스템 호출 비용으로 mmap대규모 메모리 영역에 매핑 MAP_POPULATE하고 효율적으로 액세스 할 수 있습니다.
1 여기에는 파일이 완전히 캐싱되지 않았지만 OS 미리 읽기가 충분하기 때문에 파일이 제대로 표시되는 경우도 포함됩니다 (예 : 일반적으로 페이지는 시간에 따라 캐시됩니다) 원하는). 이 방법은 작품을 미리 읽을 수 있기 때문에 자주와 매우 다르다하지만 미묘한 문제 mmap와 read통화 및에 설명 된대로 더 "조언"호출에 의해 조정할 수 2 .
2 ... 파일이 캐시 되지 않은 경우 액세스 패턴이 기본 하드웨어에 대한 동정심을 포함하여 IO 관련 문제에 의해 행동이 완전히 지배 될 것입니다. 예를 들어 사용 madvise또는 fadvise호출 (및 액세스 패턴을 개선하기 위해 수행 할 수있는 응용 프로그램 수준 변경)을 통해 가능합니다.
3 예를 들어 mmap작은 크기의 창 ( 예 : 100MB) 을 순차적으로 사용하면 문제를 해결할 수 있습니다 .
4 사실, 그것은 판명 MAP_POPULATE접근 (적어도 하나의 일부 하드웨어 / OS 조합) 만 약간 빠른 아마 커널이 사용하고 있기 때문에, 그것을 사용하지 않는 것보다 faultaround - 사소한 오류의 실제 수는 16의 비율로 감소되도록 정도.
mmap()syscall을 사용하는 것보다 2-6 배 빠릅니다 (예 :)read().