IT

최후의 수단의 성능 최적화 전략

lottoking 2020. 10. 3. 09:37
반응형

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


이 사이트에는 이미 많은 성능 질문이 특화되어 있고 상당히 좁다는 것을 알게되었습니다. 그리고 거의 모든 사람들이 조기 최적화를 피하기 위해 조언을 반복합니다.

가정 해봅시다 :

  • 코드가 이미 작동하고 있습니다.
  • 선택한 알고리즘은 이미 문제 상황에 최적입니다.
  • 코드가 측정되는 문제가 격리됩니다.
  • 최적화 시도는 문제를 예방 조치하지 않습니다.

내가 여기서 찾고있는 것은 중요한 알고리즘의 마지막 몇 퍼센트를 짜낼 수있는 전략과 트릭입니다.

이상적으로는 언어에 구애받지 않는 답변을 만들고 해당하는 경우 제안 된 전략을 표시합니다.

제 자신의 초기 제안과 함께 답장을 추가하고 Stack Overflow 커뮤니티가 생각할 수있는 다른 모든 것을 기대합니다.


좋아, 개선의 여지가 많지 않은 것처럼 보이는 문제를 정의하고 있습니다. 내 경험상 상당히 드물다. 나는 1993 년 11 월 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)입니다 빨리 : 나는 스택 오버플로에 너무 자주 볼 질문의 종류 .

추신 왜 약한 러를 사용하지 않을까 궁금 할 것입니다. 대답은 "문제"가 거의 모든 것이 정확히 찾아내는 함수 호출 사이트라는 것입니다. 거의에도 약한 명령문과 호출 명령이 전체 함수보다 위치를 찾는 것이 더 중요하고 수정하기는 생각을 거의하지 않습니다.

나는 실제로 그것을 위해 위해 프로파일 러를 만들었지 만, 코드가하는 일에 대한 진정한 친밀감을 위해, 당신의 손가락을 바로 잡을 수있는 대체물은 없다. 발견 된 문제 중 어느 것도 너무 작아서 쉽게 놓칠 수 없기 때문에 몇 가지 간단한 문제가 아닙니다.

추가됨 : 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으로 증가하여 이제 "더 큰 물고기"가되었습니다. 일반적으로 그렇게됩니다.

이 프로젝트는 내가 도운 실제 프로젝트에서 나오는 데일 수 있습니다. 이 프로젝트에서 성능 문제는 작업이 완료 확인하기 위해 내부 루프 내 데이터베이스 액세스 루틴을 호출하는 것과 같이 훨씬 더 극적 성능.

참조 추가 : 원본 코드와 재 이미지 소스 코드는 www.ddj.com , 1993 년의 9311.zip 파일, slug.asc 및 slug.zip 파일에서 사용할 수 있습니다 .

2011/11/26 편집 : 이제 Visual C ++의 소스 코드와 튜닝 방법에 대한 간단한 설명이 포함 SourceForge 프로젝트 가 있습니다. 그러나 거치며 동일한 순서를하지만 여전히 2-3 배의 속도를 얻습니다.


제안 :

  • 재 계산보다는 사전 계산 : 입력 범위가 또는 계산 결과 계산이 포함 된 루프가 또는 계산 결과 계산이 포함 된 루프에 대해 계산 결과를 포함하는 모든 값에 대해 해당 계산을 포함하는 조회하는 것이 좋습니다. 입력. 그런 다음 대신 알고리즘 내부에서 간단한 조회를 사용하십시오.
    다운 : 미리 계산 된 값의 몇 가지 실제적인 문제를 사용하는 경우, 상당한 메모리를 사용할 수 있습니다.
  • 라이브러리 메서드를 사용하지 않습니다 . 대부분의 라이브러리는 광범위한 시나리오에서 사용 되어야만하는 변수에 대한 null 검사를 수행해야합니다. 방법을 다시 구현하면 다음과 같은 많은 논리를 제거 할 수 있습니다. 사용중인 상황에는 적용되지 않습니다.
    단점 : 추가 코드를 작성하면 버그가 더 커집니다.
  • 도서관 방법을 사용하십시오 : 저와 모순되기 위해 언어 도서관은 당신이나 나보다 훨씬 똑똑한 사람들에 의해 작성됩니다. 확률은 더 빨리 더 빨리받을 것입니다. 실제로 더 빨리 만들 수 있습니다 직접 구현하지 않고 (예 : 항상 측정하십시오!).
  • 속임수 : 어떤 경우에는 문제에 대한 정확한 계산이 필요한 수 있고 '정확함'이 필요하지 않을 수 있고, 근사치가 '충분히 좋다'고 거래에서 훨씬 빠를 수 있습니다. 스스로에게 물어보세요. 답이 1 % 나 나오고 정말 중요한가요? 5 %? 10 %라도?
    단점 : 음 ...하지 않을 것입니다.

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

