C++Builder  |  Delphi  |  FireMonkey  |  C/C++  |  Free Pascal  |  Firebird
볼랜드포럼 BorlandForum
 경고! 게시물 작성자의 사전 허락없는 메일주소 추출행위 절대 금지
분야별 포럼
C++빌더
델파이
파이어몽키
C/C++
프리파스칼
파이어버드
볼랜드포럼 홈
헤드라인 뉴스
IT 뉴스
공지사항
자유게시판
해피 브레이크
공동 프로젝트
구인/구직
회원 장터
건의사항
운영진 게시판
회원 메뉴
북마크
볼랜드포럼 광고 모집

자유게시판
세상 살아가는 이야기들을 나누는 사랑방입니다.
[21638] RAD XE3 (Confidential pre-release version) 와 LLVM에 대해서
빌더(TWx) [builder] 6121 읽음    2012-08-19 17:57
헤드라인 뉴스에 글쓰기 권한이 없어서 여기다 적습니다.

헤드라인 뉴스에 보면 "XE3, 네이티브 버리고 LLVM 대변신?" 이란 타이틀로 글이 올라와 있던데...

네이티브를 버리고 LLVM을 선택하므로써, 이전 부터 최고의 네이티브 성능을 자랑해온 델파이나 C++빌더가
코드의 질적인 후퇴를 가져오는게 아닌가 하는 것을 글의 결론으로 삼았던데요..

그 글은 다소 오해의 소지가 있어서 몇자 적어 봅니다.

컴파일러는 렉서, 파서, 코드최적화, 타겟 바이너리 생성등의 핵심 요소들로 구성되게 되죠.

LLVM은 그 중에서 코드최적화와 타겟 코드 생성 부분 등이 여러 모듈들로 나뉘어져 구현되어 있고
AST 트리를 생성하는 프론트 엔드인 clang은 LLVM 에 별도로 추가해서 사용하는 패키지일 뿐입니다.

clang이 LLVM 모듈을 이용하기 때문에 LLVM의 tools 디렉토리에 clang 패키지를 별도로 추가로 설치해서
전체 패키지를 컴파일 하게 됩니다.

LLVM의 핵심은 중간코드를 이용해서 글로벌 옵티마이징을 처리한 후, 타켓 CPU 아키텍쳐에 맞는 머쉰코드를 생성하는 거죠.

코드 최적화를 고려하지 않는 컴파일러를 만든다면...중간코드 단계를 거칠 필요 없이 AST 트리를 생성할 때 바로 타겟 머쉰코드를
만들어 내도록 프론트엔드를 구현하면 됩니다.

그러나... 중간코드를 이용한 글로벌 옵티마이징 단계를 밟지 않고..
AST 트리를 생성할 때 프론트엔드에서 바로 타겟 CPU의 머쉰코드를 생성해 내는 방식은 코드최적화에 제한을 받게 됩니다.

그래서...
코드 최적화에 중점을 두고 컴파일러를 만들 때는 ( 요즘의 지능적인 최신 컴파일러들)...
CPU 아키텍쳐에 종속적이지 않은 중간코드를 이용해서 이를 바탕으로 전체적인 글로벌 옵티마이징을 처리한 후에..
해당하는 CPU의 인스트럭션 셋을 매핑해서  머쉰코드를 에미트 하도록 컴파일러를 구현하기 마련이죠. 글로벌 옵티마이징
구현 부분이 CPU 아키텍쳐에 종속적이지도 않기 때문에 멀티 타겟 CPU도 고려한다면 이런 방식이 유리하기도 하고요.

그리고 LLVM 패키지를 만들 때, JIT 컴파일러도 서브 파트로 구현해 놓았기 때문에 가상머쉰이란 얘기도 나오는 것일 뿐...
JIT 컴파일러(버추얼머쉰)는 전체 LLVM 패키지에서 한 부분일 뿐입니다. .Net 과 같은 유형의 JIT Execution 시스템을 구현할 게
아니라면... 네이티브 컴파일러 에서는 의미도 없고, 사용할 필요가 없는 서브 파트에 불과 합니다.

