최후의 수단의 성능 최적화 전략 [닫기]


609

이 사이트에는 이미 성능에 관한 질문이 많이 있지만, 거의 모든 것이 문제별로 다르고 상당히 좁습니다. 거의 모든 것이 조기 최적화를 피하기 위해 조언을 반복합니다.

가정하자 :

  • 코드가 이미 올바르게 작동하고 있습니다
  • 선택한 알고리즘은 문제의 상황에 대해 이미 최적입니다
  • 코드가 측정되었으며 문제가되는 루틴이 격리되었습니다.
  • 모든 최적화 시도도 문제를 악화시키지 않도록 측정됩니다.

내가 여기서 찾고있는 것은 남은 일 외에는 아무것도하지 않을 때 중요한 알고리즘에서 마지막 몇 퍼센트까지 짜내는 전략과 요령입니다.

이상적으로는 언어에 구애받지 않고 대답을 시도하고 해당되는 경우 제안 된 전략에 대한 단점을 나타냅니다.

나는 자신의 초기 제안으로 답장을 추가하고 스택 오버플로 커뮤니티가 생각할 수있는 다른 것을 기대합니다.

답변:


427

좋아, 당신은 개선의 여지가별로없는 것처럼 문제를 정의하고 있습니다. 내 경험상 그것은 매우 드 rare니다. 나는 1993 년 11 월 Dr. Dobbs 기사에서 명백한 낭비가없는 기존의 잘 설계된 비 사소한 프로그램에서 시작하여 벽시계 시간이 48 초에서 단축 될 때까지 일련의 최적화를 통해 설명했습니다. 1.1 초로, 소스 코드 크기는 4 배 줄었습니다. 내 진단 도구 는 다음과 같습니다 . 변경 순서는 다음과 같습니다.

  • 발견 된 첫 번째 문제점은 목록 클러스터 (현재 "반복자"및 "컨테이너 클래스")를 절반 이상 사용하는 것입니다. 그것들은 상당히 간단한 코드로 대체되어 시간이 20 초로 단축되었습니다.

  • 이제 가장 큰 타임 테이커는 더 많은 목록 작성입니다. 백분율로 보면 이전에는 그리 크지 않았지만 이제는 더 큰 문제가 제거 되었기 때문입니다. 속도를 높이는 방법을 찾으면 시간이 17 초로 줄어 듭니다.

  • 이제 명백한 범인을 찾기가 더 어렵지만, 내가 할 수있는 몇 가지 작은 것들이 있으며 시간은 13 초로 떨어집니다.

이제 벽에 부딪힌 것 같습니다. 샘플은 정확히 무엇을하고 있는지 알려주지 만 개선 할 수있는 것을 찾을 수없는 것 같습니다. 그런 다음 프로그램의 기본 디자인, 트랜잭션 중심 구조에 대해 생각하고 수행중인 모든 목록 검색이 실제로 문제의 요구 사항에 의해 지시되는지 묻습니다.

그런 다음 프로그램 코드가 더 작은 소스 세트에서 실제로 (전 처리기 매크로를 통해) 생성되고 프로그램이 프로그래머가 알고있는 것을 지속적으로 파악하지 못하는 재 설계에 착수했습니다. 다시 말해, 일련의 작업을 "해석"하지 말고 "컴파일"하십시오.

  • 재 설계가 완료되고 소스 코드가 4 배 줄어들고 시간이 10 초로 줄어 듭니다.

이제는 점점 빨라지기 때문에 샘플링하기가 어렵 기 때문에 10 배나 많은 작업을 수행하지만 다음 시간은 원래 작업량을 기준으로합니다.

  • 더 많은 진단은 대기열 관리에 시간을 보내고 있음을 나타냅니다. 인라인으로 시간을 7 초로 줄입니다.

  • 이제 큰 시간을 보냈다는 것은 내가하고 있던 진단 인쇄입니다. 플러시-4 초.

  • 이제 가장 큰 타임 테이커는 malloc을 호출 하고 무료 입니다. 개체 재활용-2.6 초

  • 계속해서 샘플을 보더라도 여전히 1.1 초라는 꼭 필요한 작업은 없습니다.

총 속도 향상 요소 : 43.6

이제 두 프로그램이 비슷하지는 않지만 장난감이 아닌 소프트웨어에서는 항상 이와 같은 진행 상황을 보았습니다. 먼저 수익이 줄어들 기 전까지는 쉬운 일을하고 더 어려운 일을합니다. 그렇다면 당신이 얻는 통찰력은 다시 디자인이 줄어들어 다시 줄어드는 수익에 도달 할 때까지 새로운 속도 향상을 시작합니다. 이제이이 있는지 궁금해에 적합 할 수 있습니다되는 지점입니다 ++i또는 i++또는 for(;;)또는 while(1)입니다 빨리 : 나는 스택 오버플로에 너무 자주 볼 질문의 종류.

PS 왜 프로파일 러를 사용하지 않았는지 궁금 할 것입니다. 이에 대한 답은 이러한 "문제"중 거의 모든 것이 샘플을 정확하게 찾아주는 함수 호출 사이트라는 것입니다. 오늘날에도 프로파일 러는 전체 함수보다 명령문 및 호출 명령이 위치를 찾고 수정하기가 더 중요하다는 생각을 거의 듣지 않고 있습니다.

실제로이 작업을 수행하기 위해 프로파일 러를 구축했지만 코드가 수행하는 작업에 대한 불길하고 친밀한 친밀감을 얻으려면 손가락을 바로 넣을 수 없습니다. 발견되는 문제가 너무 작아서 쉽게 놓칠 수 없기 때문에 샘플 수가 적다는 것은 문제가되지 않습니다.

추가 : jerryjvl이 몇 가지 예를 요청했습니다. 첫 번째 문제는 다음과 같습니다. 시간이 절반 이상 걸리는 소수의 개별 코드 줄로 구성됩니다.

 /* IF ALL TASKS DONE, SEND ITC_ACKOP, AND DELETE OP */