fooCalc 알고리즘을 더 빨리 만들 수는 없지만 애플리케이션이 사용자에게 더 반응하는 것처럼 보이게하는 방법이 종종 있습니다.

몇 가지 예 :

  • 사용자가 무엇을해야할지 예상하고 그 전에 작업을 시작합니다.
  • 결과를 한꺼번에 표시하는 대신
  • 정확한 진행 미터

당신의 프로그램을 더 빨리 만들지는 않을 것이지만, 당신의 사용자가 당신의 속도로 더 행복하게 만들 수 있습니다.


나는 내 인생의 대부분을 이곳에서 보냅니다. 광범위한 보유는 약력 러를 실행하고 기록하는 것입니다.

  • 캐시 누락 . 데이터 캐시는 대부분의 프로그램에서 중단의 가장 큰 원인입니다. 더 나은 지역에서 문제가 발생하는 데이터 구조를 갖도록 캐시 적중률을 개선합니다. 사용할 수있는 바이트 (가능한 캐시 페치)를 제거하기 위해 구조 및 숫자 유형을 압축합니다. 지연을 가능성이 가능한 경우 데이터를 미리 가져옵니다.
  • 로드 히트 스토어 . 포인터 앨리어싱에 대한 컴파일러 가정과 메모리를 통해 연결이 있고 이동하는 경우 전체 CPU 파이프 라인이로드 작업에서 지워지는 특정 병리학 적 동작을 유발할 수 있습니다. float, 벡터 및 int가 서로 캐스팅되는 위치를 찾아 제거하십시오. __restrict에게 앨리어싱을 컴파일러 약속 하려면 자유롭게 사용하십시오 .
  • 마이크로 코딩 된 작업 . 대부분의 프로세서에는 파이프 라인 할 수없는 작업이 대신 ROM에 많은 서브 루틴을 실행합니다. PowerPC의 예로는 정수 곱하기, 나누기 및 변수에 의한 시프트가 있습니다. 모든 파이프 라인이 중지되는 것입니다. 디스패치의 이점을 얻을 수 있습니다.
  • 분기가 잘못 예측 합니다. 이것들은 파이프 라인을 너무 비입니다. CPU가 분기 후 파이프를 다시 채우는 데 많은 시간을 소비하는 경우를 찾고, 가능한 경우 분기 힌팅을 사용하여 더 자주 예측하도록하십시오. 또는 더 좋은 방법은 분기를 가능한 한 조건부 이동으로 바꾸는 것입니다. 특히 부동 소수점 연산 후에는 파이프가 일반적으로 더 깊고 fcmp 후 조건이 플래그를 보유면 중단이 있습니다.
  • 순차 부동 소수점 연산 . 이 SIMD를 만드십시오.

그리고 제가 좋아하는 한 가지 더 :

  • 어셈블리 목록을 출력하도록 컴파일러를 설정 하고 코드의 핫스팟 함수에 대해 무엇을하는지 확인합니다. "좋은 컴파일러가 자동으로 수행 할 수있는"모든 영리한 최적화? 실제 컴파일러가 수행하지 않을 가능성이 있습니다. GCC가 진정으로 WTF 코드를 세우는 것을 보았습니다.

더 많은 하드웨어를 사용 해보세요!


더 많은 제안 :

  • I / O 방지 : 모든 I / O (디스크, 네트워크, 포트 등)는 항상 계산을 수행하는 코드보다 훨씬 느리 엄격하게 필요하지 않습니다.

  • I / O를 미리 이동 : 계산에 필요한 모든 데이터를 미리로드하여 중요한 알고리즘의 핵심 내에서 I / O 대기를 반복하지 않습니다 (결과적으로 반복 될 수 있음). 탐색 탐색, 한 번에 모든 데이터를로드 할 때 탐색을 피할 수 있음).

  • 지연 I / O : 계산이 끝날 때까지 결과를 작성하지 말고 데이터 구조에 저장 한 다음 어려운 작업이 끝에서 한 번에 기록합니다.

  • Threaded I / O : 대담한 사람들을 위해로드하는 내용으로 이동하여 'I / O'또는 'Delay I / O'를 실제 계산과 결합하여 더 많은 데이터를로드하는 동안 작업 할 수 있습니다. 이미 가지고있는 데이터에 대한 계산에서 또는 다음 데이터 배치를 계산하는 동안 마지막 배치의 결과를 사용할 수 있습니다.


