C/C++에서 양의 모듈로를 얻는 가장 빠른 방법
내부 루프에서는 어레이 크기가 100이고 코드가 요소 -2를 요구하는 경우 요소 98을 지정할 수 있도록 "랩 어라운드" 방식으로 어레이를 인덱싱해야 하는 경우가 많습니다.Python과 많은 에서는 Python을 사용하여 이 할 수 .my_array[index % array_size]
그러나 어떤 이유에서인지 C의 정수 산술(보통)은 일관되게 반올림하는 대신 0을 향해 반올림하고 결과적으로 그 모듈로 연산자는 음의 첫 번째 인수를 지정하면 음의 결과를 반환합니다.
는 종종 있다.index
않을 것이다-array_size
'아까', '아까', '아까', '아까' 이렇게 my_array[(index + array_size) % array_size]
단, 이것은 보증할 수 없는 경우가 있기 때문에, 그러한 경우, 항상 포지티브인 모듈로 기능을 구현하는 가장 빠른 방법을 알고 싶습니다.않고 할 수 몇 가지 .예를 들어 다음과 같습니다.
inline int positive_modulo(int i, int n) {
return (n + (i % n)) % n;
}
또는
inline int positive_modulo(int i, int n) {
return (i % n) + (n * (i < 0));
}
물론 시스템에서 가장 빠른 것을 찾기 위해 프로파일을 작성할 수 있지만, 더 나은 것을 놓쳤거나 다른 시스템에서 더 빠른 것이 더 느릴 수 있다는 우려를 금할 수 없습니다.
그럼 표준적인 방법이나 제가 놓친 가장 빠른 방법이 있을까요?
또 희망사항일 수도 있지만 자동벡터화할 수 있는 방법이 있다면 정말 좋을 것 같습니다.
제가 배운 표준적인 방법은
inline int positive_modulo(int i, int n) {
return (i % n + n) % n;
}
으로는 처음 입니다.abs
(어느쪽이든)최적화 컴파일러가 이 패턴을 인식하고 "서명되지 않은 모듈로"를 계산하는 기계 코드로 컴파일할 수 있다고 해도 나는 놀라지 않을 것이다.
편집:
변종으로 넘어가겠습니다.. - 입니다.n < 0
should be i < 0
.
한 것처럼 수많은 에서 이 배리언트는 된 것처럼 보입니다.i < 0
이치노경우든, 이 빠를 것입니다.(n * (i < 0))
i < 0? n: 0
이것은 곱셈을 회피합니다.게다가 부울을 int로 재해석하는 것을 회피하기 때문에, 「단순」입니다.
이 두 가지 변형 중 어느 것이 더 빠른지에 대해서는 컴파일러와 프로세서 아키텍처에 따라 달라집니다.이 두 변형 중 어느 것이 더 빠른지 시간을 두고 확인할 수 있습니다.하지만 이 두 변종보다 더 빠른 방법은 없을 것 같습니다.
대부분의 경우 컴파일러는 코드를 최적화하는 데 매우 능숙하기 때문에 보통 코드를 판독 가능한 상태로 유지하는 것이 좋습니다(컴파일러와 다른 개발자가 모두 사용자가 무엇을 하고 있는지 알 수 있습니다).
어레이 사이즈는 항상 플러스이므로, 다음과 같이 지수를 정의할 것을 권장합니다.unsigned
컴파일러는 작은 if/else 블록을 브랜치가 없는 조건부 명령으로 최적화합니다.
unsigned modulo( int value, unsigned m) {
int mod = value % (int)m;
if (mod < 0) {
mod += m;
}
return mod;
}
이것에 의해, 브랜치가 없는 매우 작은 함수가 작성됩니다.
modulo(int, unsigned int):
mov eax, edi
cdq
idiv esi
add esi, edx
mov eax, edx
test edx, edx
cmovs eax, esi
ret
를 들어, 「」입니다.modulo(-5, 7)
2
.
안타깝게도 이 몫은 알 수 없기 때문에 정수 나눗셈을 수행해야 합니다. 이는 다른 정수 연산과 비교하여 약간 느립니다.어레이의 크기가 2배라는 것을 알고 있다면 이러한 함수 정의를 헤더에 저장하여 컴파일러가 보다 효율적인 함수로 최적화할 수 있도록 하는 것이 좋습니다., 그럼 이 함수는 다음과 .unsigned modulo256(int v) { return modulo(v,256); }
:
modulo256(int): # @modulo256(int)
mov edx, edi
sar edx, 31
shr edx, 24
lea eax, [rdi+rdx]
movzx eax, al
sub eax, edx
lea edx, [rax+256]
test eax, eax
cmovs eax, edx
ret
어셈블리 참조: https://gcc.godbolt.org/z/DG7jMw
가장 많이 투표된 답변과의 비교를 참조하십시오.http://quick-bench.com/oJbVwLr9G5HJb0oRaYpQOCec4E4
편집: Clang은 조건부 이동 명령 없이 함수를 생성할 수 있습니다(일반 산술 연산보다 비용이 많이 듭니다).이 차이는 적분분분할이 전체 시간의 약 70%를 차지하기 때문에 일반적인 경우 완전히 무시할 수 있다.
꽝은 꽝, 꽝, 꽝, 꽝, 꽝, 꽝, 꽝.value
m
, (실제)0xffffffff
및 부정의 0
않은 경우의 두 위해 사용됩니다.mod + m
.
unsigned modulo (int value, unsigned m) {
int mod = value % (int)m;
m &= mod >> std::numeric_limits<int>::digits;
return mod + m;
}
2의 거듭제곱을 곱하면 다음과 같은 작업이 수행됩니다(2가 보형 표현이라고 가정).
return i & (n-1);
C/C++에서 양의 모듈로를 얻는 가장 빠른 방법
다음과 같은 속도가 빠른가요? - 다른 제품보다 빠르지 않을 수 있지만, 모든 고객에게1 심플하고 기능적으로 적합합니다. a,b
과 달리
int modulo_Euclidean(int a, int b) {
int m = a % b;
if (m < 0) {
// m += (b < 0) ? -b : b; // avoid this form: -b is UB when b == INT_MIN
m = (b < 0) ? m - b : m + b;
}
return m;
}
에도 여러 가지 이 있습니다.mod(a,b)
약점b < 0
.
에 대한 아이디어는 유클리드 나눗셈을 참조하십시오.b < 0
inline int positive_modulo(int i, int n) {
return (i % n + n) % n;
}
「」의 경우에 한다.i % n + n
i, n
- 되지 않은 동작 「 」 。
return i & (n-1);
의존하다n
2의 거듭제곱이 됩니다. (정답에 이런 말이 있는 것은 당연합니다.)
int positive_mod(int i, int n)
{
/* constexpr */ int shift = CHAR_BIT*sizeof i - 1;
int m = i%n;
return m+ (m>>shift & n);
}
「 」의 경우에 하는 경우가 .n < 0
e, g,positive_mod(-2,-3) --> -5
int32_t positive_modulo(int32_t number, int32_t modulo) {
return (number + ((int64_t)modulo << 32)) % modulo;
}
2개의 정수폭을 사용해야 합니다.(답변에서 이에 대해 언급하는 것이 적절합니다.)
가 발생하다에서 실패하다modulo < 0
positive_modulo(2, -3)
--> -1.
inline int positive_modulo(int i, int n) {
int tmp = i % n;
return tmp ? i >= 0 ? tmp : tmp + n : 0;
}
「 」의 경우에 하는 경우가 .n < 0
e, g,positive_modulo(-2,-3) --> -5
1 예외:주식회사,a%b
되어 않다a/b
a/0
★★★★★★★★★★★★★★★★★」INT_MIN/-1
.
모든 조건부 경로(위에서 생성된 조건부 이동 포함)를 회피하려면(예를 들어 이 코드를 벡터화하거나 일정한 시간에 실행해야 하는 경우) 기호 비트를 마스크로 사용할 수 있습니다.
unsigned modulo(int value, unsigned m) {
int shift_width = sizeof(int) * 8 - 1;
int tweak = (value >> shift_width);
int mod = ((value - tweak) % (int) m) + tweak;
mod += (tweak & m);
return mod;
}
다음은 Quickbench 결과입니다.gcc에서는 일반적인 케이스가 더 낫다는 것을 알 수 있습니다.clang의 경우 일반 케이스와 같은 속도입니다.clang은 일반 케이스에서 브랜치프리 코드를 생성하기 때문입니다.컴파일러가 특정 최적화를 수행할 때 항상 신뢰할 수 있는 것은 아니며 벡터 코드를 위해 수동으로 컴파일러를 롤링해야 할 수도 있기 때문에 이 기술은 어떤 경우에도 유용합니다.
보다 큰 타입으로 프로모트 할 수 있는 경우(및 보다 큰 타입으로 modulo를 실행할 수 있는 경우), 이 코드는 단일 modulo를 실행하고 다음 경우 no를 수행합니다.
int32_t positive_modulo(int32_t number, int32_t modulo) {
return (number + ((int64_t)modulo << 32)) % modulo;
}
2개의 보완 부호 비트의 전파를 사용하여 옵션의 애드엔드를 입수하는 구식 방법:
int positive_mod(int i, int m)
{
/* constexpr */ int shift = CHAR_BIT*sizeof i - 1;
int r = i%m;
return r+ (r>>shift & m);
}
array[(i+array_size*N) % array_size]
여기서 N은 양의 인수를 보증하기에 충분한 정수이지만 오버플로하지 않도록 충분히 작습니다.
array_size가 일정할 경우 계수를 분할하지 않고 계산하는 기술이 있습니다.두 가지 접근법의 검정력 외에도 비트그룹의 가중치 합계에 2^i % n을 곱한 값을 계산할 수 있다. 여기서 i는 각 그룹에서 최하위 비트이다.
예: 32비트 정수 0xaabbccd % 100 = dd + cc*[2]56 + bb*[655]36 + aa*[167772]16, 최대 범위는 (1+56+36+16)*255 = 27795입니다.반복적인 적용과 다른 세분화를 통해 연산을 몇 가지 조건부 감산으로 줄일 수 있습니다.
일반적인 관행은 또한 2^32/n의 역수를 갖는 나눗셈의 근사치를 포함하며, 이는 일반적으로 상당히 넓은 범위의 인수를 처리할 수 있다.
i - ((i * 655)>>16)*100; // (gives 100*n % 100 == 100 requiring adjusting...)
당신의 두 번째 예가 첫 번째 예보다 낫다.곱셈은 if/else 연산보다 더 복잡한 연산이므로 다음을 사용하십시오.
inline int positive_modulo(int i, int n) {
int tmp = i % n;
return tmp ? i >= 0 ? tmp : tmp + n : 0;
}
언급URL : https://stackoverflow.com/questions/14997165/fastest-way-to-get-a-positive-modulo-in-c-c
'IT이야기' 카테고리의 다른 글
드래그 클릭을 방지하려면 어떻게 해야 합니까? (0) | 2022.06.27 |
---|---|
mocha 및 styles-resources-loader가 있는 Vue.js는 종속성 sass를 로드할 수 없습니다. (0) | 2022.06.27 |
서버 측에서 렌더링된 콘텐츠가 vue로 대체되는 이유 (0) | 2022.06.25 |
Nuxt.js 앱의 Vuex 스토어에서 값을 설정하는 방법 (0) | 2022.06.25 |
하위 경로가 여전히 상위 구성 요소만 로드하고 있습니다. (0) | 2022.06.25 |