기본 최적화
최적화에 관해서 많은 사람들은 "컴퓨터 속도가 지금 이렇게 빠른데, 몇 퍼센트만 빨라진다고 무슨 소용이 있겠는가? "라고 무시하는 사람들이 많습니다. 이는 그래픽, 이미지, 멀티미디어 등의 특정 소프트웨어 개발을 제외하면 대부분의 경우 의도적인 최적화가 필요하지 않지만 현재 컴파일러로 컴파일된 결과는 완전히 최적화되어 있습니다. 최적화를 완료하는 동안 개발 효율성을 보장하거나 개선할 수도 있습니다.
물론, 알고리즘의 설계는 최적화의 핵심이지만 대부분의 경우 프로그램의 실행 효율성은 주로 개발자의 프로그램에 대한 전반적인 이해, 알고리즘의 설계 등에 의해 결정됩니다! 그러나 때로는 세부 사항을 최적화하는 것도 의미가 있습니다!
게다가 이런 종류의 최적화에는 어셈블리를 통해 직접 코드를 작성할 필요가 없는 경우가 많지만, 이 경우 어셈블리 지식을 마스터하는 것의 우수성을 반영할 수도 있습니다!
다음과 같은 두 가지 기능이 있습니다.
function GetBit(i: 추기경; n: 추기경): 부울;
시작하다
결과 := Boolean((i shr n) 및 1);
끝;
function GetBit(i: 추기경; n: 추기경): 부울;
시작하다
결과 := Boolean((1 shl n) 및 i);
끝;
해당 어셈블리 코드:
MOV ECX, EDX
SHR EAX, CL
그리고 EAX, $01
MOV ECX, EDX
MOV EDX, $01
SHL EDX, CL
그리고 EAX, EDX
그들은 동일한 기능을 가지고 있으며, 모두 i의 특정 비트 값을 취하고, 1이면 True를 반환하고, 0이면 False를 반환합니다!
표면적으로는 두 함수의 실행 효율성이 동일하다고 생각할 수 있지만 실제로는 차이가 있습니다. 첫 번째 프로그램의 시프트 연산은 델파이의 기본 호출 규칙에 따르면, 이때, 값은 EAX 레지스터에 저장되고 시프트 연산은 직접 완료될 수 있지만 두 번째 프로그램은 즉치값 1에 대한 시프트 연산을 완료하려면 먼저 레지스터로 전송되어야 합니다. 그러니 지시 사항이 하나 더 있어야 합니다! 물론 모든 경우에 더 적은 명령이 더 많은 명령보다 반드시 더 빠른 것은 아닙니다. 특정 실행 중에는 명령 실행의 클럭 주기 및 명령 쌍과 같은 문제도 고려해야 합니다(이에 대해서는 나중에 설명할 수 없음). 특정 코드 환경에서만 비교가 가능한 경우에만 문제를 해결할 수 있습니다.
일반적인 상황에서는 효율성의 차이가 너무 미미하지만, 프로그래밍하는 동안 최적화에 대한 인식을 유지하는 것은 결코 나쁜 것이 아닙니다! 이러한 코드가 루프의 가장 안쪽 레이어에 위치하고, 수많은 루프를 통해 N 클럭 사이클이 누적되면 실행 효율성의 차이가 매우 커질 수 있습니다!
위의 내용은 단지 작은 예일 뿐입니다. 개발 중 어셈블리 관점에서 몇 가지 문제를 생각해 볼 수 있다면 개발 효율성을 보장하면서 고급 언어로 보다 효율적이고 상세한 코드를 작성할 수 있다는 것을 알 수 있습니다! 그러나 여전히 임베디드 어셈블리 코드를 사용하여 세부적인 최적화를 완료해야 하는 경우가 많으며, 때로는 임베디드 어셈블리 코드를 적용함으로써 코드 작성도 더욱 효율적이 될 수 있습니다.
32자리 숫자의 바이트 순서를 바꿔야 하는 경우 델파이의 고급 언어로 어떻게 완전히 바꿀 수 있습니까? Shifting을 사용할 수도 있고 내장 함수인 Swap을 여러 번 호출할 수도 있지만 BSWAP 명령어를 생각하면 모든 것이 매우 간단해집니다.
function SwapLong(값: 추기경): 추기경;
asm
BSWAP EAX
끝;
참고: 위와 동일하게 Value의 값은 EAX 레지스터에 저장되고 32자리 값도 EAX를 통해 반환되므로 한 문장만 필요합니다.
물론 대부분의 임베디드 어셈블리 최적화는 그렇게 간단하지 않지만 대학에서 배운 작은 어셈블리 지식으로는 더 심층적인 최적화를 달성하기 어렵습니다. 컴파일된 어셈블리 코드를 지속적으로 축적하고 비교해야만 경험을 얻을 수 있습니다! 다행히도 대부분의 경우 세부적인 최적화는 프로그램 설계의 주요 부분이 아닙니다.
하지만 개발된 프로그램이 그래픽, 이미지, 멀티미디어 등을 포함하는 경우에는 더욱 심층적인 최적화 작업이 필요합니다! 다행스럽게도 Delphi6은 부동 소수점 명령어의 최적화이든 MMX, SSE, 3DNow 등의 적용이든 훌륭한 지원을 제공할 수 있습니다. 이전 버전의 Delphi에서 이러한 CPU 확장 명령어 세트를 지원하거나 향후 새로운 CPU 명령어 세트를 지원하려는 경우에도 임베디드 어셈블리에서 Delphi가 지원하는 DB, DW, DD, DQ의 네 가지 어셈블리 명령어를 사용할 수 있습니다( 볼랜드의 Delphi6 공식 언어 매뉴얼에는 DB, DW, DD를 지원한다고만 나와 있고 관련 명령어의 수치 표현도 유연하게 구현할 수 있습니다.
좋다:
DW $A20F //CPUID
DW $770F //EMMS
DB $0F, $6F, $C1 //MOVQ MM0, MM1
지침을 이해하는 것은 단지 기초일 뿐입니다. FPU, MMX 및 SSE를 중심으로 알고리즘을 설계한 후 이를 더욱 최적화하려면 CPU 자체의 일부 기술적 특성도 이해해야 합니다.
다음 두 가지 코드를 살펴보겠습니다.
asm
추가 [a], ECX
추가 [b], EDX
끝
asm
MOV EAX, [a]
MOV EBX, [b]
EAX, ECX 추가
EBX, EDX 추가
MOV [a], EAX
MOV [b], EBX
끝
두 번째가 더 효율적인가요? 위에서 언급한 바와 같이 명령어 수가 적다고 해서 실행 효율성이 높은 것은 아닙니다. 관련 정보에 따르면 코드의 첫 번째 섹션에 있는 두 명령어를 실행하는 데 필요한 클럭 주기는 3입니다(각 명령어는 세 단계의 읽기를 완료해야 합니다. 수정 및 쓰기 단계), 코드의 두 번째 섹션에 있는 6개 명령어에 의해 실행되는 클럭 사이클은 모두 1입니다. 그렇다면 두 코드가 똑같이 효율적인가요? 다시 한 번 틀렸지만, 두 번째 코드 조각은 실제로 첫 번째 코드 조각보다 더 효율적으로 실행됩니다! 왜? 펜티엄급 이후의 CPU는 명령을 실행하는 파이프라인이 2개이기 때문에 인접한 두 명령을 쌍으로 만들면 동시에 실행할 수 있습니다! 위의 두 가지 코드에 특정한 이유는 무엇입니까?
첫 번째 코드의 두 명령어는 쌍을 이룰 수 있지만 필요한 총 실행 클럭 주기는 3이 아닌 5인 반면, 두 번째 코드의 6개 명령어는 병렬로 실행될 수 있으므로 이러한 결과가 발생합니다.
말하자면, 이것들은 모두 매우 간단한 예이기 때문에 그 자체로는 큰 도움을 줄 수 없습니다. 특정 프로그램을 정말로 최적화하고 싶다면 FPU 및 MMX 최적화에 대한 특별 기사를 찾아보거나 "비순차 실행" 및 "분기 예측"과 같은 기술을 연구하고 연구하는 기술 매뉴얼을 찾아야 합니다. 나는 대학에 다니는 모든 친구들이 "돈을 버는" 개발 도구와 세련된 신기술에만 집중하지 않고 기초를 다지는 데 더 많은 시간을 할애하여 새로운 지식을 빠르게 습득할 수 있기를 바랍니다. , 그래야만 새로운 개발 도구와 기술을 더 빠른 시간 내에 익힐 수 있습니다... (천 단어 생략).
하지만 여전히 실용적인 문제를 해결하려면 지식을 사용해야 합니다. 매일 기술적인 세부 사항에만 집중한다면 훌륭한 해커가 될 수는 있지만 결코 일류 소프트웨어를 개발할 수는 없습니다. 그러므로 근본적인 목적은 여전히 가치 창출이어야 합니다. 그러니까... 더 이상 이야기하지 않겠습니다. 더 이상 이야기하면 기술적인 기사처럼 보이지 않을 것입니다. ^_^
첨부: 실행 효율성을 고려하는 것 외에도 프로그램 최적화는 크기 문제도 고려해야 합니다(작은 크기는 메모리를 더 빠르게 로드하고 명령 디코딩 및 기타 작업을 더 빠르게 완료할 수 있음). 예를 들어 EAX 레지스터를 지우려면 SUB EAX, EAX 또는 XOR EAX를 사용하십시오. , MOV EAX 대신 EAX, $0. 실행 클럭 주기는 모두 1이지만 전자(2바이트)의 명령 길이는 후자(5바이트)보다 확실히 짧습니다. 하지만 위에서 언급한 내용은 모두 세부사항이기 때문에 물량 문제는 언급되지 않았습니다. 더 많은 크기 감소 문제는 컴파일러가 해결해야 합니다. 임베디드 ASM 코드를 작성할 때 약간의 주의만 기울이십시오.