Go 웹 사이트를 둘러 보았지만 Go의 특별한 빌드 시간에 대한 설명을 찾지 못하는 것 같습니다. 언어 기능 (또는 그 부족), 고도로 최적화 된 컴파일러 또는 다른 제품입니까? 나는 Go를 홍보하려고하지 않습니다. 그냥 궁금 해서요
Go 웹 사이트를 둘러 보았지만 Go의 특별한 빌드 시간에 대한 설명을 찾지 못하는 것 같습니다. 언어 기능 (또는 그 부족), 고도로 최적화 된 컴파일러 또는 다른 제품입니까? 나는 Go를 홍보하려고하지 않습니다. 그냥 궁금 해서요
답변:
의존성 분석.
이동 자주 묻는 질문은 다음과 같은 문장을 포함하는 데 사용 :
Go는 종속성 분석을 쉽게하고 C 스타일 포함 파일 및 라이브러리의 오버 헤드를 피할 수있는 소프트웨어 구성 모델을 제공합니다.
이 문구는 더 이상 FAQ에 없지만이 주제는 Google 토크 대화에서 자세히 설명합니다.이 주제는 C / C ++ 및 Go의 종속성 분석 접근 방식을 비교합니다.
이것이 빠른 컴파일의 주된 이유입니다. 그리고 이것은 의도적으로 설계된 것입니다.
Go 컴파일러가 빠르다 는 것이 아니라 다른 컴파일러가 느리다고 생각 합니다.
C 및 C ++ 컴파일러는 엄청난 양의 헤더를 구문 분석해야합니다. 예를 들어 C ++ "hello world"를 컴파일하려면 18k 라인의 코드를 컴파일해야합니다.
$ cpp hello.cpp | wc
18364 40513 433334
Java 및 C # 컴파일러는 VM에서 실행됩니다. 즉, 무엇이든 컴파일하기 전에 운영 체제가 전체 VM을로드해야하며 바이트 코드에서 네이티브 코드로 JIT 컴파일되어야합니다.
컴파일 속도는 몇 가지 요소에 따라 다릅니다.
일부 언어는 빠르게 컴파일되도록 설계되었습니다. 예를 들어, Pascal은 단일 패스 컴파일러를 사용하여 컴파일되도록 설계되었습니다.
컴파일러 자체도 최적화 할 수 있습니다. 예를 들어, Turbo Pascal 컴파일러는 수작업으로 최적화 된 어셈블러로 작성되었으며 언어 설계와 결합하여 286 클래스 하드웨어에서 작동하는 컴파일러가 매우 빠릅니다. 지금까지도 현대 파스칼 컴파일러 (예 : FreePascal)가 Go 컴파일러보다 빠르다고 생각합니다.
Go 컴파일러가 대부분의 C / C ++ 컴파일러보다 훨씬 빠른 여러 가지 이유가 있습니다.
가장 큰 이유 : 대부분의 C / C ++ 컴파일러는 컴파일 속도 관점에서 볼 때 예외적으로 나쁜 디자인을 나타냅니다. 또한 컴파일 속도 측면에서 C / C ++ 에코 시스템의 일부 (예 : 프로그래머가 코드를 작성하는 편집기)는 컴파일 속도를 염두에두고 설계되지 않았습니다.
가장 큰 이유 : 빠른 컴파일 속도는 Go 컴파일러와 Go 언어에서 의식적인 선택이었습니다.
Go 컴파일러는 C / C ++ 컴파일러보다 간단한 옵티 마이저를 가지고 있습니다.
C ++와 달리 Go에는 템플릿과 인라인 함수가 없습니다. 즉, Go에서 템플릿 또는 함수 인스턴스화를 수행 할 필요가 없습니다.
Go 컴파일러는 하위 수준의 어셈블리 코드를 더 빨리 생성하고 옵티마이 저는 어셈블리 코드에서 작동하는 반면, 일반적인 C / C ++ 컴파일러에서는 최적화 패스가 원본 소스 코드의 내부 표현에서 작동합니다. C / C ++ 컴파일러의 추가 오버 헤드는 내부 표현을 생성해야한다는 사실에서 비롯됩니다.
Go 컴파일러는 사용 된 모든 어셈블리 코드를 거치고 C / C ++와 같은 다른 추가 작업을 수행하기 때문에 Go 프로그램의 최종 링크 (5l / 6l / 8l)는 C / C ++ 프로그램을 링크하는 것보다 느릴 수 있습니다. 링커는하지 않습니다
일부 C / C ++ 컴파일러 (GCC)는 텍스트 형식 (어셈블러에 전달됨)으로 명령어를 생성하는 반면 Go 컴파일러는 이진 형태로 명령어를 생성합니다. 텍스트를 이진으로 변환하려면 추가 작업을 수행해야합니다.
Go 컴파일러는 소수의 CPU 아키텍처 만 대상으로하는 반면 GCC 컴파일러는 많은 수의 CPU를 대상으로합니다.
Jikes와 같은 빠른 컴파일 속도를 목표로 설계된 컴파일러는 빠릅니다. 2GHz CPU에서 Jikes는 초당 20000+ 라인의 Java 코드를 컴파일 할 수 있습니다 (그리고 증분 컴파일 모드는 훨씬 더 효율적입니다).
컴파일 효율성은 주요 디자인 목표였습니다.
마지막으로, 빠른 속도를 목표로합니다. 단일 컴퓨터에서 큰 실행 파일을 빌드하는 데 최대 몇 초가 걸립니다. 이러한 언어 적 문제를 해결하기 위해 이러한 목표를 달성하기 위해서는 표현력이 있지만 가벼운 유형의 시스템; 동시성 및 가비지 콜렉션; 엄격한 의존성 사양; 등등.자주하는 질문
언어 FAQ는 구문 분석과 관련된 특정 언어 기능과 관련하여 매우 흥미 롭습니다.
둘째,이 언어는 분석하기 쉽게 설계되었으며 기호 테이블없이 구문 분석 할 수 있습니다.
aType
변수 참조 인 것처럼 식에 대한 구문 분석 트리를 만들고 나중에 의미 분석 단계에서 의미있는 오류가 인쇄되지 않는 것을 알게됩니다.
컴파일러의 변환 효율성에 대한 좋은 테스트는 자체 컴파일입니다. 주어진 컴파일러가 자체 컴파일하는 데 얼마나 걸립니까? C ++의 경우 시간이 오래 걸립니다 (시간?). 이에 비해 Pascal / Modula-2 / Oberon 컴파일러는 하나 미만으로 자체 컴파일됩니다 최신 머신에서 초 [1].
Go는 이러한 언어에서 영감을 얻었지만 이러한 효율성의 주요 원인은 다음과 같습니다.
효율적인 스캔 및 구문 분석을 위해 수학적으로 명확하게 정의 된 구문입니다.
C / C ++에서와 같이 독립적 인 컴파일과 달리 헤더 파일을 불필요하게 다시 읽고 다른 모듈을 다시 컴파일하는 것을 피하기 위해 모듈 경계에 걸쳐 종속성 및 유형 검사 와 함께 별도의 컴파일 을 사용하는 형식 안전하고 정적으로 컴파일 된 언어 컴파일러는 이러한 교차 모듈 검사를 수행하지 않습니다 (따라서 간단한 한 줄 "hello world"프로그램의 경우에도 모든 헤더 파일을 반복해서 다시 읽어야합니다).
효율적인 컴파일러 구현 (예 : 단일 패스, 재귀 하향 하향식 파싱)-물론 위의 1 및 2 지점에서 크게 도움이됩니다.
이러한 원칙은 1970 년대와 1980 년대에 Mesa, Ada, Modula-2 / Oberon 및 기타 여러 언어로 이미 알려져 있으며 완전히 구현되었으며 현재는 (2010 년대) Go (Google)와 같은 현대 언어로의 길을 찾고 있습니다. , Swift (Apple), C # (Microsoft) 및 기타 여러 가지.
이것이 곧 예외가 아닌 표준이 되길 바랍니다. 거기에 가려면 두 가지 일이 발생해야합니다.
먼저, Google, Microsoft 및 Apple과 같은 소프트웨어 플랫폼 제공 업체는 애플리케이션 개발자가 새로운 코드 작성 방법을 사용 하도록 권장 하면서 기존 코드 기반을 재사용 할 수 있도록해야합니다. 이것이 바로 동일한 런타임 환경을 사용하기 때문에 Objective-C와 공존 할 수있는 Swift 프로그래밍 언어와 관련하여 Apple이 시도하는 것입니다.
둘째, 기본 소프트웨어 플랫폼 자체는 결국 이러한 원칙을 사용하여 시간이 지남에 따라 다시 작성되어야하며 동시에 모듈 식을 모 놀리 식으로 만들도록 모듈 계층 구조를 재 설계해야합니다. 이것은 물론 굉장한 작업이며 10 년 동안 더 잘 할 수 있습니다 (실제로 그렇게 할 수있을 정도로 용기가 있다면-Google의 경우 전혀 확실하지 않습니다).
어쨌든 언어 채택을 주도하는 플랫폼이며 다른 방식은 아닙니다.
참고 문헌 :
[1] http://www.inf.ethz.ch/personal/wirth/ProjectOberon/PO.System.pdf , 6 페이지 : "컴파일러가 약 3 초 안에 컴파일됩니다". 이 인용문은 25MHz의 클럭 주파수에서 실행되고 1MByte의 메인 메모리를 갖춘 저가의 Xilinx Spartan-3 FPGA 개발 보드를위한 것입니다. 이것으로부터 1GHz 이상의 클럭 주파수와 몇 GBytes의 주 메모리 (즉, Xilinx Spartan-3 FPGA 보드보다 몇 배나 더 강력한)에서 실행되는 최신 프로세서의 경우 1 초 미만으로 쉽게 추정 할 수 있습니다 . I / O 속도를 고려할 때도 마찬가지입니다. Oberon이 2-4MB의 주 메모리를 가진 25MHz NS32X32 프로세서에서 실행 된 1990 년에 이미 컴파일러는 몇 초 만에 자체 컴파일되었습니다. 실제로 기다리는 개념컴파일러가 컴파일주기를 마치기 전까지는 Oberon 프로그래머에게는 완전히 알려지지 않았습니다. 일반적인 프로그램의 경우 컴파일러가 방금 트리거 한 컴파일을 완료하기를 기다리는 것보다 컴파일 명령을 트리거 한 마우스 버튼에서 손가락을 제거하는 데 항상 더 많은 시간이 걸렸습니다. 거의 제로에 가까운 대기 시간으로 즉각적인 만족감이었습니다. 그리고 당시에 사용 가능한 최고의 컴파일러와 완전히 동일하지는 않지만 제작 된 코드의 품질은 대부분의 작업에 상당히 좋았으며 일반적으로 상당히 수용 가능했습니다.
Go는 빠르도록 설계되었으며 잘 보여줍니다.
GO는 그러한 기능을 가진 유일한 언어는 아니지만 (모듈은 현대 언어의 표준 임), 잘 해냈습니다.
Alan Donovan과 Brian Kernighan의 " The Go Programming Language " 책에서 인용 한 내용 :
Go 컴파일은 처음부터 빌드 할 때도 대부분의 다른 컴파일 된 언어보다 훨씬 빠릅니다. 컴파일러 속도에는 세 가지 주요 이유가 있습니다. 먼저, 모든 가져 오기는 각 소스 파일의 시작 부분에 명시 적으로 나열되어야하므로 컴파일러는 종속성을 판별하기 위해 전체 파일을 읽고 처리 할 필요가 없습니다. 둘째, 패키지의 종속성은 유향 비순환 그래프를 형성하며 사이클이 없기 때문에 패키지를 개별적으로 또는 병렬로 컴파일 할 수 있습니다. 마지막으로 컴파일 된 Go 패키지의 오브젝트 파일은 패키지 자체뿐만 아니라 종속성에 대한 내보내기 정보도 기록합니다. 패키지를 컴파일 할 때 컴파일러는 각 가져 오기에 대해 하나의 객체 파일을 읽어야하지만 이러한 파일을 넘어서는 안됩니다.
컴파일의 기본 아이디어는 실제로 매우 간단합니다. 재귀 강하 파서는 원칙적으로 I / O 바운드 속도로 실행될 수 있습니다. 코드 생성은 기본적으로 매우 간단한 프로세스입니다. 기호 테이블과 기본 유형 시스템은 많은 계산이 필요한 것이 아닙니다.
그러나 컴파일러 속도를 늦추는 것은 어렵지 않습니다.
다중 레벨 포함 지시문, 매크로 정의 및 조건부 컴파일 이있는 전 처리기 단계가있는 경우 ,이를로드하는 것은 어렵지 않습니다. (예를 들어, Windows 및 MFC 헤더 파일을 생각하고 있습니다.) 사전 컴파일 된 헤더가 필요한 이유입니다.
생성 된 코드를 최적화하는 측면에서 해당 단계에 추가 할 수있는 처리량에는 제한이 없습니다.
구문이 매우 쉬우므로 (분석하고 파싱하기 때문에) 간단하게 (내 자신의 말로)
예를 들어, 유형 상속이 없다는 것은 새로운 유형이 기본 유형에 의해 부과 된 규칙을 따르는 지 확인하기위한 문제가없는 분석을 의미합니다.
예를 들어이 코드 예제에서 : "인터페이스" 컴파일러는 해당 유형을 분석하는 동안 지정된 유형 이 지정된 인터페이스를 구현 하는지 확인하지 않습니다 . 사용 (및 사용 된 경우)까지만 점검이 수행됩니다.
다른 예에서, 컴파일러는 변수를 선언하고 사용하지 않는지 알려줍니다 (또는 반환 값을 보유하고 있고 그렇지 않은 경우)
다음은 컴파일되지 않습니다 :
package main
func main() {
var a int
a = 0
}
notused.go:3: a declared and not used
이러한 종류의 시행과 원칙 은 결과 코드를 더 안전하게 만들어 주며, 컴파일러는 프로그래머가 할 수있는 추가적인 검증을 수행 할 필요가 없습니다.
이 모든 세부 사항은 언어를 구문 분석하기 쉽게 만들어 컴파일 속도가 빠릅니다.
다시 말하지만, 내 말로.
또 뭐요?