대부분의 성능 문제는 데이터베이스 문제와 관련이 있으므로 쿼리 및 저장 프로 시저를 제공 할 때 몇 가지 특정 사항을 제공합니다.

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

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

상관 된 하위 쿼리를 사용하지 않습니다. 조인 (가능한 파생 경우 된 테이블에 대한 조인 포함)을 사용합니다 (Microsoft SQL Server에 해당되는 사항이지만 다른 백엔드를 사용할 때 조언을 테스트합니다).

색인, 색인, 색인. 그리고 데이터베이스에 해당하는 경우 통계를 업데이트하십시오.

쿼리를 sarg 가능하게 만드십시오 . 의미는 같은 절의 첫 문자에 와일드 카드를 사용하거나 조인에서 함수를 사용하거나 어디에 문의 부분에 사용하는 것과 같이 사용할 것을 사용할 수 없습니다.

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

어떤 종류의 루프도 트리거에 넣지

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

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

어떤 종류의 약력 러 도구를 사용하여 데이터베이스에서 보내고받은 내용을 찾습니다. 프로 시저로드가 빠른 약력 링을 통해 웹 페이지가 한 번이 아닌 여러 번 쿼리를 요청하고 사실을 발견 할 때 페이지 속도가 느린 이유를 기억할 수 없습니다.

약력 러는 누가 누가 차단하는지 찾는데도 도움이됩니다. 빨리 실행되는 일부 쿼리는 다른 쿼리의 잠금으로 인해 매우 느려질 수 있습니다.


하나의 가장 중요한 제한 요소는 메모리 장치 입니다. 멀티 코어는 하나가 코어간에 공유되기 때문에 있습니다. 또한 캐시 구현에 사용되는 칩 영역도 코어와 단일로 나누어 져이 문제가 더욱 악화됩니다. 마지막으로, 코어 수가 증가하여 서로 다른 캐시를 일관되게 유지하는 데 필요한 칩 간 신호도 증가합니다. 이것은 또한 벌금을 추가합니다.

관리해야 할 효과입니다. 거기에 마이크로 코드 관리를 통해, 거기에 신중한 고려와 리팩토링을 통해.

많은 댓글이 이미 캐시에있는 코드를 사용하고 있습니다. 이것에는 최소한 두 가지 다른 특징이 있습니다.

  • 메모리 가져 오기 지연을 피하십시오.
  • 낮은 메모리 버스 강도 (대역폭).

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

두 번째 문제는 데이터 개선과 관련이 있습니다. 사용 가능한 캐시에 맞는 데이터의 하위 집합에서 작동하는 알고리즘을 변경하고 해당 데이터가 캐시에있는 동안 가능한 한 많이 보완합니다.

데이터를 더 엄격하게 압축하고 핫 루프의 캐시 라인에있는 모든 데이터를 사용하는지 확인하면 다른 효과를 방지 하고 캐시유용한 데이터를 넣을 수 있습니다 .


  • 어떤 하드웨어에서 실행 중입니까? 플랫폼 별 최적화 (예 : 벡터화)를 사용할 수 있습니까?
  • 더 나은 컴파일러를 가질 수 있습니까? 예를 들어 GCC에서 인텔로 전환 하시겠습니까?
  • 알고리즘을 사용할 수 있습니까?
  • 데이터를 사용하여 캐시 미스를 사용하여 있습니까?
  • 어설 션을 축소 할 수 있습니까?
  • 컴파일러 및 플랫폼에 맞게 미세 최적화하십시오. "if / else에서 가장 일반적인 문장을 먼저 입력"이라는 스타일로

"Google 관점"을 올립니다. 즉, 애플리케이션이 어떻게 크게 송 신화되고 처리 될 수 있는지 결정합니다. 이는 즉 필연적으로 언젠가는 애플리케이션을 다른 머신과 네트워크에 배포하여 거의 선형 적으로 확장 할 수있는 것이 의미합니다. 당신이 원하는 던지는 하드웨어와 함께.

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

