메모리가 정렬되었는지 확인하는 방법은 무엇입니까?
-
19-09-2019 - |
문제
SSE/SSE2 지침으로 코드를 최적화하는 것이 처음이며 지금까지 나는 멀지 않았습니다. 내 아는 바와 같이 일반적인 SSE- 최적화 된 기능은 다음과 같습니다.
void sse_func(const float* const ptr, int len){
if( ptr is aligned )
{
for( ... ){
// unroll loop by 4 or 2 elements
}
for( ....){
// handle the rest
// (non-optimized code)
}
} else {
for( ....){
// regular C code to handle non-aligned memory
}
}
}
그러나 메모리가 ptr
포인트는 16 바이트에 의해 정렬됩니까? 이 기능에 전달 된 모든 메모리가 정렬 될 수 없으므로 정렬되지 않은 메모리에 대한 일반 C 코드 경로를 포함해야한다고 생각합니다. 그리고 정렬되지 않은 메모리에서 SSE 레지스터에 데이터를로드하기 위해 고입재를 사용하는 것은 끔찍한 느린 것 같습니다 (일반 C 코드보다 느리게).
미리 감사드립니다 ...
해결책
편집 : 캐스팅 long
오늘날 INT와 포인터가 다른 크기 일 가능성에 맞지 않도록 자신을 보호하는 저렴한 방법입니다.
아래 의견에서 지적했듯이 헤더를 기꺼이 포함하려는 경우 더 나은 솔루션이 있습니다 ...
포인터 p
16 바이트 경계 IFF에 정렬됩니다 ((unsigned long)p & 15) == 0
.
다른 팁
#define is_aligned(POINTER, BYTE_COUNT) \
(((uintptr_t)(const void *)(POINTER)) % (BYTE_COUNT) == 0)
캐스트 void *
(또는 동등성, char *
) 표준은 반전 전환 만 보장하기 때문에 필요합니다. uintptr_t
~을 위한 void *
.
유형 안전을 원한다면 인라인 함수 사용을 고려하십시오.
static inline _Bool is_aligned(const void *restrict pointer, size_t byte_count)
{ return (uintptr_t)pointer % byte_count == 0; }
컴파일러 최적화를 희망합니다 byte_count
컴파일 타임 상수입니다.
왜 우리가 변환해야합니까? void *
?
C 언어는 다른 포인터 유형에 대해 다른 표현을 허용합니다. 예를 들어 64 비트를 가질 수 있습니다. void *
입력 (전체 주소 공간) 및 32 비트 foo *
입력 (세그먼트).
변환 foo *
-> void *
예를 들어 오프셋을 추가하는 실제 계산이 포함될 수 있습니다. 또한 표준은 또한 (임의의) 포인터를 정수로 변환 할 때 발생하는 일에도 구현에 이르지만 종종 누프로 구현 된 것으로 생각됩니다.
그러한 구현을 위해 foo *
-> uintptr_t
-> foo *
효과가 있지만 foo *
-> uintptr_t
-> void *
그리고 void *
-> uintptr_t
-> foo *
그렇지 않을 것입니다. 세그먼트 오프셋과 관련하여 정렬 만 확인하기 때문에 정렬 계산도 안정적으로 작동하지 않습니다.
결론적으로 : 항상 사용하십시오 void *
구현 독립적 행동을 얻기 위해.
다른 답변은 낮은 비트 세트로 AN 및 작동을 제안하고 0과 비교합니다.
그러나보다 간단한 테스트는 원하는 정렬 값을 가진 모드를 수행하고 0과 비교하는 것입니다.
#define ALIGNMENT_VALUE 16u
if (((uintptr_t)ptr % ALIGNMENT_VALUE) == 0)
{
// ptr is aligned
}
기능 템플릿과 같은
#include <type_traits>
template< typename T >
bool is_aligned(T* p){
return !(reinterpret_cast<uintptr_t>(p) % std::alignment_of<T>::value);
}
다음과 같은 것을 호출하여 런타임시 정렬을 확인할 수 있습니다.
struct foo_type{ int bar; }foo;
assert(is_aligned(&foo)); // passes
잘못된 정렬이 실패하는 것을 확인하려면
// would almost certainly fail
assert(is_aligned((foo_type*)(1 + (uintptr_t)(&foo)));
이것은 기본적으로 내가 사용하는 것입니다. 정수를 템플릿으로 만들면 컴파일 시간이 확장되어 있으므로 내가 무엇을하든 느린 모듈로 작동으로 끝나지 않습니다.
나는 항상 내 입력을 확인하는 것을 좋아하므로 컴파일 타임 어설 션이 있습니다. 정렬 값이 잘못되면 잘 컴파일하지 않습니다 ...
template <unsigned int alignment>
struct IsAligned
{
static_assert((alignment & (alignment - 1)) == 0, "Alignment must be a power of 2");
static inline bool Value(const void * ptr)
{
return (((uintptr_t)ptr) & (alignment - 1)) == 0;
}
};
무슨 일이 일어나고 있는지 확인하려면 다음을 사용할 수 있습니다.
// 1 of them is aligned...
int* ptr = new int[8];
for (int i = 0; i < 8; ++i)
std::cout << IsAligned<32>::Value(ptr + i) << std::endl;
// Should give '1'
int* ptr2 = (int*)_aligned_malloc(32, 32);
std::cout << IsAligned<32>::Value(ptr2) << std::endl;
당신은 단지 '와'0x03 (4S에 정렬), 0x07 (8 초에 정렬) 또는 0x0F (16S에 정렬)를 가진 PTR을 설정하여 가장 낮은 비트가 설정되어 있는지 확인할 수 있습니까?
전문가에게 맡기십시오.
bool is_aligned(const void* ptr, std::size_t alignment) noexcept;
예시:
char D[1];
assert( boost::alignment::is_aligned(&D[0], alignof(double)) ); // might fail, sometimes
어때요 :
void *mem = malloc(1024+15);
void *ptr =( (*(char*)mem) - (*(char *)mem % 16) );