내장형 성능: char vs short vs int vs float vs double
다소 바보 같은 질문 같지만 다른 토픽에서 Alexandre C의 답변을 보면 내장된 타입과 성능 차이가 있는지 궁금합니다.
char
»short
»int
★★float
★★double
.
통상, 실생활의 프로젝트에서는, 그러한 퍼포먼스 차이는 고려하지 않습니다만, 교육상의 목적으로 알고 싶습니다.일반적인 질문은 다음과 같습니다.
적분 산술과 부동 소수점 산술 사이에 성능 차이가 있습니까?
어느 쪽이 빠릅니까?더 빠른 이유는 무엇입니까?이걸 설명해 주세요.
부동 대 정수:
지금까지 부동소수는 정수 연산보다 훨씬 느릴 수 있습니다.최신 컴퓨터에서는 이러한 현상이 더 이상 발생하지 않습니다(일부 플랫폼에서는 다소 느리지만 완벽한 코드를 작성하여 모든 사이클에 최적화하지 않는 한 그 차이는 코드의 다른 비효율성에 의해 메워집니다).
하이엔드 휴대폰과 같이 다소 제한된 프로세서에서는 부동소수가 정수보다 다소 느릴 수 있지만 하드웨어 부동소수를 사용할 수 있는 한 일반적으로 크기(또는 그 이상)의 범위 내에 있습니다.휴대전화가 점점 더 일반적인 컴퓨팅 워크로드를 실행하도록 요구됨에 따라 이 격차가 매우 빠르게 좁혀지고 있습니다.
매우 한정된 프로세서(저가 휴대전화 및 토스터)에서는 일반적으로 부동소수점 하드웨어가 없기 때문에 부동소수점 연산을 소프트웨어로 에뮬레이트해야 합니다.이것은 느립니다. 정수 산술보다 몇 배 느립니다.
다만, 말씀드린 것처럼, 사람들은 전화나 다른 기기가 「실제 컴퓨터」와 같이 동작하는 것을 기대하고 있습니다.하드웨어 설계자는 그 요구를 만족시키기 위해 FPU를 급속히 강화하고 있습니다.마지막 사이클마다 추적하거나 부동소수점 지원이 거의 또는 전혀 없는 매우 제한된 CPU에 대한 코드를 작성하는 경우가 아니라면 성능 차이는 중요하지 않습니다.
다른 크기 정수 유형:
일반적으로 CPU는 네이티브 워드 크기의 정수에서 가장 빠르게 동작합니다(64비트 시스템에 대한 주의 사항이 있습니다).32비트 동작은 일반적으로 최신 CPU에서의8비트 또는 16비트 동작보다 빠르지만 아키텍처에 따라 상당히 다릅니다.또한 CPU의 속도를 분리하여 고려할 수 없습니다. CPU는 복잡한 시스템의 일부이기 때문입니다.16비트 번호로 동작하는 것이 32비트 번호로 동작하는 것보다 2배 느리더라도 32비트 대신 16비트 번호로 표현하면 캐시 계층에 2배의 데이터를 넣을 수 있습니다.이로 인해 캐시 누락이 빈번하게 발생하는 대신 모든 데이터를 캐시에서 가져오는 것이 다른 경우, 메모리 액세스 속도가 더 빠를수록 CPU의 동작이 더 느려집니다.
기타 주의사항:
는 보다 타입즉, 보다 좁은 타입)에.float
및 8비트 및 16비트 정수) -- 같은 폭의 벡터 내에서 더 많은 연산을 수행할 수 있습니다.그러나 좋은 벡터 코드는 쓰기 어렵기 때문에 많은 주의를 기울이지 않고서는 이 장점을 얻을 수 없습니다.
퍼포먼스 차이가 나는 이유는 무엇입니까?
CPU에서의 동작의 고속화에 영향을 주는 요인은 동작의 회선의 복잡성과 동작의 고속화에 대한 사용자의 요구의 2가지뿐입니다.
칩 설계자가 문제에 충분한 트랜지스터를 던질 의향이 있다면 (합리적인 범위 내에서) 어떤 동작도 빠르게 할 수 있습니다.그러나 트랜지스터는 비용이 많이 듭니다(또는 트랜지스터를 많이 사용하면 칩이 커집니다.즉, 웨이퍼당 칩 수가 적어지고 수율이 낮아집니다). 따라서 칩 설계자는 어느 정도의 복잡한 작업을 어느 정도 사용해야 하는지 균형을 맞춰야 합니다.사용자의 요구(인식된)를 기반으로 합니다.대략, 조작을 다음의 4개의 카테고리로 나눌 수 있습니다.
high demand low demand
high complexity FP add, multiply division
low complexity integer add popcount, hcf
boolean ops, shifts
높은 수요와 복잡성이 낮은 작업은 거의 모든 CPU에서 빠르게 이루어집니다. 즉, CPU는 매달림이 적고 트랜지스터당 최대의 사용자 이점을 제공합니다.
사용자가 기꺼이 비용을 지불하기 때문에 고가의 CPU(컴퓨터에 사용되는 CPU 등)에서는 수요가 많고 복잡도가 높은 작업이 빠르게 이루어집니다.단, 고속 FP 증식을 위해 토스터에 3달러를 추가로 지불하고 싶지는 않을 것입니다.따라서 저렴한 CPU는 이러한 명령어를 사용할 필요가 없습니다.
저수요, 고복잡한 조작은 일반적으로 거의 모든 프로세서에서 느립니다.그것은 비용을 정당화할 수 있는 충분한 이점이 없기 때문입니다.
다른 사람이 신경 쓰면 빠르고, 그렇지 않으면 존재하지 않는 저수요, 저복잡도 운영이 빨라집니다.
추가 정보:
- Agner Fog는 낮은 수준의 성능 세부 사항에 대한 많은 논의가 있는 멋진 웹사이트를 운영하고 있습니다(또한 이를 뒷받침하는 매우 과학적인 데이터 수집 방법론도 있습니다).
- 인텔 ® 64 / IA-32 아키텍처 최적화 레퍼런스 매뉴얼 (PDF 다운로드 링크는 페이지 하단에 있습니다)도 이러한 많은 문제를 다루고 있습니다.단, 특정 아키텍처 패밀리에 초점을 맞추고 있습니다.
승급규칙에 대해서는 아무도 언급하지 않은 것 같습니다. C C/C++ 서 음 음 음 음 음 smaller 음 음 음 음 음 음 음 than than than than smaller smaller than smaller smaller smaller smaller smaller a a a a a a a be be a can a a a a a a a a a a a a aint
현재 플랫폼에서 char 또는 short가 int보다 작을 경우 int(버그의 주요 소스)로 암묵적으로 승격됩니다.컴파일러는 이러한 암묵적인 프로모션을 수행해야 합니다. 표준을 위반하지 않고는 피할 수 없습니다.
정수 프로모션은 int보다 작은 정수 유형에서는 언어 내 연산(더하기, 비트 단위, 논리 등)을 수행할 수 없음을 의미합니다.따라서 char/short/int에 대한 연산은 전자의 연산이 후자로 승격되기 때문에 일반적으로 동일하게 빠릅니다.
그리고 정수 승진 위에 "통상적인 산술 변환"이 있습니다. 즉, C는 두 피연산자를 같은 유형으로 만들고, 두 피연산자 중 하나가 다른 경우 더 큰 피연산자로 변환합니다.
단, CPU는 8, 16, 32 등의 레벨에서 다양한 로드/스토어 조작을 실행할 수 있습니다.8비트 및 16비트 아키텍처에서는 8비트 및 16비트타입이 정수 프로모션에도 불구하고 고속임을 의미할 수 있습니다.32비트 CPU에서는 모든 것을 32비트 청크로 깔끔하게 정렬하고 싶기 때문에 작은 타입의 CPU에서는 속도가 느려질 수 있습니다.일반적으로 32비트 컴파일러는 속도를 최적화하고 지정된 공간보다 큰 공간에 작은 정수 타입을 할당합니다.
일반적으로 작은 정수형은 큰 정수형보다 공간을 적게 차지하기 때문에 RAM 크기에 맞게 최적화할 경우 더 적합합니다.
위의 첫 번째 답변은 훌륭합니다.그리고 저는 그 작은 블록을 다음 복제품에 복사했습니다(이것이 제가 먼저 끝냈기 때문입니다.
다양한 정수 크기에 대해 할당, 초기화 및 산술을 수행하는 프로파일의 코드를 제공하고자 합니다.
#include <iostream>
#include <windows.h>
using std::cout; using std::cin; using std::endl;
LARGE_INTEGER StartingTime, EndingTime, ElapsedMicroseconds;
LARGE_INTEGER Frequency;
void inline showElapsed(const char activity [])
{
QueryPerformanceCounter(&EndingTime);
ElapsedMicroseconds.QuadPart = EndingTime.QuadPart - StartingTime.QuadPart;
ElapsedMicroseconds.QuadPart *= 1000000;
ElapsedMicroseconds.QuadPart /= Frequency.QuadPart;
cout << activity << " took: " << ElapsedMicroseconds.QuadPart << "us" << endl;
}
int main()
{
cout << "Hallo!" << endl << endl;
QueryPerformanceFrequency(&Frequency);
const int32_t count = 1100100;
char activity[200];
//-----------------------------------------------------------------------------------------//
sprintf_s(activity, "Initialise & Set %d 8 bit integers", count);
QueryPerformanceCounter(&StartingTime);
int8_t *data8 = new int8_t[count];
for (int i = 0; i < count; i++)
{
data8[i] = i;
}
showElapsed(activity);
sprintf_s(activity, "Add 5 to %d 8 bit integers", count);
QueryPerformanceCounter(&StartingTime);
for (int i = 0; i < count; i++)
{
data8[i] = i + 5;
}
showElapsed(activity);
cout << endl;
//-----------------------------------------------------------------------------------------//
//-----------------------------------------------------------------------------------------//
sprintf_s(activity, "Initialise & Set %d 16 bit integers", count);
QueryPerformanceCounter(&StartingTime);
int16_t *data16 = new int16_t[count];
for (int i = 0; i < count; i++)
{
data16[i] = i;
}
showElapsed(activity);
sprintf_s(activity, "Add 5 to %d 16 bit integers", count);
QueryPerformanceCounter(&StartingTime);
for (int i = 0; i < count; i++)
{
data16[i] = i + 5;
}
showElapsed(activity);
cout << endl;
//-----------------------------------------------------------------------------------------//
//-----------------------------------------------------------------------------------------//
sprintf_s(activity, "Initialise & Set %d 32 bit integers", count);
QueryPerformanceCounter(&StartingTime);
int32_t *data32 = new int32_t[count];
for (int i = 0; i < count; i++)
{
data32[i] = i;
}
showElapsed(activity);
sprintf_s(activity, "Add 5 to %d 32 bit integers", count);
QueryPerformanceCounter(&StartingTime);
for (int i = 0; i < count; i++)
{
data32[i] = i + 5;
}
showElapsed(activity);
cout << endl;
//-----------------------------------------------------------------------------------------//
//-----------------------------------------------------------------------------------------//
sprintf_s(activity, "Initialise & Set %d 64 bit integers", count);
QueryPerformanceCounter(&StartingTime);
int64_t *data64 = new int64_t[count];
for (int i = 0; i < count; i++)
{
data64[i] = i;
}
showElapsed(activity);
sprintf_s(activity, "Add 5 to %d 64 bit integers", count);
QueryPerformanceCounter(&StartingTime);
for (int i = 0; i < count; i++)
{
data64[i] = i + 5;
}
showElapsed(activity);
cout << endl;
//-----------------------------------------------------------------------------------------//
getchar();
}
/*
My results on i7 4790k:
Initialise & Set 1100100 8 bit integers took: 444us
Add 5 to 1100100 8 bit integers took: 358us
Initialise & Set 1100100 16 bit integers took: 666us
Add 5 to 1100100 16 bit integers took: 359us
Initialise & Set 1100100 32 bit integers took: 870us
Add 5 to 1100100 32 bit integers took: 276us
Initialise & Set 1100100 64 bit integers took: 2201us
Add 5 to 1100100 64 bit integers took: 659us
*/
i7 4790k의 MSVC 결과:
& 444us1100100 8비트: 444us
정수 , 1,100' 8비트: 358us
& 666usInitialize & Set 1100100 16비트: 666us
정수 359us1100100 5 5 11 16 비 :
& 취득:
추가: 276us110000 5 5 더 32 트 : 276us
& 정수:
정수: 트정001100100 55 가 : 659us
그렇고 말고요.
첫째, 물론 문제의 CPU 아키텍처에 전적으로 의존합니다.
단, 정수형 및 부동소수점 유형은 매우 다르게 처리되므로 거의 항상 다음과 같습니다.
- 간단한 조작의 경우, 적분 타입은 고속입니다.예를 들어 정수 덧셈은 보통 단일 사이클의 지연 시간만 가지며 정수 곱셈은 일반적으로 약 2-4 사이클(IIRC)입니다.
- 훨씬 느린 수행에 사용되는 부동 소수점 유형입니다.그러나 오늘날의 CPU에서는 뛰어난 throughput을 갖추고 있으며, 각 부동소수점 유닛은 보통 사이클마다 작업을 폐기할 수 있기 때문에 정수 연산과 동일한(또는 유사한) throughput을 얻을 수 있습니다.그러나 일반적으로 지연이 더 심합니다.부동소수점 추가의 지연은 약 4 사이클입니다(ints의 경우 1).
- 일부 복잡한 운영의 경우 상황이 다르거나 심지어 역전될 수도 있습니다.예를 들어 FP 상의 나눗셈은 단순히 두 경우 모두 구현이 복잡하기 때문에 정수에 비해 지연 시간이 적을 수 있지만 FP 값에 더 일반적으로 유용하기 때문에 FP의 경우 최적화에 더 많은 노력(및 트랜지스터)이 소요될 수 있습니다.
CPU에 따라서는 더블이 플로트보다 훨씬 느릴 수 있습니다.일부 아키텍처에서는 더블 전용 하드웨어가 없기 때문에 플로트 크기의 청크를 2개 통과시킴으로써 처리량이 향상되고 대기 시간이 2배로 늘어납니다.그 외(예를 들어 x86 FPU)에서는 두 유형이 모두 동일한 내부 형식 80비트 부동소수로 변환되므로 성능은 동일합니다.또, float와 double 모두 적절한 하드웨어를 서포트하고 있습니다만, float의 비트가 적기 때문에, float의 처리 속도가 약간 빨라져, 통상, 2배의 조작에 비해 레이텐시를 약간 단축할 수 있습니다.
면책사항: 언급한 모든 타이밍과 특성은 메모리에서 추출한 것입니다.제가 찾아본 게 없어서 틀렸을 수도 있어요.;)
정수 타입에 따라 답은 CPU 아키텍처에 따라 크게 다릅니다.x86 아키텍처는 오랜 역사를 가지고 있기 때문에 8, 16, 32(현재는 64)비트 조작을 모두 네이티브로 지원해야 합니다.일반적으로 모두 같은 하드웨어를 사용하며 필요에 따라 상위 비트를 제로로 합니다.
다른 에서는 데이터 .int
로드/저장 비용이 더 많이 들 수 있습니다(바이트를 메모리에 쓰려면 바이트가 위치한 32비트 워드 전체를 로드한 다음 비트 마스킹을 수행하여 레지스터에서 단일 바이트를 업데이트한 다음 전체 단어를 다시 써야 합니다).로, 타입이 데이터 타입보다 큰 int
일부 CPU는 동작을 2개로 분할하여 로딩/스토리지/연산을 하부와 상부를 분리해야 할 수 있습니다.
그러나 x86의 경우, 답은 거의 중요하지 않다는 것입니다.과거의 이유로 CPU는 모든 데이터 유형에 대해 상당히 강력한 지원을 제공해야 합니다.단, 부동소수점 ops는 레이텐시가 더 높다는 점(단, 스루풋은 비슷하기 때문에 코드를 올바르게 쓰면 적어도 그 자체로는 느려지지 않습니다)을 알 수 있습니다.
일반적으로 정수 수학은 부동 소수점 수학보다 빠릅니다.정수 수학은 더 간단한 계산을 수반하기 때문입니다.그러나 대부분의 작업에서는 12개 미만의 클럭이 사용됩니다.밀리, 마이크로, 나노, 진드기가 아니라 시계입니다.현대 코어에서 초당 20억에서 30억 번 발생하는 현상입니다.또한 486의 많은 코어에는 부동소수점 처리장치(FPU)가 있으며 부동소수점 연산을 효율적으로 수행하기 위해 유선 연결되어 있으며 CPU와 병렬로 연결되어 있는 경우가 많습니다.
그 결과 기술적으로는 느리지만 부동소수점 계산은 여전히 매우 빠릅니다.따라서 차분 시간을 재려고 하면 실제로 계산을 수행할 때보다 타이밍 메커니즘과 스레드 스케줄링에 고유한 오류가 더 많이 발생합니다.가능한 경우에는 ints를 사용하되, 사용할 수 없는 경우에는 이해하고 상대적인 계산 속도에 대해 너무 걱정하지 마십시오.
적분 산술과 부동 소수점 산술 사이에 성능 차이가 있습니까?
네. 하지만 이는 플랫폼과 CPU에 따라 매우 다릅니다.플랫폼마다 다른 속도로 다른 산술 연산을 수행할 수 있습니다.
그렇다고는 해도, 문제의 회답은 조금 더 구체적이었다. pow()
는 이중값으로 동작하는 범용 루틴입니다.정수값을 입력함으로써 비정수 지수를 처리하는 데 필요한 모든 작업을 수행하고 있습니다.직접 곱셈을 사용하면 많은 복잡함을 우회할 수 있습니다.그 때문에, 속도가 향상됩니다.이것은 실제로 (많은 종류의) 문제가 아니라, 어떤 지수로든 pow 함수를 만들기 위해 필요한 대량의 복잡한 코드를 우회하는 것입니다.
프로세서와 플랫폼의 구성에 따라 다릅니다.
부동소수점 코프로세서를 탑재한 플랫폼에서는 코프로세서와 값을 주고받을 필요가 있기 때문에 정수 연산보다 속도가 느려질 수 있습니다.
부동소수점 처리가 프로세서의 코어 내에 있는 경우 실행 시간은 무시할 수 있습니다.
부동소수점 계산이 소프트웨어에 의해 에뮬레이트되면 적분 산술이 더 빨라집니다.
확실하지 않을 때는 프로파일을 작성하십시오.
최적화하기 전에 프로그래밍이 올바르게 작동하도록 하십시오.
아니, 그렇지 않아.물론 CPU와 컴파일러에 따라 다르지만 성능 차이는 거의 없습니다.
부동 소수점과 정수 산술 사이에는 분명히 차이가 있다.CPU의 특정 하드웨어 및 마이크로 명령에 따라 성능 및 정밀도가 달라집니다.정확한 설명을 위한 좋은 구글 용어입니다(저도 정확히는 모릅니다).
FPU x87 MMX SSE
크기 그)를 이 사이즈는, 「」( 「2」)로 됩니다.int32_t
및 x86 »int64_t
x86_64의 경우. 값 중동시에 및 MMX 등)을 가지고 있을 수 있으므로 곱셈 .SOME 프로세서에는 이러한 값 중 여러 개를 한 번에 처리하는 고유 명령(SSE(부동소수점) 및 MMX 등)이 있어 병렬 추가 또는 곱셈 속도를 높일 수 있습니다.
언급URL : https://stackoverflow.com/questions/5069489/performance-of-built-in-types-char-vs-short-vs-int-vs-float-vs-double
'programing' 카테고리의 다른 글
하위 구성 요소에서 다른 VueJS 하위 구성 요소로 참조 컨텐츠 전달 (0) | 2022.08.18 |
---|---|
하나의 .vue 파일에 대해서만 vue/multi-word-component-names eslint 규칙을 해제하려면 어떻게 해야 합니까? (0) | 2022.08.18 |
이 v-tabs Vuetify.js 컴포넌트를 작동시키려면 어떻게 해야 합니까? (0) | 2022.08.18 |
typedef 구조 vs 구조 정의 (0) | 2022.08.18 |
Vue에서 프레젠테이션 데이터 관리(이벤트 크기 조정) (0) | 2022.08.18 |