– 애플리케이션을 프로파일 링하는 것은 더 이상적으로 프로그램 코드를 프로파일 링하는 것이 아니라 관점에서 이러한 가능성을 최적화 할 수있는 시야하기 위해 모든 주변 시스템 및 인프라 (네트워크, 스위치, 서버, RAID 어레이)를 프로파일 링하는 것을 의미합니다.


Mike Dunlavey의 답변이 답변이 들지만 실제로는 예를 지원하는 훌륭한 답변이지만 매우 간단하게 표현할 수 있습니다.

가장 많은 시간이 그것이 무엇인지 이해하고 있습니다.

알고리즘을 구체화해야하는 부분을 이해하는 데 도움이되는 것입니다. 이것은 이미 해결되어야하는 유일한 문제입니다. 또한 속도에 대한 탐구에서 아키텍처에 독립적이기를 원 가정합니다.

따라서 알고리즘은 최적화 될 수 있습니다. 방송을 통해 알고리즘 또는 구현 중 어느 부분이 무엇인지 알 수 있습니다. 따라서 시간이 가장 많은 돼지가 검토 대상이 될 수 있습니다. 그러나 마지막 몇 %를 짜내고 싶다고 말하고 더 약간 부분, 처음부터 자세히 조사하지 않습니다.

마지막으로 뛰어난 솔루션 또는 성능으로 다른 구현하는 여러 가지 방법에 대한 성능 수치와 함께 약간의 시행 착오를 통해 시간을 절약 할 수있는 시간 절약을 제공하는 데 도움이되는 결과를 얻을 수 있습니다.

HPH, asoudmove.


  • 인라인 루틴 (호출 / 반환 및 변수 푸시 제거)
  • 테이블 조회로 테스트 / 스위치를 제거하십시오 (빠른 경우).
  • CPU 캐시에 딱 맞는 지점까지 루프 (Duff의 장치)를 풀니 다.
  • 캐시를 날리지 않도록 메모리 액세스 지역화
  • 최적화 프로그램이 아직 수행하지 않는 경우 관련 계산을 현지화합니다.
  • 수행하지 않는 경우 루프 불변을 제거하십시오.

  • 효율적인 알고리즘을 사용하고 점에 도달하면 더 많은 속도 또는 메모리 가 필요한지에 대한 질문입니다 . 더 빠른 속도를 위해 메모리에서 "지불"예측 캐싱을 사용하거나 메모리 풋 프린트를 위해 계산을 사용하십시오.
  • 가능하다면 (그리고 더 많은 비용 효율적인) 하드웨어를 문제에 없앨 수 있습니다. 더 빠른 CPU, 더 많은 메모리 또는 HD가 문제를 코딩보다 빨리 빨리 수 있습니다.
  • 가능하면 보내 화를 사용하십시오 . 의 일부를 코드 여러 스레드에서 실행 하십시오 .
  • 작업에 도구를 사용하십시오 . 일부 프로그래밍 언어는 관리 코드 (예 : Java / .NET)를 사용하여 더 효율적인 코드를 생성하지만 기본 프로그래밍 언어는 더 빠른 실행 코드를 생성합니다.
  • 마이크로 최적화 . 적용 가능한 경우에만 최적화 된 어셈블리를 사용하여 작은 코드 조각의 속도를 사용할 수 있습니다. 올바른 위치에서 SSE / 벡터 최적화를 사용하면 성능이 향상 될 수 있습니다.

분할 및 정복

