델파이에서 어셈블리 사용하기 (0)

사용자 삽입 이미지
이 아티클은 귀도 기벨스(Guido Gybels)의 Using Assembler in Delphi를 번역한 것으로, 총 4개의 장으로 되어 있는 시리즈 아티클들 중 서문 부분입니다. 번역 및 전재를 하도록 허락해주신 귀도씨에게 감사드립니다.

원문: http://www.guidogybels.eu/asmintro.html

 

델파이에서 어셈블리 사용하기

4번째 리비젼, 2010년 5월

이 문서의 첫번째 에디션은 90년대말에 만들어졌으며 당시엔 델파이 2 버전에 맞춰져 있었습니다. 제가 제 프로젝트에서 어셈블리를 사용할 필요가 있었을 때, 델파이 프로그램에 어셈블리 코드를 적절히 통합하는 방법에 대한 문서가 그다지 많지 않다는 것을 알게 되었습니다. 이 문서는 그런 갭을 메꾸기 위한 목적으로 작성되었으며, 여러번 개정되었습니다. 제가 처음 이 문서를 쓴 이후로 프로세서와 델파이 모두 크게 발전해왔지만, 원래의 가치는 그대로 유지되었다고 생각합니다.

서론

많은 프로그래머들은 아직도 어셈블리를 어렵고 저수준의 프로그래밍과 결부시켜 생각합니다. 또 그 대부분은 어셈블리를 이해할 수 없고 마스터가 불가능하다고 생각하기도 합니다. 사실은 그렇게까지 심하지는 않으며, 그런 인식 대부분은 단지 낯설기 때문입니다. 좋은 어셈블리 코드 작성하는 방법을 배우는 것은 상당히 가능한 일이고 천재여야만 가능한 것은 아닙니다. 반대로, 강좌 몇개를 학습한다고 해서 평균적인 델파이/파스칼 코드보다 더 빠른 코드를 작성할 수 있게 되는 것도 아닙니다. 그 이유는, 델파이 코드를 작성할 때 여러분은 효율적이고 경험 있는 어셈블리 프로그래머인 ‘컴파일러’와 경쟁하는 셈이기 때문입니다. 전반적으로, 컴파일러에 의해 만들어진 코드는 대부분의 애플리케이션의 경우 충분히 효율적이고 빠릅니다.

대부분의 사람들은 자기 소프트웨어의 성능을 높이기 위해 어셈블리에 손을 대기 시작합니다. 하지만, 성능 문제의 핵심은 대부분 언어의 선택이 아닌 설계에 달려 있습니다. 어셈블리를 사용한다고 해도 나쁜 알고리즘으로 인한 성능 저하를 만회할 수 있는 부분은 아주 미미합니다. 따라서, 성능에 있어 병목 현상을 겪는 경우 가장 먼저 해야 할 일은 바로 어셈블리에 의지하려 할 것이 아니라 자신의 코드 아키텍처와 파스칼 구현을 재검토하는 것입니다. 코드를 어셈블리로 작성한다고 해서 나쁜 알고리즘이나 나쁜 접근 방식을 좋은 것으로 바꿀 수는 없습니다.

비슷하게, 대부분의 프로그래머들은 어셈블리로 코드를 작성하는 것은 당연히 더 빠르다는 것을 의미하다고 잘못 믿고 있으며, 어셈블리로 작성한 코드는 당연히 델파이/파스칼 코드보다 빠르다고 간주하는 경향이 있습니다. 분명히 그렇지 않습니다. 잘못 작성된 어셈블리 루틴은 낮은 성능을 내며 이상한 버그와 문제들을 일으킬 수 있습니다.

하지만 어떤 경우엔 어셈블리 코드를 적절히 사용하는 좋은 경우도 있습니다. 델파이(파스칼) 언어는 범용 프로그래밍 언어이기 때문에 어떤 전문화된 작업은 어셈블리에서 더 잘 구현될 수도 있습니다. 또한 프로세서 고유의 기능을 활용하기 위해서도 수작업의 코드 설계가 필요할 수 있습니다.

어셈블리 프로그래밍의 원칙들을 가르치는 것은 이 문서의 범위에 포함되지 않습니다. 어셈블리 프로그래밍을 다루는 정보 리소스들은 다른 곳에도 많이 있습니다. 이 시리즈의 가장 마지막에 제시될 ‘추가 참고 자료들’을 참고하십시오.