기실 어찌보면 프론트엔드 보다 코드 최적화 파트가 구현하는 게 더 까다롭고 힘든 부분이기도 한데요.
프론트엔드는 현재 나와있는 여러가지 "파서 제너레이터" 툴을 이용하면 되기 때문이죠.

현재 RAD XE3 (Confidential pre-release version)에는 64비트 C++ 컴파일러가 들어있지 않습니다.

RTL 소스 디렉토리의 빌드 프로젝트 구성을 보면 64비트 버전의 RTL 바이너리를 생성하기 위해서 엠바카데로 내부에서
clang을 이용하고 있다는 것을 알수 있지만, 64비트 C++ 컴파일러는 빠져 있습니다. 이번 베포 버전에서는 32비트 버전의
bcc32 만 들어 있습니다.

향후에도 32비트는 기존의 bcc32를 그대로 이용하고, 64비트만 clang을 이용 하는 게 아닐까 하는 생각이 들기도???.

64비트 C++ 컴파일러 지원에 대한 얘기가 나온지도 벌써 몇년이 지났는데 여태 뭐하고 있다가 이제와서
오픈소스 컴파일러에 묻어 가겠다는 걸 보면 엠바카데로 얘네들 참 한심한 친구들이네요. --;


RAD XE3 에 이르기 까지 엠바카데로의 컴파일러는 코드 최적화 수준이 빵점 입니다.

팩토리얼을 구하는 다음 코드를 예로 들어 볼까요...

int  factorial( int X )
{
     if (X == 0)
            return 1;
    
     return X * factorial ( X - 1 );
}

구현을 쉽고 용이하게 하기 위해서 재귀호출을 사용하고 있는데요...

엠바카데로 컴파일러가 생성한 기계어 코드 부터 보죠...