처리중인 데이터 세트가 너무 크면 청크를 반복합니다. 코드를 많이 사용합니다. 모 놀리 식 프로그램이 권한이 있다면 이제 더 잘 알 것입니다.


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

  • ... 기억이라면- "컴퓨터 프로그래밍의 예술"시리즈 중 하나 인 Knuth가 오래 전에 쓴 책 중 하나를 찾으십시오. 아마도 그것은 정렬과 검색에 관한 것입니다. 제 기억이 틀렸다면 그가 느린 테이프 데이터 저장을 다루는 방법에 대해 어떤 이야기를하는지 알아 내야합니다. 그의 메모리 / 테이프 쌍을 추가 캐시 / 주 메모리 쌍 (또는 L1 / L2 캐시)으로 정신적으로 변환 합니다. 그가 설명하는 모든 트릭을 연구하십시오. 문제를 해결하는 것을 찾을 수 없습니다. 전문 컴퓨터 과학자를 고용하여 전문 연구를 수행하십시오. 메모리 문제가 FFT로 우연히 발생하는 경우 (기수 -2 나비를 수행 할 때 비트 반전에서 캐시 누락) 과학자를 고용하지 않습니다. 대신 수동으로 최적화 할 때까지 하나씩 패스를 최적화하십시오. 다시이기거나 막 다른 골목에 도달합니다. 당신은 남은 몇 퍼센트까지 짜내 죠? 의 경우 그것 실제로 당신이 추론 가능성이 이길을 구석으로입니다.

  • ... 프로세서 인 경우-어셈블리 언어로 전환하십시오. 프로세서 사양 연구- , VLIW, SIMD가 필요한 것. 함수 호출은 대체 가능한 틱 이터 일 가능성이 있습니다. 루프 변환 학습-파이프 라인, 전개. 곱셈과 나눗셈은 비트 시프트로 교체 / 보간 될 수 있습니다 (작은 정수로 곱하면 더하기로 바꿀 수 있음). 더 짧은 데이터로 트릭을 시도. 운이 좋으면 64 비트의 급한 하나가 32 비트에 2 개, 16 비트에 4 개 또는 8 비트에 8 개로 대체 될 수 있습니다. 더 오래 시도하십시오데이터-예를 들어 부동 계산은 특정 프로세서에서 이중 계산보다 느려질 수 있습니다. 삼각법이있는 경우 미리 계산 된 테이블과 싸우십시오. 또한 정밀도 손실이 허용 된 한계 내에있는 경우 작은 값의 사인이 해당 값으로 대체 될 수 있음을 유의하십시오.

  • ... 네트워크라면-전달하는 데이터를 압축하는 것을 생각해보십시오. XML 전송을 바이너리로 바꿉니다. 연구 프로토콜. 데이터 손실을 처리 할 수 ​​있다면 TCP 대신 UDP를 사용해보십시오.

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

HTH :)


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

때로는 캐시가 많은 것을 얻지 못하지만 캐시를 완전히 추가 한 다음 도움이되지 않는 곳에서는 비활성화하는 쉬운 방법입니다. 나는 종종 코드를 마이크로 분석하지 않고도 엄청난 성능을 얻는다는 것을 발견했습니다.


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

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

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


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

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

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

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

  • 병렬화 : 논리적으로 엄격하게 순차적으로 발행 할 필요가없는 순차적 트랜잭션을 찾아 병렬 로 발행하도록 시스템을 재 작업합니다. 엔드 투 엔드 요청에 내재 된 네트워크 지연이 ~ 2 초인 한 가지 사례를 다루었습니다. 이는 단일 트랜잭션에서는 문제가되지 않았지만 사용자가 클라이언트 애플리케이션을 다시 제어하기 전에 6 개의 연속 2 초 왕복이 필요했을 때 , 그것은 엄청난 좌절의 원천이되었습니다. 이러한 트랜잭션이 실제로 독립적이라는 사실을 발견하면 트랜잭션을 병렬로 실행할 수 있으므로 최종 사용자의 지연을 단일 왕복 비용에 매우 가깝게 줄일 수 있습니다.

  • 결합 : 순차적 요청 순차적 으로 실행 되어야하는 경우이를 하나의보다 포괄적 인 요청으로 결합 할 기회를 찾습니다. 일반적인 예에는 새 엔터티 생성과 해당 엔터티를 다른 기존 엔터티와 연결하라는 요청이 있습니다.

  • 압축 : 텍스트 형식을 바이너리 형식으로 바꾸거나 실제 압축 기술을 사용하여 페이로드 압축을 활용할 기회를 찾습니다. 많은 현대 (즉, 10 년 이내) 기술 스택이이를 거의 투명하게 지원하므로 구성되어 있는지 확인하십시오. 나는 종종 문제가 대역폭이 아닌 지연 시간이라는 것이 분명해 보이는 압축의 상당한 영향에 놀랐습니다. 트랜잭션이 단일 패킷에 맞도록 허용하거나 그렇지 않으면 패킷 손실을 피하여 크기가 커진다는 사실을 발견 한 후 성능에 미치는 영향.

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

In the steps above, I focus on the application related optimisation process, but of course you must ensure the underlying network itself is configured in the most efficient manner to support your application too. Engage the networking specialists in the business and determine if they're able to apply capacity improvements, QoS, network compression, or other techniques to address the problem. Usually, they will not understand your application's needs, so it's important that you're equipped (after the Analyse step) to discuss it with them, and also to make the business case for any costs you're going to be asking them to incur. I've encountered cases where erroneous network configuration caused the applications data to be transmitted over a slow satellite link rather than an overland link, simply because it was using a TCP port that was not "well known" by the networking specialists; obviously rectifying a problem like this can have a dramatic impact on performance, with no software code or configuration changes necessary at all.


