캐시 회선 크기에 맞게 조정하는 방법 및 시기
C++로 작성된 Dmitry Vyukov의 뛰어난 경계 mpmc 큐에 대해서는, http://www.1024cores.net/home/lock-free-algorithms/queues/bounded-mpmc-queue 를 참조해 주세요.
그는 패딩 변수를 추가합니다.성능을 위해 캐시 라인에 맞춰 조정해야 합니다.
몇 가지 질문이 있습니다.
- 왜 이런 식으로 하는 거죠?
- 항상 효과가 있는 휴대용 방법입니까?
- 에 사용하는 요?
__attribute__ ((aligned (64)))
★★★★★★ 。 버퍼 포인터 앞에 패딩이 있으면 퍼포먼스에 도움이 되는 이유는 무엇입니까?포인터가 캐시에 로드되어 있기 때문에 실제로는 포인터 크기뿐이 아닐까요?
static size_t const cacheline_size = 64; typedef char cacheline_pad_t [cacheline_size]; cacheline_pad_t pad0_; cell_t* const buffer_; size_t const buffer_mask_; cacheline_pad_t pad1_; std::atomic<size_t> enqueue_pos_; cacheline_pad_t pad2_; std::atomic<size_t> dequeue_pos_; cacheline_pad_t pad3_;
이 컨셉은 c코드의 gcc에서 작동합니까?
이렇게 하면 서로 다른 필드를 수정하는 서로 다른 코어가 캐시 간에 두 필드를 모두 포함하는 캐시 라인을 바운스할 필요가 없어집니다.일반적으로 프로세서가 메모리 내의 일부 데이터에 액세스하려면 프로세서를 포함하는 캐시 라인 전체가 프로세서의 로컬 캐시에 있어야 합니다.데이터를 수정하는 경우 일반적으로 해당 캐시 엔트리는 시스템 내 캐시에 있는 유일한 복사본이어야 합니다(MESI/MOESHI 스타일의 캐시 일관성 프로토콜에서는 제외 모드).서로 다른 코어가 같은 캐시 라인에 존재하는 다른 데이터를 수정하려고 하면 해당 라인 전체를 앞뒤로 이동하는 데 시간이 낭비되는 것을 잘못된 공유라고 합니다.
공유) 1의 을 할 수 .buffer_
쓰기만.enqueue_pos_
디큐()buffer_
배타적 타 anddequeue_pos_
쪽의 스톨을 하지 않습니다.
번째 은 '아예'를 합니다.buffer_
★★★★★★★★★★★★★★★★★」buffer_mask_
는, 하기 로 할 것이 캐시 회선상에 것입니다.
나는 그 기술이 완전히 휴대 가능한지는 잘 모르겠다.
가정은 각각 cacheline_pad_t
그 자체는 64바이트(그 사이즈의) 캐시 행 경계에 맞춰져 있기 때문에, 그 후의 것은 모두 다음 캐시 행에 배치됩니다. 제가 알기로는 C 및 C++ 언어 표준에서는 구성원의 정렬 요건을 위반하지 않고 어레이 내에서 적절하게 동작할 수 있도록 전체 구조에서만 이 기능을 필요로 합니다.
(댓글 참조)
그attribute
접근법은 컴파일러에 따라 다르지만 패딩이 각 요소를 완전한 캐시 라인으로 반올림하는 것으로 제한되기 때문에 이 구조의 크기를 반으로 줄일 수 있습니다.이런 걸 많이 가지고 있으면 꽤 도움이 될 것 같아요.
C++뿐만 아니라 C에도 동일한 개념이 적용됩니다.
인터럽트 또는 고성능 데이터 읽기를 사용하는 경우 캐시 라인 경계(일반적으로 캐시 라인당 64바이트)에 맞춰야 할 수 있습니다.이 경계는 인터럽트 또는 프로세스 간 소켓을 사용하는 경우 반드시 사용해야 합니다.Interprocess 소켓에서는 여러 캐시 라인 또는 DDR RAM 워드에 분산할 수 없는 제어 변수가 있습니다.그렇지 않으면 L1, L2 등의 캐시 또는 DDR RAM이 로우패스 필터로 기능하여 인터럽트 데이터를 필터링할 수 있습니다.안 좋아!!!그것은 알고리즘이 훌륭할 때 이상한 오류가 발생하며, 그것은 당신을 미치게 할 가능성이 있다는 것을 의미합니다.
DDR RAM은 거의 항상 128비트 워드(DDR RAM Words)로 읽힙니다.따라서 링 버퍼 변수는 여러 DDR RAM 워드에 분산되어 있지 않습니다.시스템에 따라서는 64비트 DDR RAM 워드를 사용합니다.기술적으로는 16비트 CPU에서는 32비트 DDR RAM 워드를 사용할 수 있지만 SDRAM을 사용하는 경우도 있습니다.
또한 고성능 알고리즘에서 데이터를 읽을 때 사용되는 캐시 라인 수를 최소화하는 데에도 관심이 있을 수 있습니다.저는 세계에서 가장 빠른 정수 대 문자열 알고리즘(이전보다 40% 빠른 알고리즘)을 개발했으며, 세계에서 가장 빠른 부동 소수점 알고리즘인 그리수 알고리즘을 최적화하고 있습니다.부동소수점 숫자를 인쇄하려면 정수를 인쇄해야 합니다.그래서 그리스를 최적화하기 위해 구현한 최적화 중 하나는 그리스의 LUT(Lookup Table)를 정확히 15개의 캐시 라인으로 정렬하는 것입니다.이것은 실제로 그렇게 정렬된 것이 다소 이상합니다.그러면 .bss 섹션(즉, 정적 메모리)에서 LUT가 가져와 스택(또는 힙)에 배치됩니다.벤치마킹은 하지 않았지만 불러오는 것이 좋으며, 많은 것을 배웠습니다. 값을 로드하는 가장 빠른 방법은 d-cache가 아닌 i-cache에서 로드하는 것입니다.차이점은 i-cache는 읽기 전용이고 읽기 전용이기 때문에 캐시 행이 훨씬 크다는 것입니다(2KB는 교수가 한 번 인용한 것입니다).따라서 다음과 같은 변수를 로드하는 대신 어레이 인덱싱으로 인해 성능이 저하됩니다.
int faster_way = 12345678;
더 느린 방법과는 달리:
int variables[2] = { 12345678, 123456789};
int slower_way = variables[0];
차이가 요.int variable = 12345678
내의 , i-cache 행은 i-cache 행에서 로드됩니다.slower_way = int[0]
는 훨씬 느린 배열 인덱싱을 사용하여 작은 d-cache 행에서 로드됩니다.방금 발견한 이 특별한 알고리즘은 실제로 제 알고리즘과 다른 많은 정수 대 문자열 알고리즘을 느리게 하고 있습니다.읽기 전용 데이터를 캐시 정렬하여 최적화하는 데 문제가 있을 수 있기 때문입니다.
, C에서는, 「C++」를 합니다.std::align
기능.이 기능은 최적으로 동작하는 것이 보증되지 않기 때문에 사용하지 않는 것이 좋습니다.캐시 라인에 가장 빠르게 정렬할 수 있는 방법은 다음과 같습니다. 맨 앞에 제가 작성자이며, 이는 모조 없는 플러그입니다.
Kabuki Toolkit 메모리 얼라인먼트 알고리즘
namespace _ {
/* Aligns the given pointer to a power of two boundaries with a premade mask.
@return An aligned pointer of typename T.
@brief Algorithm is a 2's compliment trick that works by masking off
the desired number of bits in 2's compliment and adding them to the
pointer.
@param pointer The pointer to align.
@param mask The mask for the Least Significant bits to align. */
template <typename T = char>
inline T* AlignUp(void* pointer, intptr_t mask) {
intptr_t value = reinterpret_cast<intptr_t>(pointer);
value += (-value ) & mask;
return reinterpret_cast<T*>(value);
}
} //< namespace _
// Example calls using the faster mask technique.
enum { kSize = 256 };
char buffer[kSize + 64];
char* aligned_to_64_byte_cache_line = AlignUp<> (buffer, 63);
char16_t* aligned_to_64_byte_cache_line2 = AlignUp<char16_t> (buffer, 63);
다음으로 보다 빠른 표준::align 치환 방법을 제시하겠습니다.
inline void* align_kabuki(size_t align, size_t size, void*& ptr,
size_t& space) noexcept {
// Begin Kabuki Toolkit Implementation
intptr_t int_ptr = reinterpret_cast<intptr_t>(ptr),
offset = (-int_ptr) & (align - 1);
if ((space -= offset) < size) {
space += offset;
return nullptr;
}
return reinterpret_cast<void*>(int_ptr + offset);
// End Kabuki Toolkit Implementation
}
언급URL : https://stackoverflow.com/questions/8469427/how-and-when-to-align-to-cache-line-size
'programing' 카테고리의 다른 글
모든 JVM 플래그 인쇄 (0) | 2022.11.03 |
---|---|
오래된 데이터를 잃지 않고 FileOutputStream을 사용하여 데이터를 쓰는 방법 (0) | 2022.11.03 |
AWS 람다:작업 시간 초과 (0) | 2022.11.03 |
Carbon을 사용하여 다른 시간대에서 UTC 가져오기 (0) | 2022.11.03 |
Chrome 콘솔에서 VueX 작업/변환 오류 (0) | 2022.11.03 |