Int factorial(int X)
{
@1:
    push      ebp
    mov       ebp,esp
    push      ebx
    mov       ebx,dword ptr [ebp+8]
    test      ebx,ebx
    jne       short @2
    mov       eax,1
@5:
    pop       ebx
    pop       ebp
    ret
@2:
    lea       edx,dword ptr [ebx-1]
    push      edx
    call      @@factorial$qi
    imul      ebx
    pop       ecx
@4:
@3:
                pop       ebx
    pop       ebp
    ret

릴리즈 모드로 컴파일 한 건데요. 생성된 기계어 코드를 보면 쉽게 알수 있듯이
코드 최적화가 개판 입니다.


반면에... 아래와 같이...
LLVM이 생성한 머쉰코드를 보면...

int factorial(int X)
    movl    $1, %eax
    movl    4(%esp), %ecx
    testl    %ecx, %ecx
    je    .LBB0_2
.LBB0_1:     imull    %ecx, %eax
    decl    %ecx
    jne    .LBB0_1
.LBB0_2:     ret

LLVM이 생성한 머쉰코드를 보면 코드최적화가 대단하죠.
루프인덕션은 물론... 기계어 코드를 잘 살펴보면 알수 있듯이..
스텍프레임이 반복적으로 사용되기 때문에 퍼포먼스에 불리한 재귀호출 까지 제거해 버렸습니다.

재귀호출 구조를 이용하면 알고리즘 구현하기가 쉽지만, 스텍프레임이 재귀적으로 반복 사용되는 구조이기 때문에
비재귀 구조보다 퍼포먼스가 떨어지죠. 그래서 재귀구조가 제거된 같은 기능의 비재귀 함수를 구현하기 위해선 번거롭게
머리를 써야 하는데... LLVM을 이용한 clang의 경우 코드 최적화 과정에서 알아서 재귀구조를 제거해 주고있습니다.


엠바카데로의 컴파일러의 코드 최적화는 빵점.
..


Lyn님이 단 댓글도 있고해서 추가로 몇가지 덧붙입니다.


1.
Lyn [tohnokanna]   2012-08-19 18:50 X
컴파일러 못만드는걸 인정한꼴밖에 안되는듯 ... 빌더는 애초에 느려 터졋지만...
제가 퍼포먼스 볼때마다 속이 다 뒤집어지는 gcc보다도 느린 LLVM 조차 빌더 컴파일러보다 빠른거 보면 이건 뭐 답이없음.

2.
Lyn [tohnokanna]   2012-08-19 18:51 X
근데 과연... 그 컴파일러 빵점인 애들이... 델파이랑 연결하느라 여기저기 확장해놓은 빌더 문법을 과연 제대로 clang에 연결 할까...? 가 의문

3.
Lyn [tohnokanna]   2012-08-19 18:53 X
gcc나 LLVM 같은 중간언어를 거쳐서 최종결과물을 만들어 내면 분명 이식하기 좋고 여러가지 장점도 있겠지만 정말 그 플랫폼에 극도로 특화된 바이너리는 생성하기 힘들긴 하겠지만요...


답변 입니다.

1.
토큰을 정의해 주면 렉서 코드를 생성해주는 렉서 제너레이터 툴도 있지만,  원시소스 로 부터 토큰을 뽑아내는 속도를 높이기
위해서 "렉서 제네레이터" 툴을 이용하지 않고 대부분의 컴파일러 들이 직접 손으로 렉서 부분 코드를 구현 합니다.

그러나 파서는 손으로 직접 구현하기가 대단히 복잡하기 때문에 파서 제네이터 툴을 이용하기 마련인데요.
clang은 파서 제네레이터에 의해서 생성된 코드 배이스 기반의 프론트 엔드가 아니고... clang은 렉서는 물론 파서도 직접 손으로
구현하고 있습니다.

"파서 제네레이터" 툴을 이용할 경우 생성되는 DAG 테이블 자체만의 크기도 엄청나서 메모리도 많이 차지하고 일반화된 파싱
패턴 코드베이스 이기 때문에 손으로 구현한 파서 보다 속도에서도 불리 합니다. 직접 C++과 템플레이트를 이용해서 손으로
구현한 clang 프론트엔드가... "파서 제네레이터" 코드 배이스로 구현된 gcc 프론트엔드 보다 파싱 속도가 훨씬 빠릅니다.
그리고 LLVM은 프론트 엔드가 아닙니다.

2.
clang 프론트엔드 구조가 전체 소스를 수정할 필요 없이 디피니션 파일을 통해서 신텍스와 시멘틱스 등을 재정의 할수 있는
구조로 되어 있기 때문에 clang 에서 VCL을 지원하게 확장하는 것이 난해한 작업은 아닙니다.

3.
글로벌 옵티마이징은 중간코드를 대상으로 해서 이루어지 지게 되고, 최적화 처리된 중간코드를 해당 CPU 아키텍쳐의
인스트럭션 셋으로 매핑해서 에미트 하면 그만이기 때문에, 명령어 Sets 에서 특화된 명령으로 매핑하도록 인스트럭션 매퍼에
지시하면 그만일 뿐, 중간코드를 이용하기 때문에 특정 플렛폼에 특화된 바이너리를 생성하지 못하는 게 아닙니다.
...
Lyn [tohnokanna]   2012-08-19 18:50 X
컴파일러 못만드는걸 인정한꼴밖에 안되는듯 ... 빌더는 애초에 느려 터졋지만...

제가 퍼포먼스 볼때마다 속이 다 뒤집어지는 gcc보다도 느린 LLVM 조차 빌더 컴파일러보다 빠른거 보면 이건 뭐 답이없음... 캐리어가야됨 ㅡ.ㅡ
Lyn [tohnokanna]   2012-08-19 18:51 X
근데 과연... 그 컴파일러 빵점인 애들이... 델파이랑 연결하느라 여기저기 확장해놓은 빌더 문법을 과연 제대로 clang에 연결 할까...? 가 의문
Lyn [tohnokanna]   2012-08-19 18:53 X
gcc나 LLVM 같은 중간언어를 거쳐서 최종결과물을 만들어 내면 분명 이식하기 좋고 여러가지 장점도 있겠지만 정말 그 플랫폼에 극도로 특화된 바이너리는 생성하기 힘들긴 하겠지만요...

문제는 그런데도 빌더가 느리다는거 Orz
빌더(TWx) [builder]   2012-08-19 19:46 X
세가지 댓글에 대한 답변은 본문 추가로 대신합니다.

+ -

관련 글 리스트
21638 RAD XE3 (Confidential pre-release version) 와 LLVM에 대해서 빌더(TWx) 6121 2012/08/19
Google
Copyright © 1999-2015, borlandforum.com. All right reserved.