이 질문에 대한 일반적인 대답을 제공하는 것은 매우 어렵습니다. 실제로 문제 영역과 기술 구현에 따라 다릅니다. 언어 중립적 인 일반적인 기술 : 제거 할 수없는 코드 핫스팟을 식별하고 어셈블러 코드를 직접 최적화합니다.


마지막 몇 %는 CPU 및 애플리케이션에 따라 매우 다릅니다 ....

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

목록은 계속됩니다 ....하지만 이런 종류의 것들은 정말 마지막 수단입니다 ...

x86 용으로 빌드하고 적절한 성능 프로파일 링을 위해 코드에 대해 Valgrind / Cachegrind를 실행 합니다. 또는 Texas Instruments의 CCStudio 에는 멋진 프로파일 러가 있습니다. 그러면 어디에 집중해야할지 정말 알게 될 것입니다 ...


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

오프라인이 아닌 프로젝트의 경우 최상의 소프트웨어와 최상의 하드웨어를 보유하고 있지만 통과 출력이 약하면 그 얇은 선이 데이터를 압착하고 지연을 줄 것입니다 (밀리 초).하지만 마지막 하락에 대해 이야기하는 경우 , 그것은 보내거나받은 모든 패키지에 대해 연중 무휴로 얻을 수있는 약간의 드롭입니다.


이전 답변만큼 깊이 또는 복잡하지는 않지만 여기에 있습니다. (초급 / 중급 수준입니다)

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

말할 수 없습니다. 코드가 어떻게 생겼는지에 따라 다릅니다. 코드가 이미 존재한다고 가정 할 수 있다면 코드를보고 그로부터 최적화 방법을 알아낼 수 있습니다.

더 나은 캐시 지역성, 루프 풀기, 긴 종속성 체인을 제거하여 더 나은 명령 수준 병렬 처리를 얻으십시오. 가능한 경우 분기보다 조건부 이동을 선호합니다. 가능한 경우 SIMD 지침을 활용하십시오.

코드가 수행하는 작업을 이해하고 실행중인 하드웨어를 이해합니다. 그러면 코드 성능을 향상시키기 위해 수행해야 할 작업을 결정하는 것이 매우 간단 해집니다. 이것이 제가 생각할 수있는 유일한 일반적인 조언입니다.

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


더 나은 하드웨어가 옵션이라면 확실히 그것을 선택하십시오. 그렇지 않으면

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

Google 방식은 "캐시 .. 가능하면 디스크를 만지지 마십시오"라는 옵션 중 하나입니다.


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

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

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

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

조회를 피하십시오 . 루프 내부에서 최적화 할 수 있도록 조회를 수행 할 필요가 없습니다. 루프 외부의 오프셋 및 / 또는 인덱스를 찾아 내부 데이터를 재사용합니다.

IO 최소화 특히 네트워크 연결을 통해 읽거나 써야하는 횟수를 줄이는 방식으로 설계하십시오.

추상화 감소 코드가 처리 해야하는 추상화 계층이 많을수록 속도가 느려집니다. 중요 루프 내에서 추상화를 줄입니다 (예 : 추가 코드를 피하는 하위 수준 메서드 표시).

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

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


이 답변을 다른 모든 답변에 포함하지 않았기 때문에 추가했습니다.

형식과 기호 간의 암시 적 변환을 최소화합니다.

이것은 적어도 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

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

어떤 경우에는 우발적 인 변환을 방지하기 위해 포함 된 엄격한 경고가있는 헤더가 있지만 의도적 인 변환에 많은 캐스트를 추가하여 코드를 최소한으로 복잡하게 만들 수 있기 때문에 이것은 절충안입니다. 이득.


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


OS와 프레임 워크를 조정합니다.

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

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

이에 대한 또 다른 예는 Window2008입니다. Win2K8에는 X 응용 프로그램 (예 : 웹 응용 프로그램, 서버 응용 프로그램)을 실행하는 데 필요한 기본 OS 만 설치할 수있는 버전이 있습니다. 이렇게하면 OS가 실행중인 프로세스에 대한 오버 헤드가 많이 줄어들고 성능이 향상됩니다.

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


값 대신 참조로 전달

참고 URL : https://stackoverflow.com/questions/926266/performance-optimization-strategies-of-last-resort

반응형