여기에 많은 주요 요점을 다루는 좋은 답변이 이미 많이 있으므로 직접 위에서 언급하지 않은 몇 가지 문제를 추가하겠습니다. 즉,이 답변은 장단점의 포괄적 인 것으로 간주되어서는 안되며 다른 답변에 대한 부록으로 간주되어야합니다.
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.S
REP MOVQ
read()
스펙터 및 멜트 다운 후 업데이트
스펙터 및 멜트 다운 취약점에 대한 완화 조치로 시스템 호출 비용이 상당히 증가했습니다. 필자가 측정 한 시스템에서 "아무것도하지 않는"시스템 호출 비용 (통화에 의해 수행 된 실제 작업을 제외하고 시스템 호출의 순수한 오버 헤드 추정값)은 일반적으로 약 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()
.