IT story

C ++ 캐시 인식 프로그래밍

hot-time 2020. 12. 31. 22:56
반응형

C ++ 캐시 인식 프로그래밍


C ++에서 CPU의 캐시 크기를 결정하는 방법이 있습니까? 나는 많은 데이터를 처리하는 알고리즘을 가지고 있으며이 데이터를 캐시에 맞도록 덩어리로 나누고 싶습니다. 이것이 가능한가? 캐시 크기를 염두에두고 프로그래밍에 대한 다른 힌트를 줄 수 있습니까 (특히 멀티 스레드 / 멀티 코어 데이터 처리와 관련하여)?

감사!


Ulrich Drepper가 작성한 " 모든 프로그래머가 메모리에 대해 알아야 할 사항"에 따르면 Linux에서 다음을 수행 할 수 있습니다.

메모리 요구 사항에 대한 공식이 있으면이를 캐시 크기와 비교할 수 있습니다. 앞서 언급했듯이 캐시는 다른 여러 코어와 공유 될 수 있습니다. 현재 {곧 더 나은 방법이있을 것입니다!} 하드 코딩 지식없이 올바른 정보를 얻는 유일한 방법은 / sys 파일 시스템을 이용하는 것입니다. 표 5.2에서 커널이 하드웨어에 대해 게시하는 내용을 살펴 보았습니다. 프로그램은 디렉토리를 찾아야합니다.

/sys/devices/system/cpu/cpu*/cache

이것은 섹션 6 : 프로그래머가 할 수있는 일에 나열되어 있습니다.

그는 또한 OS에서 얻을 수없는 경우 L1D 캐시 크기를 결정하는 데 사용할 수있는 그림 6.5 바로 아래에 간단한 테스트를 설명합니다.

그의 논문에서 내가 만난 것이 한 가지 더있다. sysconf(_SC_LEVEL2_CACHE_SIZE)그것은 잘 문서화되지 않은 것 같지만 L2 캐시 크기를 반환해야하는 Linux에서의 시스템 호출이다.


C ++ 자체는 CPU 캐시에 대해 "관심"하지 않으므로 언어에 내장 된 캐시 크기 쿼리를 지원하지 않습니다. Windows 용으로 개발하는 경우 CPU 캐시에 대한 정보를 쿼리하는 데 사용할 수있는 GetLogicalProcessorInformation ()-function 이 있습니다.


큰 배열을 미리 할당합니다. 그런 다음 각 요소에 순차적으로 액세스하고 각 액세스 시간을 기록하십시오. 이상적으로는 캐시 미스 발생시 액세스 시간이 급증합니다. 그런 다음 L1 캐시를 계산할 수 있습니다. 작동하지 않을 수도 있지만 시도해 볼 가치가 있습니다.


cpu (x86)의 cpuid를 읽고 조회 테이블로 캐시 크기를 결정합니다. 테이블은 CPU 제조업체가 프로그래밍 매뉴얼에 게시 한 캐시 크기로 채워야합니다.


수행하려는 작업에 따라 일부 라이브러리에 남겨 둘 수도 있습니다. 멀티 코어 처리에 대해 언급 했으므로 Intel Threading Building Blocks를 살펴 보는 것이 좋습니다 .

TBB에는 캐시 인식 메모리 할당자가 포함됩니다. 보다 구체적으로 확인하십시오 cache_aligned_allocator(참조 문서에서 직접 링크를 찾을 수 없음).


흥미롭게도 나는 이것을하기위한 프로그램을 얼마 전에 썼다 (하지만 C에서는 C ++ 코드에 쉽게 통합 할 수있을 것이라고 확신한다).

http://github.com/wowus/CacheLineDetection/blob/master/Cache%20Line%20Detection/cache.c

get_cache_line 함수는 배열 액세스의 타이밍 데이터에서 가장 큰 스파이크 직전의 위치를 ​​반환하는 흥미로운 함수입니다. 내 컴퓨터에서 올바르게 추측되었습니다! 다른 것이 있다면 자신의 것을 만드는 데 도움이 될 수 있습니다.

원래 내 관심을 불러 일으킨이 기사를 기반으로합니다. http://igoro.com/archive/gallery-of-processor-cache-effects/