코드를 어셈블리로 다시 작성하기 전에, 여러분의 병목이 어디에 있는지 그리고 왜인지를 먼저 살펴보십시오. 이런 분석 작업에는 프로파일러가 도움이 될 수 있습니다. 일단 원인을 확인하고 나면, 한발 물러나서 구조와 알고리즘을 살펴보십시오. 어셈블리로 직행하기 전에 알고리즘이나 애플리케이션 설계를 변경함으로써 더 높은 성능을 얻을 수 있는 경우가 종종 있습니다. 반면 어떤 특정한 경우에는 어셈블리가 정말로 더 나을 수도 있고, 심지어는 더 단순할 수도 있습니다.

일단 어셈블리가 실제로 필요하다고 결론을 내린 후에는, 여러분의 코드에 대한 계획을 잡아보고 알고리즘을 설계할 시간을 가져야 합니다. 원하는 것과 구현할 방법에 대한 분명한 생각을 정리한 후에야 코딩을 시작할 수 있습니다. 이런 이슈들에 대해 미리 생각해두지 않으면 스파게티 코드와 복잡한 문들, 유지보수가 불가능한 프로그램만 남게 될 것입니다. 형편없는 버그들을 만들게 될 가능성은 말할 것도 없습니다.

기본 원칙들

구현 과정에서 여러분이 반드시 따라야 할 몇가지 원칙들이 있습니다. 첫번째 중요한 규칙은, 루틴은 최대한 짧아야 한다는 것입니다. 어셈블리는 성능과 간략함이 필수적인 실질적인 핵심 동작 일부에서만 사용되어야 합니다. 따라서 대부분의 경우 여러분은 상당히 짧고 전문화된 루틴들을 다룰 수 있게 됩니다. 여러분의 프로젝트에 긴 어셈블리 코드가 많이 나타난다면, 어셈블리 코드 작업에 지나치게 빠져있는 것일 수도 있습니다.

두번째로, 의미있는 방법으로 주석문을 달아놓음으로써 루틴을 읽을 수 있도록 유지해야 합니다. 어셈블리 코드는 기본적으로 선형 방식이기 때문에 각각의 문들은 꽤 읽기 쉽습니다. 여러분이 알고리즘을 어떻게 구현하려고 했는지 명확하게 하여 다른 개발자가 즉시 이해할 수 있도록 주석문이 필요합니다. 주석에는 여러분의 코드를 보게 될 사람을 위한 중요한 정보를 써넣어야 합니다. 따라서, 다음과 같은 방식은 분명히 문제가 있습니다:


이런 주석문은 완전히 무의미한데, 명령어 자체만 봐도 edx 레지스터를 증가시킨다는 것이 너무나 뻔하기 때문입니다. 주석문은 명령어 자체만 봐도 알 수 있는 내용을 반복하는 것이 아니라, 알고리즘의 내부 동작을 나타내고 코드와 문맥만으로는 바로 알아보기 힘든 정보를 제공해야 합니다. 따라서, 위의 코드는 예를 들면 아래와 같은 방식으로 주석을 바꿔야 하겠지요.

세번째로, 가능하면 느린 명령어의 사용을 피해야 합니다. 일반적으로, 복잡한 OP코드보다는 단순한 명령어를 사용하는 게 좋습니다. 최신 CPU들에서 복잡한 OP코드들은 마이크로코드로 구현되어 있기 때문입니다. 구글에서 애그너 포그(Agner Fog)의 코드 최적화(code optimisation)에 대한 글들을 검색해보십시오. 프로세서 동작의 관점에서 이 이슈와 다른 많은 이슈들을 다루고 있습니다.

마지막으로 당부할 일반적 원칙은 아마 여러분들도 짐작하실 수 있을텐데요. 여러분의 어셈블리 코드를 완전하게, 그리고 지속적으로 테스트하라는 것입니다. 컴파일러는 어셈블리로 작성된 코드에 대해서는 워닝과 에러 메시지를 상당히 적게 보여주며, 어셈블리 코드의 기본 로직에 대한 피드백도 제시하지 않습니다. 사용되지 않는 변수와 잘못된 포인터 사용에 대해서도 파스칼 코드에서와 달리 쉽게 알아내기 어렵습니다. 따라서, 여러분의 코드에 대해 집중적인 테스트와 디버깅을 할 준비를 해둬야 합니다. 통상적인 파스칼 코드를 디버깅하는 데 비해 더 많은 시간과 노력이 들 것입니다. 이것은 어셈블리 루틴을 가능한 한 짧고 읽기 쉽게 유지해야 하는 또 하나의 이유입니다.

이런 원칙들을 모두 읽은 후에도 델파이/파스칼 프로그램에서 어셈블리 코드를 사용하려고 한다면, 이 시리즈 글들을 읽어보는 것이 좋은 출발이 될 것입니다. 저는 가능한 한 일반적으로 글을 쓰려고 노력했습니다.

1 comment for “델파이에서 어셈블리 사용하기 (0)

답글 남기기

이메일 주소는 공개되지 않습니다.