if (ptop->current_task >= ILST_LENGTH(ptop->tasklist){
. . .
/* FOR EACH OPERATION REQUEST */
for ( ptop = ILST_FIRST(oplist); ptop != NULL; ptop = ILST_NEXT(oplist, ptop)){
. . .
/* GET CURRENT TASK */
ptask = ILST_NTH(ptop->tasklist, ptop->current_task)

이들은 목록 클러스터 ILST (목록 클래스와 유사)를 사용하고있었습니다. 그것들은 일반적인 방법으로 구현되며, "정보 숨기기"는 클래스 사용자가 구현 방식에 신경 쓸 필요가 없다는 것을 의미합니다. 이 줄이 작성되었을 때 (약 800 줄의 코드 중), 이것이 "병목 현상"(나는 그 단어를 싫어한다) 일 수 있다는 생각에 대해서는 생각하지 않았다. 그들은 단순히 일을하는 권장 방법입니다. 가급적이면 이러한 것들을 피해야한다고 말하기는 쉽지만 내 경험상 모든 성능 문제는 이와 같습니다. 일반적으로 성능 문제가 발생하지 않도록하는 것이 좋습니다. "피해야 했음"(후시)에도 생성 된 것을 찾고 수정하는 것이 더 좋습니다.

두 번째 문제는 두 줄로 나뉘어져 있습니다.

 /* ADD TASK TO TASK LIST */
ILST_APPEND(ptop->tasklist, ptask)
. . .
/* ADD TRANSACTION TO TRANSACTION QUEUE */
ILST_APPEND(trnque, ptrn)

이들은 끝에 항목을 추가하여 건물 목록입니다. (수정은 항목을 배열로 수집하고 한 번에 목록을 작성하는 것이 었습니다.) 흥미로운 점은 이러한 문장이 원래 시간의 3/48에 불과하기 때문에 비용이 들지 않았다는 것입니다. 사실 처음 에는 큰 문제 입니다. 그러나 첫 번째 문제를 제거한 후 3/20의 시간이 소요되어 이제는 "더 큰 물고기"가되었습니다. 일반적으로 그렇게됩니다.

이 프로젝트가 내가 도와 준 실제 프로젝트에서 증류되었다고 덧붙일 수 있습니다. 이 프로젝트에서 내부 루프 내에서 데이터베이스 액세스 루틴을 호출하여 작업이 완료되었는지 확인하는 등 성능 문제가 훨씬 더 급격했습니다.

참고 ADDED : 소스 코드, 모두 원본과 재 설계는에서 찾을 수 있습니다 www.ddj.com 파일 9311.zip 파일 slug.asc 및 slug.zip에서, 1993.

2011/11/26 편집 : 이제 Visual C ++에 소스 코드가 포함 된 SourceForge 프로젝트 와 조정 방법에 대한 자세한 설명이 있습니다. 그것은 위에서 설명한 시나리오의 전반부를 통과하며 정확히 동일한 순서를 따르지 않지만 여전히 2-3 배의 속도 향상을 얻습니다.


3
위에서 설명한 단계의 세부 사항 중 일부를 읽고 싶습니다. 풍미에 대한 최적화의 일부를 포함시킬 수 있습니까? (게시물을 너무 길게 만들지 않고?)
jerryjvl

8
... 나는 또한 이제 인쇄물이 나간 책을 썼기 때문에 아마존에서 우스운 가격을 책정 할 것입니다. "더 나은 애플리케이션 구축"ISBN 0442017405. 본질적으로 동일한 자료가 첫 번째 장에 있습니다.
Mike Dunlavey

3
@ Dunlavey와 마찬가지로 Google에서 이미 스캔했음을 알려주는 것이 좋습니다. 게시자를 구매 한 사람과 이미 계약을 맺었을 수 있습니다.
Thorbjørn Ravn Andersen

19
@ Thorbjørn : 후속 조치를 취하기 위해 GoogleBooks를 연결하고 모든 양식을 작성하여 하드 카피를 보냈습니다. 저작권을 실제로 소유하고 있는지 묻는 이메일을 받았습니다. Reuters가 구입 한 International Thompson이 구입 한 출판사 인 Van Nostrand Reinhold는 전화 나 이메일을 보내려고 할 때 블랙홀과 같습니다. 그래서 그것은 림보에 있습니다-아직 그것을 진정시킬 에너지가 없었습니다.
Mike Dunlavey


188

제안 :

  • 재 계산이 아닌 사전 계산 : 입력 범위가 상대적으로 제한된 계산이 포함 된 루프 또는 반복 호출의 경우 유효한 범위의 모든 값에 대한 계산 결과를 포함하는 조회 (배열 또는 사전)를 고려하십시오. 입력. 그런 다음 알고리즘 내부에서 간단한 조회를 사용하십시오.
    다운 측면 : 미리 계산 된 값의 몇 실제로이 문제를 악화시킬 수 있습니다 사용하는 경우, 또한 조회는 상당한 메모리를 취할 수있다.
  • 라이브러리 메소드를 사용하지 마십시오 . 대부분의 라이브러리는 광범위한 시나리오에서 올바르게 작동하고 매개 변수 등에서 널 점검을 수행해야합니다. 메소드를 다시 구현하면 많은 로직을 제거 할 수 있습니다. 사용하는 정확한 환경에는 적용되지 않습니다.
    단점 : 추가 코드를 작성하면 버그가 더 많이 나타납니다.
  • 도서관 방법을 사용하십시오 : 자신과 모순되기 때문에 언어 도서관은 당신이나 나보다 훨씬 똑똑한 사람들에 의해 작성됩니다. 그들이 더 빠르고 더 잘했을 것입니다. 실제로 더 빨리 만들 수 없다면 직접 구현하지 마십시오 (예 : 항상 측정!).
  • 치트 : 어떤 경우에는 문제에 대한 정확한 계산이 가능하지만 '정확한'필요가 없을 수도 있습니다. 때로는 근사치가 '충분히 좋으며'거래가 훨씬 빠를 수도 있습니다. 스스로 답하십시오. 답이 1 % 나쁘면 정말 중요합니까? 5 %? 심지어 10 %?
    단점 : 음 ... 정답은 정확하지 않습니다.

32
사전 계산이 항상 도움이되는 것은 아니며 때로는 손상 될 수도 있습니다. 조회 테이블이 너무 크면 캐시 성능이 저하 될 수 있습니다.
Adam Rosenfield

37
부정 행위가 종종 이길 수 있습니다. 나는 핵심에서 3x3 행렬로 점이 찍힌 3 개의 벡터 인 색 보정 프로세스를 가졌습니다. CPU에는 하드웨어에 매트릭스가 곱 해져서 크로스 섹션 중 일부를 생략하고 다른 모든 방법과 비교할 때 실제로 빠르지 만 4x4 매트릭스와 4 벡터 플로트 만 지원했습니다. 여분의 빈 슬롯을 운반하도록 코드를 변경하고 계산을 고정 소수점에서 부동 소수점으로 변환하면 약간 덜 정확하지만 훨씬 빠른 결과를 얻을 수 있습니다.
RBerteig

6
부정 행위는 내부 곱의 일부를 생략 한 행렬 곱셈을 사용하여 단일 CPU 명령에 대해 마이크로 코드로 구현하여 단일 명령의 개별 시퀀스보다 훨씬 빠르게 완료 할 수있었습니다. "올바른"답변을 얻지 못하기 때문에 "충분히 정확한"답변입니다.
RBerteig

6
@RBerteig : "충분히 정확한"것은 대부분의 사람들이 내 경험에서 놓칠 수있는 최적화의 기회입니다.
Martin Thompson

5
모든 사람이 당신보다 똑똑하다고 항상 생각할 수는 없습니다. 결국 우리는 모두 전문가입니다. 그러나 사용하는 특정 라이브러리가 존재하고 품질로 인해 환경에 도달했다고 가정 할 수 있으므로이 라이브러리의 작성은 매우 철저해야합니다. 전문가가 아니기 때문에 그렇게 할 수는 없습니다. 필드에 같은 종류의 시간을 투자하지 마십시오. 똑똑하지 않기 때문이 아닙니다. 어서
v.oddou

164

더 이상 성능을 향상시킬 수없는 경우 대신 인식 된 성능을 향상시킬 수 있는지 확인하십시오 .

fooCalc 알고리즘을 더 빨리 만들지 못할 수도 있지만 종종 응용 프로그램이 사용자에게보다 반응 적으로 보이도록하는 방법이 있습니다.

몇 가지 예 :

  • 사용자가 무엇을 요청할지 예상하고 그 전에 작업을 시작합니다.
  • 마지막에 한 번에 모든 결과가 표시되는 대신 결과 표시
  • 정확한 진행 측정기

이렇게하면 프로그램이 더 빨라지지는 않지만 속도가 빨라지면 사용자가 더 행복해질 수 있습니다.


27
끝에서 속도가 빨라지는 진행률 표시 줄은 절대적으로 정확한 표시 줄보다 빠르게 인식 될 수 있습니다. "진행률 표시 줄 다시 생각하기"(2007)에서 Harrison, Amento, Kuznetsov 및 Bell은 사용자 그룹에서 여러 유형의 막대를 테스트하고 진행률을 더 빨리 인식 할 수 있도록 작업을 재정렬하는 몇 가지 방법을 논의합니다.
Emil Vikström 2016 년

9
naxa, 대부분의 진행률 표시 줄은 가짜입니다. 하나의 비율로 여러 단계의 흐름을 예측하는 것이 어렵거나 때로는 불가능하기 때문입니다. :-( 99 %에 걸리면 모든 바에서 그냥보기
에밀 Vikström

138

나는 대부분의 인생을이 곳에서 보냅니다. 광범위한 스트로크는 프로파일 러를 실행하고 기록하도록합니다.

  • 캐시가 누락되었습니다 . 데이터 캐시는 대부분의 프로그램에서 중단의 # 1 소스입니다. 더 나은 지역성을 갖도록 문제가되는 데이터 구조를 재구성하여 캐시 적중률을 향상시킵니다. 낭비되는 바이트 (따라서 캐시 페치 낭비)를 제거하기 위해 구조 및 숫자 유형을 압축합니다. 스톨을 줄이려면 가능한 한 데이터를 프리 페치하십시오.
  • 적중 상점 . 포인터 앨리어싱에 대한 컴파일러 가정 및 메모리를 통해 연결이 끊어진 레지스터 세트간에 데이터가 이동하는 경우 특정 병리학 적 동작으로 인해 전체 CPU 파이프 라인이로드 op에서 지워질 수 있습니다. 부동 소수점, 벡터 및 정수가 서로 캐스팅되는 장소를 찾아서 제거하십시오. __restrict앨리어싱에 대해 컴파일러에게 약속 하려면 자유로이 사용하십시오 .
  • 마이크로 코드 된 작업 . 대부분의 프로세서에는 파이프 라인 할 수없는 일부 작업이 있지만 대신 ROM에 저장된 작은 서브 루틴을 실행하십시오. PowerPC의 예는 정수 곱하기, 나누기 및 변수 별 시프트입니다. 문제는이 작업이 실행되는 동안 전체 파이프 라인이 중지되는 것입니다. 이러한 작업의 사용을 없애거나 최소한 파이프 라인 ops로 분류하여 나머지 프로그램이 수행하는 모든 작업에서 수퍼 스칼라 디스패치의 이점을 얻을 수 있습니다.
  • 지점이 잘못 예측 합니다. 파이프 라인이 비어 있습니다. CPU가 분기 후 파이프를 다시 채우는 데 많은 시간을 소비하는 사례를 찾고 가능한 경우 분기 힌트를 사용하여 더 자주 올바르게 예측하십시오. 또는 파이프가 일반적으로 더 깊고 fcmp 후에 조건 플래그를 읽으면 중단이 발생할 수 있으므로 특히 부동 소수점 연산 후에 분기를 조건부 이동으로 대체하십시오 .
  • 순차적 부동 소수점 연산 . 이 SIMD를 만드십시오.

그리고 내가하고 싶은 한 가지 더 :

  • 컴파일러에서 어셈블리 목록을 출력하도록 설정 하고 코드에서 핫스팟 함수에 대해 방출되는 것을 살펴보십시오. "좋은 컴파일러가 자동으로 당신을 위해 할 수 있어야한다"는 모든 영리한 최적화? 실제 컴파일러가 그렇게하지 않을 가능성이 있습니다. GCC가 실제로 WTF 코드를 생성하는 것을 보았습니다.

8
나는 주로 Intel VTune과 PIX를 사용합니다. C #에 적응할 수 있을지 모르겠지만 실제로는 JIT 추상화 계층을 얻은 후에는 캐시 위치를 개선하고 일부 분기를 피하는 것을 제외하고는 이러한 최적화의 대부분이 도달 할 수 없습니다.
Crashworks

6
그럼에도 불구하고 포스트 JIT 출력을 확인하면 JIT 단계를 통해 잘 최적화되지 않은 구문이 있는지 파악하는 데 도움이 될 수 있습니다. 막 다른 길로 밝혀 지더라도 조사는 결코 해칠 수 없습니다.
jerryjvl

5
나 자신을 포함한 많은 사람들이 gcc가 생산 한이 "wtf assembly"에 관심이 있다고 생각한다. 당신은 매우 흥미로운 직업처럼 들립니다 :)
BlueRaja-대니 Pflughoeft

1
Examples on the PowerPC ...<-즉, PowerPC의 일부 구현입니다. PowerPC는 CPU가 아닌 ISA입니다.
Billy ONeal

1
@BillyONeal 최신 x86 하드웨어에서도 imul은 파이프 라인을 중단시킬 수 있습니다. "Intel® 64 및 IA-32 아키텍처 최적화 참조 매뉴얼"을 참조하십시오. §13.3.2.3 : "정수 곱하기 명령어는 실행하는 데 몇 사이클이 걸립니다. 정수 곱하기 명령어 및 다른 긴 대기 시간 명령어가 파이프 라인으로 진행되어 그러나 정수 곱하기 명령어는 프로그램 순서의 요구로 인해 다른 단일 사이클 정수 명령어의 발행을 차단합니다. " 그렇기 때문에 일반적으로 단어 정렬 배열 크기 및을 사용하는 것이 좋습니다 lea.
Crashworks

78

더 많은 하드웨어를 던져라!


30
이미 현장에있는 하드웨어에서 실행될 것으로 예상되는 소프트웨어가있는 경우 더 많은 하드웨어가 항상 옵션이 아닙니다.
Doug T.

76
소비자 소프트웨어를 만드는 사람에게는 그다지 도움이되지 않습니다. 고객은 "더 빠른 컴퓨터를 구입하십시오"라는 말을 듣고 싶지 않을 것입니다. 특히 비디오 게임 콘솔과 같은 것을 대상으로 소프트웨어를 작성하는 경우.
Crashworks

19
@Crashworks 또는 그 문제를 위해 임베디드 시스템. 마지막 기능이 마지막에 있고 보드의 첫 번째 배치가 이미 회전 한 경우 처음에 더 빠른 CPU를 사용해야한다는 사실을 발견 할 때가 아닙니다 ...
RBerteig

71
한때 메모리 누수가 큰 프로그램을 디버깅해야했습니다. VM 크기는 시간당 약 1Mb 증가했습니다. 동료는 내가해야 할 일은 일정한 속도로 메모리 추가하는 것이라고 농담했다 . :)
j_random_hacker

9
더 많은 하드웨어 : 아, 평범한 개발자의 수명 선. "다른 기계를 추가하고 용량을 두 배로 늘렸다"는 말을 몇 번이나했는지 모르겠습니다.
Olof Forshell

58

더 많은 제안 :

  • I / O 피하기 : 모든 I / O (디스크, 네트워크, 포트 등)는 항상 계산을 수행하는 코드보다 훨씬 느리므로 반드시 필요하지 않은 I / O를 제거하십시오.

  • I / O를 사전에 이동 : 계산에 필요한 모든 데이터를 사전에로드하여 중요한 알고리즘의 핵심 내에서 I / O 대기를 반복하지 않도록 (결과가 반복 될 수 있음) 디스크 검색 (한 번의 히트로 모든 데이터를로드 할 때 검색을 피할 수 있음).

  • I / O 지연 : 계산이 끝날 때까지 결과를 쓰지 말고 데이터 구조에 저장 한 다음 열심히 일할 때 한 번에 그 결과를 덤프하십시오.

  • 스레드 I / O : 대담한 사람들을 위해,로드를 병렬 스레드로 이동하여 'I / O up-front'또는 'Delay I / O'를 실제 계산과 결합하여 더 많은 데이터를로드하는 동안 작업 할 수 있습니다 이미 보유한 데이터를 계산하거나 다음 데이터 배치를 계산하는 동안 마지막 배치의 결과를 동시에 쓸 수 있습니다.


3
"IO를 병렬 스레드로 이동"은 많은 플랫폼 (예 : Windows NT)에서 비동기 IO로 수행되어야합니다.
Billy ONeal

2
I / O는 느리고 지연 시간이 길기 때문에 실제로 중요한 포인트입니다.이 조언으로 더 빨리 얻을 수 있지만 여전히 근본적인 결함이 있습니다. 포인트는 대기 시간 (숨겨져 있어야 함)과 시스템 콜 오버 헤드 ( I / O 호출 를 줄여서 줄여야 합니다). 가장 좋은 조언은 mmap()입력에 사용 하고, 적절한 madvise()호출을 수행하고 , aio_write()큰 덩어리의 출력을 작성 하는 데 사용 하는 것입니다 (= MiB 몇 개).
cmaster-monica reinstate

1
이 마지막 옵션은 특히, 자바로 구현하기가 매우 쉽다. 필자가 작성한 응용 프로그램의 성능이 크게 향상되었습니다. 또 다른 중요한 점은 (I / O를 앞쪽으로 이동시키는 것보다) 시퀀스 및 대형 블록 I / O로 만드는 것입니다. 디스크 검색 시간으로 인해 많은 작은 읽기가 1 개의 큰 읽기보다 훨씬 비쌉니다.
BobMcGee

어느 시점에서 나는 계산하기 전에 모든 파일을 RAM 디스크로 일시적으로 이동하고 나중에 다시 이동하여 I / O를 피하는 것을 부정했습니다. 이것은 더럽지 만 I / O 호출을하는 로직을 제어하지 않는 상황에서 유용 할 수 있습니다.
MD

48

많은 성능 문제는 데이터베이스 문제와 관련되므로 쿼리 및 저장 프로 시저를 조정할 때 살펴볼 특정 사항을 알려 드리겠습니다.

대부분의 데이터베이스에서 커서를 사용하지 마십시오. 루핑도 피하십시오. 대부분의 경우 데이터 액세스는 레코드 처리에 의한 레코드가 아니라 설정 기반이어야합니다. 여기에는 1,000,000 개의 레코드를 한 번에 삽입하려는 경우 단일 레코드 저장 프로 시저를 재사용하지 않는 것이 포함됩니다.

select *를 사용하지 말고 실제로 필요한 필드 만 반환하십시오. 조인 필드가 반복되어 서버와 네트워크 모두에 불필요한로드가 발생하기 때문에 조인이있는 경우 특히 그렇습니다.

상관 된 하위 쿼리를 사용하지 마십시오. 조인 (가능한 경우 파생 테이블에 대한 조인 포함)을 사용하십시오 (Microsoft SQL Server에서는 이것이 사실이지만 다른 백엔드를 사용할 때 조언을 테스트하십시오).

인덱스, 인덱스, 인덱스 데이터베이스에 해당되는 경우 해당 통계를 업데이트하십시오.

쿼리를 sargable로 만듭니다 . 의미는 like 절의 첫 문자에 와일드 카드를 사용하거나 조인의 함수 또는 where 문의 왼쪽 부분에 색인을 사용할 수없는 것을 피하십시오.

올바른 데이터 유형을 사용하십시오. 문자열 데이터 유형을 날짜 데이터 유형으로 변환 한 다음 계산을 수행하는 것보다 날짜 필드에서 날짜 계산을 수행하는 것이 더 빠릅니다.

어떤 종류의 루프도 트리거에 넣지 마십시오!

대부분의 데이터베이스에는 쿼리 실행 방법을 확인할 수있는 방법이 있습니다. Microsoft SQL Server에서는이를 실행 계획이라고합니다. 문제 영역이 어디에 있는지 먼저 확인하십시오.

최적화해야 할 사항을 결정할 때 쿼리 실행 빈도와 실행 시간을 고려하십시오. 때때로 한 달에 한 번만 실행되는 long_running 쿼리에서 시간을 지우는 것보다 약간의 조정에서 하루에 수백만 번 실행되는 쿼리까지 더 많은 성능을 얻을 수 있습니다.

어떤 종류의 프로파일 러 도구를 사용하여 데이터베이스에서 실제로 전송되는 내용을 찾으십시오. 과거에 저장 프로 시저가 빠를 때 웹 페이지가 한 번이 아니라 여러 번 쿼리를 요청하는 프로파일 링을 통해 페이지가로드되는 속도가 느린 이유를 알 수 없었던 적이 있습니다.

프로파일 러는 누가 누가 차단하고 있는지 찾는 데 도움을줍니다. 혼자 실행하는 동안 빠르게 실행할 일부 쿼리로 인해 다른 쿼리의 잠금에 정말 느린 될 수 있습니다.


29

오늘날 가장 중요한 제한 요소는 제한된 메모리 대역폭 입니다. 대역폭이 코어간에 공유되므로 멀티 코어는이를 더욱 악화시키고 있습니다. 또한 캐시 구현에 전념하는 제한된 칩 영역도 코어와 스레드로 나누어 져이 문제가 더욱 악화됩니다. 마지막으로, 서로 다른 캐시를 일관성있게 유지하는 데 필요한 칩 간 신호도 코어 수가 증가함에 따라 증가합니다. 이것은 또한 패널티를 추가합니다.

이것들은 관리해야 할 효과입니다. 때로는 코드를 미세하게 관리하지만 신중한 고려와 리팩토링을 통해서도 있습니다.

많은 의견에서 이미 캐시 친화적 인 코드를 언급하고 있습니다. 이것에는 적어도 두 가지 독특한 풍미가 있습니다.

  • 메모리 반입 대기 시간을 피하십시오.
  • 낮은 메모리 버스 압력 (대역폭).

첫 번째 문제는 특히 데이터 액세스 패턴을보다 규칙적으로 만들어 하드웨어 프리 페 처가 효율적으로 작동하는 것과 관련이 있습니다. 데이터 객체를 메모리에 분산시키는 동적 메모리 할당을 피하십시오. 연결된 목록, 해시 및 트리 대신 선형 컨테이너를 사용하십시오.

두 번째 문제는 데이터 재사용 개선과 관련이 있습니다. 사용 가능한 캐시에 맞는 데이터의 하위 집합에서 작동하도록 알고리즘을 변경하고 여전히 캐시에있는 동안 해당 데이터를 최대한 재사용하십시오.

데이터를보다 엄격하게 패킹하고 핫 루프에서 캐시 라인의 모든 데이터를 사용하면 이러한 다른 영향을 피하고 캐시에 보다 유용한 데이터를 맞출 수 있습니다.


25
  • 어떤 하드웨어를 사용하고 있습니까? 당신은 플랫폼 별 최적화 (벡터화와 같은)를 사용할 수 있습니까?
  • 더 나은 컴파일러를 얻을 수 있습니까? 예를 들어 GCC에서 인텔로 전환 하시겠습니까?
  • 알고리즘을 병렬로 실행할 수 있습니까?
  • 데이터를 재구성하여 캐시 누락을 줄일 수 있습니까?
  • 어설 션을 비활성화 할 수 있습니까?
  • 컴파일러와 플랫폼에 맞게 마이크로 최적화하십시오. "if / else에서 가장 일반적인 문장을 먼저 배치"의 스타일

4
"GCC에서 LLVM으로 전환"
이어야합니다.

4
알고리즘을 병렬로 실행할 수 있습니까? -그 반대도 적용됨
저스틴

4
스레드의 양을 줄이는 것도 마찬가지로 좋은 최적화가 될 수 있습니다
Johan Kotlinski

다시 : 마이크로 최적화 : 컴파일러의 asm 출력을 확인하면 소스를 조정하여 더 나은 asm을 생성하도록 할 수 있습니다. Collatz 추측 테스트를 위해이 C ++ 코드가 직접 작성한 어셈블리보다 빠른 이유는 무엇입니까?를 참조하십시오 . 도움 또는 현대 x86에서 컴파일러를 치기에 대한 자세한합니다.
피터 코르

17

Mike Dunlavey의 답변이 마음에 들지만 실제로는 지원 사례에 대한 훌륭한 답변이지만 실제로 매우 간단하게 표현할 수 있다고 생각합니다.

처음의 가장 큰 금액을 필요한 것을 알아보고 이유를 이해.

알고리즘을 세분화해야하는 위치를 이해하는 데 도움이되는 것은 시간 호그의 식별 프로세스입니다. 이것은 이미 완전히 최적화 된 문제에 대해 찾을 수있는 유일한 포괄적 인 언어 불가지론 적 답변입니다. 또한 속도를 추구하는 데 독립적 인 아키텍처를 원한다고 가정합니다.

알고리즘을 최적화 할 수있다 그래서 동안, 그것의 구현되지 않을 수 있습니다. 식별을 통해 알고리즘 또는 구현 중 어느 부분을 알 수 있습니다. 따라서 시간이 가장 많이 걸리는 사람이 검토 대상입니다. 그러나 마지막 몇 %를 짜내고 싶다고 말하면 처음에는 자세히 살펴 보지 않은 작은 부분도 검사해야 할 수 있습니다.

마지막으로 동일한 솔루션 또는 잠재적으로 다른 알고리즘을 구현하는 여러 가지 방법으로 성능 수치에 대한 약간의 시행 착오를 통해 시간 낭비와 시간 절약을 식별하는 데 도움이되는 통찰력을 얻을 수 있습니다.

HPH, asoudmove.


16

"Google Perspective"를 고려해야합니다. 즉, 애플리케이션이 크게 병렬화되고 동시화 될 수있는 방법을 결정해야합니다. 이는 어느 시점에서 애플리케이션을 다른 머신과 네트워크에 분산 시켜서 거의 선형으로 확장 할 수 있도록하는 것을 의미합니다. 던지는 하드웨어로

반면에 Google 직원은 전담 엔지니어 팀을 통해 gcc대한 전체 프로그램 최적화 와 같이 사용중인 프로젝트, 도구 및 인프라의 일부 문제를 해결하는 데 많은 인력과 리소스를 투입하는 것으로도 유명합니다. Google의 일반적인 사용 사례 시나리오에 대비하기 위해 gcc 내부를 해킹합니다.

마찬가지로 응용 프로그램 프로파일 링은 더 이상 단순히 시스템 코드 관점에서 중복성과 최적화 가능성을 식별하기 위해 프로그램 코드뿐만 아니라 모든 주변 시스템 및 인프라 (네트워크, 스위치, 서버, RAID 어레이를 고려)를 프로파일 링하는 것을 의미합니다.


15
  • 인라인 루틴 (호출 / 반환 및 매개 변수 푸시 제거)
  • 테이블 조회를 통해 테스트 / 스위치를 제거하십시오 (더 빠른 경우)
  • CPU 캐시에 딱 맞는 지점까지 루프 (더프 장치) 풀기
  • 캐시를 날리지 않도록 메모리 액세스를 현지화하십시오
  • 옵티마이 저가 아직 수행하지 않은 경우 관련 계산을 현지화
  • 옵티마이 저가 아직 수행하지 않은 경우 루프 불변량 제거

2
IIRC Duff의 장치는 매우 빠릅니다. 작고 작은 수학 표현처럼 op가 매우 짧은 경우에만
BCS

12
  • 효율적인 알고리즘을 사용하고 있다는 점에 도달하면 더 많은 속도 나 메모리 가 필요한 문제가 있습니다 . 캐싱을 사용하여 메모리에서 "지불"하여 속도를 높이거나 계산을 사용하여 메모리 공간을 줄입니다.
  • 수 (및 비용 효과적인) 경우 문제에 던져 하드웨어 - 빠른 CPU, 메모리 또는 HD 코드로 시도 후 빠르게 문제를 해결할 수 있습니다.
  • 가능하면 병렬화를 사용하십시오 -코드의 일부를 여러 스레드에서 실행 하십시오 .
  • 작업에 적합한 도구를 사용하십시오 . 일부 프로그래밍 언어는 관리 코드 (예 : Java / .NET)를 사용하여 개발 속도를 높이지만 네이티브 프로그래밍 언어는 실행 코드가 더 빠릅니다.
  • 마이크로 최적화 . 최적화 된 어셈블리를 사용하여 작은 코드 조각을 빠르게 처리 할 수 ​​있으며, 적절한 장소에서 SSE / 벡터 최적화를 사용하면 성능을 크게 향상시킬 수 있습니다.

12

나누고 정복

처리중인 데이터 세트가 너무 큰 경우 청크를 반복하십시오. 코드를 올바르게 작성했다면 구현이 쉬워야합니다. 모 놀리 식 프로그램이 있다면 이제 더 잘 알 것입니다.


9
마지막 문장을 읽는 동안 들었던 flyswatter "smack"소리에 +1.
Bryan Boettcher

11

우선, 몇 가지 이전 답변에서 언급했듯이 메모리, 프로세서, 네트워크 또는 데이터베이스 또는 다른 것이 성능에 어떤 영향을 미치는지 알아보십시오. 그것에 따라 ...

  • ... 메모리라면- "컴퓨터 프로그래밍의 예술"시리즈 중 하나 인 Knuth가 오래 전에 저술 한 책 중 하나를 찾으십시오. 정렬 및 검색에 관한 것일 가능성이 큽니다. 내 메모리가 잘못된 경우 느린 테이프 데이터 저장을 처리하는 방법에 대해 이야기해야합니다. 그의 메모리 / 테이프 쌍을 각각 캐시 / 메인 메모리 쌍 (또는 L1 / L2 캐시 쌍)으로 각각 변환하십시오. 그가 설명하는 모든 요령을 연구하십시오-문제를 해결할 수있는 것을 찾으면 전문 컴퓨터 과학자를 고용하여 전문 연구를 수행하십시오. 메모리 문제가 FFT에 의해 우연히 발생하는 경우 (기수 -2 나비를 수행 할 때 비트 반전 인덱스에서 캐시 누락) 과학자를 고용하지 말고 대신 수동으로 패스를 하나씩 최적화하십시오. 다시이기거나 막 다른 길로 가십시오. 당신은 언급마지막 몇 퍼센트까지 짜내 ? 그것이 실제로 소수 라면 당신이 이길 것입니다.

  • ... 프로세서 인 경우-어셈블리 언어로 전환하십시오. 프로세서 사양 연구- 진드기 , VLIW, SIMD가 필요한 것. 함수 호출은 대체 가능한 틱 먹는 사람입니다. 루프 변환 배우기-파이프 라인, 언롤 곱셈과 나눗셈은 비트 시프트로 대체 가능 / 보간 될 수 있습니다 (작은 정수로 곱하면 덧셈으로 대체 가능). 더 짧은 데이터로 트릭을 시도하십시오-운이 좋으면 64 비트의 명령 하나가 32 비트의 2 개 또는 16 비트의 4 개 또는 8 비트의 8 비트로 대체 될 수 있습니다. 더 길게 시도데이터-예를 들어 플로트 계산은 특정 프로세서에서 두 배보다 느릴 수 있습니다. 삼각법이있는 경우 미리 계산 된 테이블과 싸워야합니다. 또한 정밀도 손실이 허용 한계 내에 있으면 작은 값의 사인이 해당 값으로 대체 될 수 있습니다.

  • ... 네트워크 인 경우 데이터를 압축하는 것이 좋습니다. XML 전송을 이진으로 바꿉니다. 연구 프로토콜. 어떻게 든 데이터 손실을 처리 할 수 ​​있으면 TCP 대신 UDP를 사용해보십시오.

  • ... 데이터베이스라면 데이터베이스 포럼으로 가서 조언을 구하십시오. 인 메모리 데이터 그리드, 쿼리 계획 최적화 등

HTH :)


9

캐싱! 거의 모든 것을 더 빠르게 만드는 저렴한 방법 (프로그래머 노력)은 프로그램의 데이터 이동 영역에 캐싱 추상화 계층을 추가하는 것입니다. I / O이거나 객체 또는 구조의 전달 / 만들기입니다. 팩토리 클래스와 리더 / 라이터에 캐시를 쉽게 추가 할 수 있습니다.

때때로 캐시는 많은 것을 얻지 못하지만 캐싱을 추가하고 도움이되지 않는 곳에서 캐시를 비활성화하는 쉬운 방법입니다. 필자는 종종 코드를 미세 분석하지 않고도 엄청난 성능을 얻을 수 있다는 것을 알았습니다.


8

나는 이것이 이미 다른 방식으로 언급되었다고 생각합니다. 그러나 프로세서 집약적 알고리즘을 다룰 때는 다른 모든 것을 희생하면서 가장 내부 루프 내부의 모든 것을 단순화해야합니다.

일부 사람들에게는 분명해 보일지 모르지만, 내가 사용하는 언어에 관계없이 집중하려고하는 것입니다. 예를 들어 중첩 루프를 처리하고 일부 코드를 한 수준 아래로 내릴 수있는 기회를 찾는 경우 코드 속도를 크게 향상시킬 수 있습니다. 또 다른 예로서, 가능할 때마다 부동 소수점 변수 대신 정수로 작업하고 가능할 때마다 나누기 대신 곱셈을 사용하는 것과 같은 작은 생각이 있습니다. 다시, 이것들은 가장 내부 루프에 대해 고려해야 할 것들입니다.

내부 루프 내부의 정수에 대해 수학 연산을 수행 한 다음 나중에 작업 할 수있는 부동 소수점 변수로 축소하는 이점이 있습니다. 그것은 한 섹션에서 다른 섹션의 속도를 향상시키기 위해 속도를 희생하는 예이지만, 어떤 경우에는 그만한 가치가 있습니다.


8

저 대역폭 및 긴 대기 시간 네트워크 (예 : 위성, 원격, 해외)를 통해 작동하는 클라이언트 / 서버 비즈니스 시스템을 최적화하는 데 시간을 보냈으며 상당히 반복 가능한 프로세스로 성능을 크게 향상시킬 수있었습니다.

  • 측정 : 네트워크의 기본 용량 및 토폴로지를 이해하여 시작합니다. 비즈니스에서 관련 네트워킹 담당자와 대화하고 핑 및 추적 경로와 같은 기본 도구를 사용하여 일반적인 운영 기간 동안 각 클라이언트 위치에서 네트워크 대기 시간을 설정합니다 (최소한). 다음으로, 문제가있는 증상을 나타내는 특정 최종 사용자 기능을 정확하게 시간 측정하십시오. 위치, 날짜 및 시간과 함께이 모든 측정 값을 기록하십시오. 최종 사용자 "네트워크 성능 테스트"기능을 클라이언트 응용 프로그램에 구축하여 고급 사용자가 개선 프로세스에 참여할 수 있도록하십시오. 이처럼 권한을 부여하는 것은있을 수 거대한 당신이 성능이 낮은 시스템에 의해 좌절 사용자를 처리 할 때 심리적 인 영향을.

  • 분석 : 사용 가능한 모든 로깅 방법을 사용하여 영향을받는 작업을 실행하는 동안 전송 및 수신되는 데이터를 정확하게 설정합니다. 이상적으로 응용 프로그램은 클라이언트와 서버 모두에서 송수신하는 데이터를 캡처 할 수 있습니다. 여기에 타임 스탬프도 포함되어 있으면 더 좋습니다. 충분한 로깅을 사용할 수없는 경우 (예 : 닫힌 시스템 또는 프로덕션 환경에 수정 사항을 배포 할 수없는 경우) 네트워크 스니퍼를 사용하여 네트워크 수준에서 진행중인 작업을 실제로 이해해야합니다.

  • 캐시 : 정적 또는 드물게 변경되는 데이터가 반복적으로 전송되는 경우를 찾고 적절한 캐싱 전략을 고려하십시오. 일반적인 예에는 "선택 목록"값 또는 기타 "참조 엔터티"가 포함되며 일부 비즈니스 응용 프로그램에서는 놀라 울 정도로 클 수 있습니다. 많은 경우에, 일반적으로 자주 사용되는 사용자 인터페이스 요소의 표시에서 상당한 시간을 줄일 수있는 경우, 사용자는 자주 업데이트되지 않는 데이터를 업데이트하기 위해 응용 프로그램을 다시 시작하거나 새로 고쳐야한다는 사실을 인정할 수 있습니다. 이미 배포 된 캐싱 요소의 실제 동작을 이해해야합니다. 많은 일반적인 캐싱 방법 (예 : HTTP ETag)에는 일관성을 유지하기 위해 여전히 네트워크 왕복이 필요하며, 네트워크 대기 시간이 비싼 경우이를 피할 수 있습니다. 다른 캐싱 접근법.

  • 병렬화 : 논리적으로 엄격하게 순차적으로 발행 될 필요가없는 순차적 트랜잭션을 찾고 시스템을 재 작업하여 병렬로 발행하십시오. 엔드 투 엔드 요청에 ~ 2 초의 고유 한 네트워크 지연이 있었지만 단일 트랜잭션에는 문제가 없었지만 사용자가 클라이언트 응용 프로그램에 대한 제어권을 다시 얻기 전에 6 번의 2 회 왕복 여행이 필요한 경우를 처리했습니다. 그것은 엄청난 좌절의 원인이되었습니다. 이러한 트랜잭션이 실제로 독립적이라는 사실을 발견하면 트랜잭션을 병렬로 실행할 수있어 최종 사용자 지연이 단일 왕복 비용에 매우 가깝습니다.

  • 결합 : 순차적 요청을 순차적 으로 실행 해야하는 경우이를보다 포괄적 인 단일 요청으로 결합 할 수있는 기회를 찾으십시오. 일반적인 예로는 새 엔터티 만들기와 해당 엔터티를 다른 기존 엔터티와 관련시키는 요청이 있습니다.

  • 압축 : 텍스트 형식을 이진 형식으로 바꾸거나 실제 압축 기술을 사용하여 페이로드 압축을 활용할 수있는 기회를 찾으십시오. 많은 최신 (즉, 10 년 이내) 기술 스택이이를 거의 투명하게 지원하므로 구성해야합니다. 압축의 영향으로 대역폭이 아닌 근본적으로 대기 시간이 문제인 것으로 보 였는데, 트랜잭션이 단일 패킷 내에 들어가거나 패킷 손실을 피할 수 있고 그에 따라 크기가 크지 않다는 사실을 알게 된 경우가 종종 있습니다. 성능에 영향을 미칩니다.

  • 반복 : 처음으로 돌아가서 개선 된 부분으로 작업을 동일한 위치와 시간에 다시 측정하고 결과를 기록하고보고하십시오. 모든 최적화와 마찬가지로 일부 문제는 현재 지배적 인 다른 문제를 노출하여 해결되었을 수 있습니다.

위의 단계에서는 애플리케이션 관련 최적화 프로세스에 중점을 두지 만 물론 기본 네트워크 자체가 애플리케이션을 지원할 수있는 가장 효율적인 방식으로 구성되어 있는지 확인해야합니다. 비즈니스에서 네트워킹 전문가를 참여시키고 용량 개선, QoS, 네트워크 압축 또는 기타 기술을 적용하여 문제를 해결할 수 있는지 확인하십시오. 일반적으로 응용 프로그램의 요구 사항을 이해하지 못하므로 분석 단계 후 응용 프로그램과 논의하고 응용 프로그램에서 발생하는 비용에 대해 비즈니스 사례를 작성하는 것이 중요합니다. . 잘못된 네트워크 구성으로 인해 애플리케이션 데이터가 오버랜드 링크가 아닌 저속 위성 링크를 통해 전송되는 경우가 발생했습니다. 네트워킹 전문가가 "잘 알려지지 않은"TCP 포트를 사용했기 때문입니다. 이와 같은 문제를 수정하면 소프트웨어 코드 나 구성 변경이 전혀 필요없이 성능에 큰 영향을 줄 수 있습니다.


7

이 질문에 대한 일반적인 대답을하기가 매우 어렵습니다. 실제로 문제 영역과 기술 구현에 따라 다릅니다. 상당히 언어 중립적 인 일반적인 기술 : 제거 할 수없는 코드 핫스팟을 식별하고 어셈블러 코드를 수동으로 최적화하십시오.


7

마지막 몇 %는 CPU와 응용 프로그램에 따라 다릅니다 ....

  • 캐시 아키텍처가 다르면 일부 칩에는 직접 매핑 할 수있는 온칩 RAM이 있고 ARM (때로는)에는 벡터 단위가 있으며 SH4는 유용한 매트릭스 연산 코드입니다. GPU 가 있습니까 -아마도 쉐이더가 갈 길입니다. TMS320 은 루프 내의 분기에 매우 민감합니다 (따라서 루프를 분리하고 가능한 경우 외부로 이동).

목록은 계속됩니다 .... 그러나 이런 종류의 것들이 실제로 최후의 수단입니다 ...

x86을 빌드하고 적절한 성능 프로파일 링을 위해 코드에 대해 Valgrind / Cachegrind를 실행 하십시오. 또는 Texas Instruments의 CCStudio 에는 달콤한 프로파일 러가 있습니다. 그러면 당신은 어디에 집중해야하는지 알게 될 것입니다 ...


7

Did you know that a CAT6 cable is capable of 10x better shielding off extrenal inteferences than a default Cat5e UTP cable?

오프라인이 아닌 프로젝트의 경우 최고의 소프트웨어와 최고의 하드웨어를 보유하고 있지만 출력이 약한 경우 해당 얇은 선이 데이터를 쥐고 지연 시간을 밀리 초 단위로 제공하지만 마지막 하락에 대해 이야기하는 경우 , 이는 어떤 팩 지에 대해 24/7 증가 / 감소 된 것입니다.


7

이전 답변만큼 심층적이거나 복잡하지는 않지만 다음과 같습니다. (초급 / 중급 수준이 높음)

  • 명백한 : 건조
  • 루프를 거꾸로 실행하면 변수가 아닌 항상 0과 비교됩니다.
  • 가능하면 비트 연산자를 사용하십시오
  • 반복적 인 코드를 모듈 / 함수로 나누기
  • 캐시 객체
  • 지역 변수는 약간의 성능 이점이 있습니다
  • 가능한 한 문자열 조작 제한

4
뒤로 루프 정보 : 예, 루프 종료 비교가 더 빠릅니다. 일반적으로 변수를 사용하여 메모리에 색인을 작성하고, 반대로 캐시에 액세스하면 캐시 누락이 자주 발생하므로 (프리 페치 없음) 역효과를 낳을 수 있습니다.
Andreas Reiff

1
대부분의 경우 AFAIK는 프로그래머가 명시 적으로 역순으로 실행하지 않고도 합리적인 옵티마이 저가 루프를 사용하여 정상적으로 작동합니다. 옵티마이 저는 루프 자체를 반대로 바꾸거나 다른 방법으로도 똑같이 좋습니다. 필자는 오름차순 대 최대 와 내림차순 대 0 으로 작성된 (상대적으로 비교적 간단한) 루프에 대해 동일한 ASM 출력을 언급했습니다. 물론 Z80 일에는 역 루프를 반복적으로 쓰는 습관이 있지만 초보자에게 일반적으로 언급하는 것은 읽을 수있는 코드 및 학습보다 중요한 관행이 우선 순위가되어야하는 경우 청어 / 조기 최적화
underscore_d

반대로, 하위 레벨 언어에서는 루프를 거꾸로 돌리면 속도가 느려집니다. 왜냐하면 0에 대한 비교와 추가 빼기 대 단일 정수 비교는 단일 정수 비교가 더 빠르기 때문입니다. 감소하는 대신 메모리의 시작 주소에 대한 포인터와 메모리의 끝 주소에 대한 포인터를 가질 수 있습니다. 그런 다음 시작 포인터가 끝 포인터와 같아 질 때까지 증가시킵니다. 이렇게하면 어셈블리 코드에서 추가 메모리 오프셋 작업이 제거되므로 성능이 훨씬 향상됩니다.
잭 지핀

5

말할 수 없습니다. 코드의 모양에 따라 다릅니다. 코드가 이미 존재한다고 가정하면 간단히 코드를보고 최적화하는 방법을 알아낼 수 있습니다.

더 나은 캐시 위치, 루프 언 롤링, 긴 종속성 체인을 제거하여 더 나은 명령 수준 병렬 처리를 얻으십시오. 가능하면 분기 위로 조건부 이동을 선호하십시오. 가능하면 SIMD 지침을 활용하십시오.

코드가 수행하는 작업을 이해하고 실행중인 하드웨어를 이해하십시오. 그런 다음 코드 성능을 향상시키기 위해 수행해야 할 작업을 결정하는 것이 상당히 간단 해집니다. 그것이 제가 생각할 수있는 유일한 진정한 조언입니다.

음, 그리고 "SO에 코드를 보여주고 특정 코드에 대한 최적화 조언을 요청하십시오".


5

더 나은 하드웨어가 옵션이라면 분명히 그쪽으로 가십시오. 그렇지 않으면

  • 최상의 컴파일러 및 링커 옵션을 사용하고 있는지 확인하십시오.
  • 다른 라이브러리의 핫스팟 루틴이 자주 호출자에게 전달되는 경우 호출자 모듈로 이동하거나 복제하는 것이 좋습니다. 일부 호출 오버 헤드를 제거하고 캐시 적중을 개선 할 수 있습니다 (AIX가 strcpy ()를 별도로 링크 된 공유 객체에 정적으로 링크하는 방법 참조). 이것은 물론 캐시 적중도 감소시킬 수 있으므로 한 가지 조치가 필요합니다.
  • 특수한 버전의 핫스팟 루틴을 사용할 수 있는지 확인하십시오. 단점은 유지해야 할 여러 버전입니다.
  • 어셈블러를보십시오. 더 나을 수 있다고 생각되면 컴파일러가 이것을 이해하지 못한 이유와 컴파일러를 도울 수있는 방법을 고려하십시오.
  • 고려하십시오 : 당신은 정말로 최고의 알고리즘을 사용하고 있습니까? 입력 크기에 가장 적합한 알고리즘입니까?

첫 번째 매개 변수에 추가 할 것입니다 : 컴파일러 옵션에서 모든 디버깅 정보를 끄는 것을 잊지 마십시오 .
varnie

5

구글 방법은 하나의 옵션 "캐시 .. 가능하면 디스크를 만지지 마십시오"입니다


5

내가 사용하는 빠르고 더러운 최적화 기술은 다음과 같습니다. 나는 이것이 '첫 번째 패스'최적화라고 생각합니다.

시간이 어디에서 소비 되는지 알아보십시오 파일 IO입니까? CPU 시간입니까? 네트워크입니까? 데이터베이스입니까? 병목 현상이 아닌 경우 IO를 최적화하는 것은 쓸모가 없습니다.

환경 파악 일반적으로 최적화 할 위치를 아는 것은 개발 환경에 따라 다릅니다. 예를 들어 VB6에서는 참조로 전달하는 것이 값으로 전달하는 것보다 느리지 만 C 및 C ++에서는 참조로 전달하는 것이 훨씬 빠릅니다. C에서는 리턴 코드가 실패를 표시하면 무언가를 시도하고 다른 것을 수행하는 것이 합리적이지만 Dot Net에서는 예외를 포착하는 것이 시도하기 전에 유효한 조건을 확인하는 것보다 훨씬 느립니다.

인덱스 자주 쿼리되는 데이터베이스 필드에 인덱스를 만듭니다. 거의 항상 공간을 속도로 교환 할 수 있습니다.

조회 방지 루프 내부에서 최적화하기 위해 조회를 수행하지 않아도됩니다. 루프 외부의 오프셋 및 / 또는 색인을 찾아 내부 데이터를 재사용하십시오.

IO 최소화 특히 네트워크 연결을 통해 읽거나 쓰는 횟수를 줄이는 방식으로 설계하려고합니다.

추상화 감소 코드가 수행 해야하는 추상화 계층이 많을수록 느려집니다. 임계 루프 내에서 추상화를 줄입니다 (예 : 추가 코드를 피하는 하위 수준의 방법 공개)

사용자 인터페이스가있는 프로젝트의 스레드 생성, 느린 작업을 수행하기 위해 새 스레드를 생성하면 응용 프로그램의 응답 성 향상되지만 응답하지 않습니다.

전처리 일반적으로 공간을 빠르게 교환 할 수 있습니다. 계산이나 기타 강력한 작업이있는 경우 중요 루프에 들어가기 전에 일부 정보를 미리 계산할 수 있는지 확인하십시오.


5

OpenCL 또는 (NVIdia 칩의 경우) CUDA를 사용하여 그래픽 프로세서 (특히 존재하는 경우)로 병렬 처리되는 부동 소수점 연산이 많은 경우, 특히 단정도 정밀 연산을 사용하는 경우. GPU는 셰이더의 부동 소수점 컴퓨팅 성능이 뛰어나 CPU보다 훨씬 뛰어납니다.


5

이 답변은 다른 모든 답변에 포함되지 않았으므로 추가했습니다.

유형과 부호 사이의 암시 적 변환을 최소화하십시오.

이것은 적어도 C / C ++에 적용됩니다. 이미 변환이 없다고 생각 하더라도 성능이 필요한 기능, 특히 루프 내 변환에 대한 감시를 수행하는 함수에 대해 컴파일러 경고를 추가하는 것이 좋습니다.

GCC spesific : 코드 주위에 자세한 pragma를 추가하여이를 테스트 할 수 있습니다.

#ifdef __GNUC__
#  pragma GCC diagnostic push
#  pragma GCC diagnostic error "-Wsign-conversion"
#  pragma GCC diagnostic error "-Wdouble-promotion"
#  pragma GCC diagnostic error "-Wsign-compare"
#  pragma GCC diagnostic error "-Wconversion"
#endif

/* your code */

#ifdef __GNUC__
#  pragma GCC diagnostic pop
#endif

이와 같은 경고로 인해 발생하는 전환을 줄임으로써 몇 퍼센트의 속도를 높일 수있는 사례를 보았습니다.

어떤 경우에는 우발적 인 변환을 방지하기 위해 엄격한 경고가 포함 된 헤더가 있지만, 의도적 인 의도적 인 변환에 많은 캐스트를 추가하여 코드를 최소한으로 혼란스럽게 만들 수 있기 때문에 트레이드 오프입니다 이익.


이것이 OCaml에서 숫자 유형 사이의 캐스트가 xplicit이어야 함을 좋아하는 이유입니다.
Gaius

@Gaius 공정 점 – 많은 경우 언어 변경은 현실적인 선택이 아닙니다. C / C ++는 매우 광범위하게 사용되므로 컴파일러에 따라 더 엄격하게 만들 수 있습니다.
ideasman42

4

때로는 데이터 레이아웃을 변경하면 도움이 될 수 있습니다. C에서는 배열 또는 구조를 배열 구조로 또는 그 반대로 전환 할 수 있습니다.


4

OS 및 프레임 워크를 조정하십시오.

과도하게 들릴 수도 있지만 다음과 같이 생각하십시오. 운영 체제 및 프레임 워크는 많은 작업을 수행하도록 설계되었습니다. 응용 프로그램은 매우 구체적인 작업 만 수행합니다. OS가 애플리케이션에 필요한 것을 정확하게 수행하고 프레임 워크 (php, .net, java)의 작동 방식을 애플리케이션이 이해하도록 할 수 있다면 하드웨어를 훨씬 더 잘 활용할 수 있습니다.

예를 들어, Facebook 은 Linux에서 일부 커널 레벨을 변경하고 memcached의 작동 방식을 변경했습니다 (예 : memcached 프록시를 작성하고 tcp 대신 udp를 사용함 ).

이에 대한 또 다른 예는 Window2008입니다. Win2K8에는 X 응용 프로그램 (예 : 웹 응용 프로그램, 서버 응용 프로그램)을 실행하는 데 필요한 기본 OS 만 설치할 수있는 버전이 있습니다. 이는 운영 체제가 프로세스를 실행하는 데 드는 오버 헤드를 크게 줄이고 성능을 향상시킵니다.

물론 첫 단계로 더 많은 하드웨어를 투입해야합니다.


2
이는 다른 모든 접근 방식이 실패한 후 또는 특정 OS 또는 프레임 워크 기능이 현저하게 성능 저하를 담당 한 경우에 유효한 접근 방식이지만,이를 해제하는 데 필요한 전문 지식과 제어 수준이 모든 프로젝트에서 사용 가능한 것은 아닙니다.
앤드류 Neely
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.