일반 최적화
내가 가장 좋아하는 최적화 중 일부입니다. 나는 이것을 사용함으로써 실제로 실행 시간을 늘리고 프로그램 크기를 줄였습니다.
작은 함수를 inline
또는 매크로 로 선언
함수 (또는 메서드)를 호출 할 때마다 변수를 스택에 푸시하는 것과 같은 오버 헤드가 발생합니다. 일부 함수는 반환시에도 오버 헤드가 발생할 수 있습니다. 비효율적 인 함수 또는 메서드는 결합 된 오버 헤드보다 콘텐츠에 더 적은 문을 포함합니다. #define
매크로 든 inline
함수 든 인라인하기에 좋은 후보입니다 . (예, inline
제안 일 뿐이라는 것을 알고 있지만이 경우 컴파일러에 대한 알림 으로 간주합니다 .)
죽은 중복 코드 제거
코드가 사용되지 않거나 프로그램의 결과에 기여하지 않으면 제거하십시오.
알고리즘 설계 단순화
한 번은 계산중인 대수 방정식을 적어 프로그램에서 많은 어셈블리 코드와 실행 시간을 제거한 다음 대수식을 단순화했습니다. 단순화 된 대수식의 구현은 원래 함수보다 공간과 시간을 덜 차지했습니다.
루프 풀기
각 루프에는 증가 및 종료 검사의 오버 헤드가 있습니다. 성능 요인의 추정치를 얻으려면 오버 헤드의 명령어 수 (최소 3 : 증가, 확인, 루프 시작으로 이동)를 계산하고 루프 내의 명령문 수로 나눕니다. 숫자가 낮을수록 좋습니다.
편집 : 루프 풀기의 예를 제공 하십시오 .
unsigned int sum = 0;
for (size_t i; i < BYTES_TO_CHECKSUM; ++i)
{
sum += *buffer++;
}
펼친 후 :
unsigned int sum = 0;
size_t i = 0;
**const size_t STATEMENTS_PER_LOOP = 8;**
for (i = 0; i < BYTES_TO_CHECKSUM; **i = i / STATEMENTS_PER_LOOP**)
{
sum += *buffer++; // 1
sum += *buffer++; // 2
sum += *buffer++; // 3
sum += *buffer++; // 4
sum += *buffer++; // 5
sum += *buffer++; // 6
sum += *buffer++; // 7
sum += *buffer++; // 8
}
// Handle the remainder:
for (; i < BYTES_TO_CHECKSUM; ++i)
{
sum += *buffer++;
}
이 이점에서 두 번째 이점이 있습니다. 프로세서가 명령 캐시를 다시로드하기 전에 더 많은 명령문이 실행됩니다.
32 개의 문에 대한 루프를 풀었을 때 놀라운 결과를 얻었습니다. 프로그램이 2GB 파일에서 체크섬을 계산해야했기 때문에 이것은 병목 현상 중 하나였습니다. 이 최적화는 블록 읽기와 결합되어 성능이 1 시간에서 5 분으로 향상되었습니다. 루프 언 롤링은 어셈블리 언어에서도 뛰어난 성능을 제공했으며 memcpy
, 컴파일러의 memcpy
. -TM
if
진술의 축소
프로세서는 프로세서가 명령 대기열을 다시로드하도록하기 때문에 분기 또는 점프를 싫어합니다.
부울 연산 ( 편집 : 코드 조각에 코드 형식 적용, 예제 추가)
if
문을 부울 할당으로 변환 합니다. 일부 프로세서는 분기없이 조건부로 명령을 실행할 수 있습니다.
bool status = true;
status = status && /* first test */;
status = status && /* second test */;
단락 의 논리 AND (오퍼레이터 &&
)가이 경우, 테스트 실행 방지 status
이다 false
.
예:
struct Reader_Interface
{
virtual bool write(unsigned int value) = 0;
};
struct Rectangle
{
unsigned int origin_x;
unsigned int origin_y;
unsigned int height;
unsigned int width;
bool write(Reader_Interface * p_reader)
{
bool status = false;
if (p_reader)
{
status = p_reader->write(origin_x);
status = status && p_reader->write(origin_y);
status = status && p_reader->write(height);
status = status && p_reader->write(width);
}
return status;
};
루프 외부 요인 변수 할당
루프 내에서 변수가 즉석에서 생성 된 경우 생성 / 할당을 루프 이전으로 이동합니다. 대부분의 경우 반복 할 때마다 변수를 할당 할 필요가 없습니다.
루프 외부에서 상수 표현식 인수
계산 또는 변수 값이 루프 인덱스에 의존하지 않는 경우 루프 외부 (앞)로 이동하십시오.
블록의 I / O
큰 청크 (블록)로 데이터를 읽고 씁니다. 클수록 좋습니다. 예를 들어 한 번 에 한 옥텟을 읽는 것은 한 번의 읽기로 1024 옥텟을 읽는 것보다 덜 효율적입니다.
예:
static const char Menu_Text[] = "\n"
"1) Print\n"
"2) Insert new customer\n"
"3) Destroy\n"
"4) Launch Nasal Demons\n"
"Enter selection: ";
static const size_t Menu_Text_Length = sizeof(Menu_Text) - sizeof('\0');
//...
std::cout.write(Menu_Text, Menu_Text_Length);
이 기술의 효율성은 시각적으로 입증 될 수 있습니다. :-)
상수 데이터에 printf
패밀리 를 사용하지 마십시오.
블록 쓰기를 사용하여 상수 데이터를 출력 할 수 있습니다. 형식화 된 쓰기는 문자 형식화 또는 형식화 명령 처리를 위해 텍스트를 스캔하는 데 시간을 낭비합니다. 위의 코드 예제를 참조하십시오.
메모리로 포맷 한 다음 쓰기
char
다중 sprintf
을 사용 하여 배열로 포맷 한 다음 fwrite
. 또한 데이터 레이아웃을 "상수 섹션"및 가변 섹션으로 나눌 수 있습니다. 메일 병합을 생각해보십시오 .
상수 텍스트 (문자열 리터럴)를 다음과 같이 선언합니다. static const
없이 변수를 선언하면 static
일부 컴파일러는 스택에 공간을 할당하고 ROM에서 데이터를 복사 할 수 있습니다. 이것은 두 가지 불필요한 작업입니다. static
접두사 를 사용하여 해결할 수 있습니다 .
마지막으로 컴파일러와 같은 코드는
때로는 컴파일러가 하나의 복잡한 버전보다 여러 개의 작은 문을 더 잘 최적화 할 수 있습니다. 또한 컴파일러가 최적화하는 데 도움이되는 코드를 작성하는 것도 도움이됩니다. 컴파일러가 특수 블록 전송 명령어를 사용하도록하려면 특수 명령어를 사용해야하는 것처럼 보이는 코드를 작성합니다.