이 스레드를 볼 수 있습니다 : http://software.intel.com/en-us/forums/topic/296674

짧은 대답은이 다른 스레드에 있습니다.

최신 IA-32 하드웨어에서 캐시 라인 크기는 64입니다. 값 128은 64 바이트 라인이 128 바이트 섹터로 쌍을 이루는 Intel Netburst Microarchitecture (예 : Intel Pentium D)의 레거시입니다. 섹터의 라인을 가져 오면 하드웨어는 섹터의 다른 라인도 자동으로 가져옵니다. 따라서 잘못된 공유 관점에서 보면 Netburst 프로세서의 유효 라인 크기는 128 바이트입니다. ( http://software.intel.com/en-us/forums/topic/292721 )


IIRC, GCC에는 __builtin_prefetch힌트가 있습니다.

http://gcc.gnu.org/onlinedocs/gcc-3.3.6/gcc/Other-Builtins.html

이것에 대한 훌륭한 섹션이 있습니다. 기본적으로 다음을 제안합니다.

__builtin_prefetch (&array[i + LookAhead], rw, locality);

여기서 rw 는 0 (읽기 준비) 또는 1 (쓰기 준비) 값이고, locality 는 숫자 0-3을 사용합니다. 여기서 0은 지역성이없고 3은 매우 강력한 지역입니다.

둘 다 선택 사항입니다. LookAhead는 미리 볼 요소의 수입니다. 메모리 액세스가 100 사이클이고 풀린 루프가 두 사이클 떨어져 있으면 LookAhead를 50 또는 51로 설정할 수 있습니다.


구별해야 할 두 가지 경우가 있습니다. 컴파일 타임이나 런타임에 캐시 크기를 알아야합니까?

컴파일 타임에 캐시 크기 결정

일부 애플리케이션의 경우 코드가 실행될 정확한 아키텍처 (예 : 호스트 시스템에서 직접 코드를 컴파일 할 수있는 경우)를 알고 있습니다. 이 경우 크기 검색을 단순화하고 하드 코딩은 옵션입니다 (빌드 시스템에서 자동화 가능). 오늘날 대부분의 컴퓨터에서 L1 캐시 라인은 64 바이트 여야합니다.

이러한 복잡성을 피하고 싶거나 알 수없는 아키텍처에서 컴파일을 지원해야하는 경우 C ++ 17 기능인 std :: hardware_constructive_interference_size 를 좋은 대안으로 사용할 수 있습니다 . 캐시 라인에 대한 컴파일 시간 추정치를 제공하지만 한계를 알고 있어야 합니다. 일반적으로 캐시 라인의 크기는 아키텍처에 따라 다르기 때문에 컴파일러는 바이너리를 생성 할 때 완벽하게 추측 할 수 없습니다.

런타임시 캐시 크기 결정

런타임시 정확한 시스템을 알고 있다는 이점이 있지만 OS에서 정보를 읽으려면 플랫폼 별 코드가 필요합니다. 좋은 시작점 은 주요 플랫폼 (Windows, Linux, MacOS)을 지원하는 이 답변 의 코드 스 니펫입니다 . 비슷한 방식으로 런타임에 L2 캐시 크기를 읽을 수도 있습니다.

시작시 벤치 마크를 실행하고 어떤 것이 가장 잘 수행되는지 측정하여 캐시 라인을 추측하지 않는 것이 좋습니다. 잘 작동 할 수 있지만 다른 프로세스에서 CPU를 사용하는 경우 오류가 발생하기 쉽습니다.

두 가지 접근 방식 결합

하나의 바이너리와 나중에 실행될 머신에 다양한 캐시 크기를 가진 다양한 아키텍처를 제공해야하는 경우 각 캐시 크기에 대해 특수 코드 부분을 만든 다음 동적으로 (애플리케이션 시작시) 가장 적합한 것을 선택할 수 있습니다. 하나.


캐시는 일반적으로 올바른 작업을 수행합니다. 일반 프로그래머의 유일한 걱정은 잘못된 공유이며 컴파일러 지시문이 필요하기 때문에 런타임에 처리 할 수 ​​없습니다.

참조 URL : https://stackoverflow.com/questions/1922249/c-cache-aware-programming

반응형