Julia의 웹 페이지를 살펴보면 여러 알고리즘에서 여러 언어의 벤치 마크를 볼 수 있습니다 (아래의 타이밍). 원래 C로 작성된 컴파일러의 언어가 C 코드보다 성능이 우수한 방법은 무엇입니까?
Julia의 웹 페이지를 살펴보면 여러 알고리즘에서 여러 언어의 벤치 마크를 볼 수 있습니다 (아래의 타이밍). 원래 C로 작성된 컴파일러의 언어가 C 코드보다 성능이 우수한 방법은 무엇입니까?
답변:
컴파일러 구현과 컴파일러 출력 사이에는 필요한 관계가 없습니다. 당신은 가장 일반적인 구현 매우 느린 파이썬이나 루비 같은 언어의 컴파일러를 쓸 수, 그리고 컴파일러는 출력이 높은 있기 때문에, 실행하는 데 시간이 오래 걸릴 것 C.에게 컴파일러 자체를 능가 할 수있는 기계 코드를 최적화 할 수 는코드는 느린 언어로 작성됩니다. (더 정확하게 구현하려면 느린 구현 언어로 작성하십시오. Raphael이 의견에서 지적한 것처럼 언어는 본질적으로 빠르거나 느리지 않습니다. 아래 에서이 아이디어를 확장합니다.) 컴파일 된 프로그램은 그 속도만큼 빠릅니다. 자체 구현 허용 —Fortran 컴파일러와 동일한 머신 코드를 생성하는 컴파일러를 Python으로 작성할 수 있으며 컴파일하는 데 시간이 오래 걸리더라도 컴파일 된 프로그램은 Fortran만큼 빠릅니다.
통역사에 대해 이야기하고 있다면 다른 이야기입니다. 해석하는 프로그램이 실행되는 동안 통역사가 실행되어야하므로 통역사가 구현되는 언어와 해석 된 코드의 성능간에 연결이 있습니다. 인터프리터가 구현되는 언어보다 빠르게 실행되는 인터프리터 언어를 만들려면 영리한 런타임 최적화가 필요하며 최종 성능은 코드 종류가 이러한 종류의 최적화에 얼마나 적합한 지에 따라 달라질 수 있습니다. Java 및 C #과 같은 많은 언어는 인터프리터의 장점과 컴파일러의 장점을 결합한 하이브리드 모델과 함께 런타임을 사용합니다.
구체적인 예로서, 파이썬을 더 자세히 살펴 보자. 파이썬에는 여러 가지 구현이 있습니다. 가장 일반적인 것은 C로 작성된 바이트 코드 인터프리터 인 CPython입니다. PyPy도 있는데,이 언어는 RPython이라는 특수한 파이썬 방언으로 작성되며 JVM과 같은 하이브리드 컴파일 모델을 사용합니다. PyPy는 대부분의 벤치 마크에서 CPython보다 훨씬 빠릅니다. 런타임에 코드를 최적화하기 위해 모든 종류의 놀라운 트릭을 사용합니다. 그러나 PyPy가 실행하는 Python 언어는 성능에 영향을 미치지 않는 몇 가지 차이점을 제외하고 CPython이 실행 하는 것과 동일한 Python 언어 입니다.
포트란을 위해 파이썬 언어로 컴파일러를 작성했다고 가정하자. 우리 컴파일러는 GFortran과 동일한 머신 코드를 생성합니다. 이제 포트란 프로그램을 컴파일합니다. 우리는 CPython 위에서 컴파일러를 실행하거나 PyPy에서 실행할 수 있습니다. PyPy는 Python으로 작성되었으며 두 구현 모두 동일한 Python 언어를 실행하기 때문입니다. 우리가 알게 될 것은 CPython에서 컴파일러를 실행하고 PyPy에서 실행 한 다음 GFortran으로 동일한 포트란 소스를 컴파일하면 세 번 모두 동일한 머신 코드를 얻게되므로 컴파일 된 프로그램은 항상 실행된다는 것입니다 거의 같은 속도로 그러나 컴파일 된 프로그램을 작성하는 데 걸리는 시간은 다릅니다. CPython은 PyPy보다 시간이 오래 걸리고 PyPy는 GFortran보다 시간이 더 걸릴 것입니다.
Julia 웹 사이트의 벤치 마크 테이블을 스캔하면 통역사 (Python, R, Matlab / Octave, Javascript)에서 실행되는 언어가 C를 능가하는 벤치 마크가없는 것처럼 보입니다. 이것은 일반적으로 내가 기대하는 것과 일치합니다. 비록 파이썬의 고도로 최적화 된 Numpy 라이브러리 (C와 Fortran으로 작성 됨)로 작성된 코드가 유사한 코드의 가능한 C 구현을 치고 있다고 상상할 수 있습니다. C보다 크거나 같은 언어는 컴파일 중이거나 (Fortran, Julia ) 부분 컴파일 (Java 및 아마도 LuaJIT)이있는 하이브리드 모델을 사용 중입니다. PyPy는 하이브리드 모델도 사용하므로 CPython 대신 PyPy에서 동일한 Python 코드를 실행하면 실제로 일부 벤치 마크에서 C를 이길 수 있습니다.
사람이 만든 기계가 사람보다 어떻게 더 강해질 수 있습니까? 이것은 정확히 같은 질문입니다.
대답은 컴파일러의 출력이 컴파일러를 구현하는 데 사용 된 언어가 아니라 해당 컴파일러가 구현 한 알고리즘에 달려 있다는 것입니다. 매우 효율적인 코드를 생성하는 매우 느리고 비효율적 인 컴파일러를 작성할 수 있습니다. 컴파일러에는 특별한 것이 없습니다 : 그것은 단지 입력을 받고 출력을 생성하는 프로그램 일뿐입니다.
제 생각에는 직업을위한 도구를 선택할 때 해롭다는 일반적인 가정에 반하여 한 가지 포인트를 만들고 싶습니다.
느리거나 빠른 언어는 없습니다. ¹
CPU가 실제로 무언가를하는 길에는 많은 단계 ²가 있습니다.
모든 단일 항목 은 측정 할 수있는 실제 런타임에, 때로는 무겁게 기여합니다. 다른 "언어"는 다른 것들에 초점을 맞춘다 ³.
몇 가지 예를 들기 위해.
1 대 2-4 : 평균 C 프로그래머는 정확성과 효율성 측면에서 일반 Java 프로그래머보다 훨씬 나쁜 코드를 생성 할 수 있습니다. 프로그래머가 C에서 더 많은 책임 을 지기 때문 입니다.
1/4 vs 7 : C와 같은 저수준 언어에서는 특정 CPU 기능 을 프로그래머로 활용할 수 있습니다 . 고급 언어에서는 컴파일러 / 인터프리터 만 대상 CPU 를 알고 있는 경우에만 그렇게 할 수 있습니다 .
1/4 vs 5 : 메모리 아키텍처를 가장 잘 사용하기 위해 메모리 레이아웃을 제어하거나 제어해야합니까? 어떤 언어는 당신이 그것을 통제 할 수있게 해줍니다.
2/4 vs 3 : 해석 된 파이썬 자체는 엄청나게 느리지 만 과학 컴퓨팅을 위해 고도로 최적화되고 기본적으로 컴파일 된 라이브러리에 대한 인기있는 바인딩이 있습니다 . 그래서 파이썬에서 어떤 일을하는 것은 빠른 작업의 대부분은이 라이브러리에 의해 수행되는 경우, 결국.
2 대 4 : 표준 Ruby 인터프리터는 상당히 느립니다. 반면에 JRuby는 매우 빠를 수 있습니다. 다른 컴파일러 / 인터프리터를 사용하는 것과 동일한 언어가 빠릅니다.
1/2 vs 4 : 컴파일러 최적화를 사용하여 간단한 코드를 매우 효율적인 기계 코드로 변환 할 수 있습니다.
결론은, 당신이 발견 한 벤치 마크는 적어도 당신이 포함하는 테이블로 요약했을 때별로 의미가 없다는 것입니다. 관심있는 모든 것이 실행 시간 일지라도 프로그래머에서 CPU까지 전체 체인 을 지정해야합니다 . 요소를 바꾸면 결과가 크게 바뀔 수 있습니다.
분명히 이것은 컴파일러 (4 단계)가 작성된 언어가 퍼즐의 한 조각 일 뿐이며 전혀 관련이 없음을 보여주기 때문에 질문에 대답합니다 (다른 답변 참조).
실행 시간 효율성, 메모리 효율성, 개발자 시간, 보안, 안전, (확실한가?) 정확성, 도구 지원, 플랫폼 독립성 등 ...
완전히 다른 목표를 위해 설계되었지만 언어를 하나의 메트릭으로 비교하는 것은 큰 오류입니다.
최적화에 대해 잊어 버린 것이 하나 있습니다.
C보다 성능이 뛰어나 C에 대한 논쟁이 오래 걸렸다. 잘못된 토론을 분리 : C와 포트란으로 같은 코드를 작성했으며 (테스터가 생각한대로) 동일한 데이터를 기반으로 성능을 테스트했습니다. 문제는이 언어가 다르고 C는 포인터 별칭을 허용하지만 fortran은 그렇지 않습니다.
따라서 코드는 동일하지 않았고 C 테스트 파일에는 __restrict가 없었습니다. 파일을 다시 작성하여 컴파일러에 포인터를 최적화 할 수 있다고 알려 주면 런타임이 비슷해졌습니다.
여기서 요점은 일부 최적화 기술이 새로 작성된 언어에서 더 쉬워 지거나 합법화되기 시작한다는 것입니다.
두 번째로 VM은 실행 중에 압력 테스트를 수행 할 수 있으므로 압력 코드를 가져와 최적화하거나 런타임 중에 사전 계산할 수 있습니다. 미리 컴파일 된 C 프로그램은 일반적인 머신 제품군에 대한 일반적인 버전의 실행 파일이 어디에 있는지 (또는 대부분) 예상하지 않습니다.
이 테스트에는 JS도 있으며 V8보다 빠른 VM이 있으며 일부 테스트에서는 C보다 빠르게 수행됩니다.
나는 그것을 확인했고, C 컴파일러에서는 아직 사용할 수없는 독특한 최적화 기술이있었습니다.
C 컴파일러는 한 번에 전체 코드를 정적으로 분석하고 주어진 플랫폼을 따라 행진하며 메모리 정렬 문제를 해결해야합니다.
VM은 코드의 일부를 최적화 된 어셈블리로 음역하고 실행했습니다.
Julia에 대해-내가 확인했을 때 AST 코드에서 작동합니다. 예를 들어 GCC는 최근 에이 단계를 건너 뛰고 최근에 정보를 얻었습니다. 이 외에도 다른 제약 조건과 VM 기술이 약간 설명 할 수 있습니다.
예 : 간단한 루프를 사용하여 변수에서 시작 끝점을 가져 와서 변수의 일부를 런타임에 알고있는 계산에로드합니다.
C 컴파일러는 레지스터에서 로딩 변수를 생성합니다.
그러나 런타임에 이러한 변수는 실행을 통해 상수로 알려지고 처리됩니다.
따라서 레지스터에서 변수를로드하는 대신 (및 변경 할 수 있기 때문에 캐싱을 수행하지 않으며 정적 분석에서는 명확하지 않습니다) 변수는 상수처럼 완전히 처리되고 접 히고 전파됩니다.
라파엘의 대답에 의해 잘 설명 된 것처럼, 이전의 답변은 대부분 실제적인 각도에서 설명을 제공하지만 , 질문은 의미 가있는만큼 이해 가됩니다 .
이 답변에 덧붙여 요즘에는 C 컴파일러가 C로 작성되어 있습니다. 물론 Raphael에 따르면 출력과 성능은 실행중인 CPU에 따라 달라질 수 있습니다. 그러나 그것은 또한 컴파일러가 수행하는 최적화의 양에 달려 있습니다. C에 대해 C에 대해 더 나은 최적화 컴파일러를 작성하면 (이전 컴파일러로 실행하여 컴파일 할 수 있음) C를 이전보다 더 빠른 언어로 만드는 새로운 컴파일러를 얻게됩니다. 그래서, C의 속도는 무엇인가? 새로운 컴파일러 자체를 두 번째 단계로 컴파일 할 수 있으므로 동일한 객체 코드를 제공하면서도 더 효율적으로 컴파일 할 수 있습니다. 그리고 전체 고용 정리 는 그들이 그러한 개선의 끝이 아님을 보여줍니다 (포인터에 대한 Raphael의 감사).
그러나 나는 근본적인 개념들, 특히 관점에 대한 것들과 운영상의 관점을 잘 보여주기 때문에 문제를 공식화하는 것이 가치가 있다고 생각합니다.
인수를 구체화하면 아마도 컴파일러가 좋은 효율성을 갖기를 원할 것이므로 적절한 시간에 변환을 수행 할 수 있습니다. 따라서 컴파일러 프로그램의 성능은 사용자에게 중요하지만 의미에 영향을 미치지 않습니다. 일부 컴파일러의 이론적 복잡성이 예상보다 훨씬 높을 수 있기 때문에 성능을 말하는 것입니다.
이것은 차이점을 설명하고 실용적인 응용 프로그램을 보여줍니다.
하여 블룸의 속도 향상 정리 BASIC 해석 첫 번째 PC의 실행에 동일한위한 프로그램보다 느리게 실행되는 매우 빠른 컴퓨터 / 컴파일러 조합에 작성되고 실행되는 프로그램이있다. "가장 빠른 언어"는 없습니다. 당신이 말할 수있는 것은 여러 언어로 동일한 알고리즘 을 작성하면 (구현 : 언급 된 것처럼 많은 다른 C 컴파일러가 있고, 심지어 유능한 C 인터프리터를 보았습니다) 각각에서 더 빠르거나 느리게 실행될 것입니다 .
"항상 더 느린"계층 구조는있을 수 없습니다. 이것은 여러 언어에 유창한 모든 사람들이 알고있는 현상입니다. 각 프로그래밍 언어는 특정 유형의 응용 프로그램을 위해 설계되었으며, 더 많이 사용되는 구현은 해당 유형의 프로그램에 맞게 사랑스럽게 최적화되었습니다. 필자는 Perl로 작성된 문자열로 장난하는 프로그램이 C로 작성된 동일한 알고리즘을 능가 할 것이라고 확신하지만 C의 큰 정수 배열에서 munching하는 프로그램은 Perl보다 빠릅니다.
원래 줄로 돌아가 봅시다. "C로 작성된 컴파일러의 언어가 C보다 더 빠를 수있는 방법은 무엇입니까?" 나는 이것이 실제로 C라고 쓰여진 Julia로 작성된 프로그램이 C로 작성된 프로그램보다 더 빠를 수있는 방법이라고 말할 수 있다고 생각합니다. 특히 Julia로 작성된 "mandel"프로그램이 C로 작성된 동등한 "mandel"프로그램 실행 시간의 87 %에서 어떻게 실행될 수 있습니까?
Babou의 논문은 지금까지이 질문에 대한 정답입니다. 지금까지의 다른 모든 답변은 다른 질문에 다소 대답하고 있습니다. babou의 텍스트의 문제점은 "컴파일러 란 무엇입니까?"라는 많은 단락의 이론적 설명이 원래 포스터가 이해하기 어려울 것이라는 용어로 작성되었다는 것입니다. "의미 론적", "직설적으로", "실현", "계산 가능"등의 단어로 언급 된 개념을 이해하는 사람은 이미 그 질문에 대한 답을 알고있을 것입니다.
더 간단한 대답은 C 코드 나 Julia 코드가 시스템에서 직접 실행할 수 없다는 것입니다. 둘 다 번역되어야하고 번역 프로세스는 실행 가능한 기계 코드가 느리거나 빠를 수 있지만 여전히 동일한 최종 결과를 생성하는 많은 방법을 도입합니다. C와 Julia는 모두 컴파일을 수행하는데, 이는 다른 형식으로의 일련의 번역을 의미합니다. 일반적으로 사람이 읽을 수있는 텍스트 파일은 내부 표현으로 변환 된 다음 컴퓨터가 직접 이해할 수있는 일련의 명령으로 작성됩니다. 일부 언어의 경우 그 이상이 있으며 Julia는 "JIT"컴파일러를 가지고 있습니다. 즉, 전체 번역 프로세스가 전체 프로그램에 대해 한 번에 모두 발생하지 않아도됩니다. 그러나 모든 언어의 최종 결과는 더 이상 번역 할 필요가없는 머신 코드입니다. CPU로 직접 보내서 무언가를 할 수있는 코드. 결국, 이것은 "계산"이며, CPU가 원하는 응답을 얻는 방법을 알려주는 방법은 여러 가지가 있습니다.
"plus"와 "multiply"연산자를 가진 프로그래밍 언어와 "plus"만있는 다른 언어를 상상할 수 있습니다. 계산에 곱셈이 필요한 경우 CPU가 두 가지를 모두 직접 수행 할 수 있기 때문에 한 언어가 "느리게"진행되지만 5 * 5를 곱할 필요성을 표현할 방법이 없으면 "5를 작성해야합니다. + 5 + 5 + 5 + 5 " 후자는 동일한 답변에 도달하는 데 더 많은 시간이 걸립니다. 아마도 Julia와 관련하여 이런 일이있을 것입니다. 아마도 언어는 프로그래머가 C로 직접 표현할 수없는 방식으로 Mandelbrot 세트를 계산하는 원하는 목표를 기술 할 수있게합니다.
벤치 마크에 사용 된 프로세서는 Xeon E7-8850 2.00GHz CPU로 표시되었습니다. C 벤치 마크에서는 gcc 4.8.2 컴파일러를 사용하여 해당 CPU에 대한 명령어를 생성 한 반면 Julia는 LLVM 컴파일러 프레임 워크를 사용합니다. gcc의 백엔드 (특정 CPU 아키텍처를위한 머신 코드를 생성하는 부분)가 LLVM 백엔드만큼 진보되지 않았을 수 있습니다. 성능에 차이가 생길 수 있습니다. 컴파일러는 프로그래머가 지정한 것과 다른 순서로 명령을 실행하거나 코드를 분석하고 그렇지 않은 것으로 판단 할 경우 일부 작업을 수행하지 않음으로써 "최적화"할 수도 있습니다. 정답을 얻기 위해 필요합니다. 그리고 프로그래머는 느리게 만드는 방식으로 C 프로그램의 일부를 작성했을 수도 있습니다.
Mandelbrot 세트를 계산하기 위해 기계어 코드를 작성하는 방법은 많이 있으며, 사용하는 언어는 기계어 코드 작성 방법에 큰 영향을 미칩니다. 컴파일, 명령어 세트, 캐시 등에 대해 더 많이 이해하면 원하는 결과를 얻을 수 있습니다. Julia가 인용 한 벤치 마크 결과의 주요 내용은 모든 언어 나 도구가 최고라는 점입니다. 실제로 전체 차트에서 가장 빠른 속도 요소는 Java였습니다!
컴파일러가 C로 작성된 언어 X로 작성된 코드는 C로 작성된 코드를 능가 할 수 있습니다. C 컴파일러에 의해 생성 된 것보다 객체 코드, X로 작성된 코드가 경쟁에서 이길 수 있습니다.
그러나 언어 X가 해석 된 언어이고 인터프리터가 C로 작성되고 언어 X와 C로 작성된 코드의 인터프리터가 동일한 C 컴파일러에 의해 컴파일되었다고 가정하면 X로 작성된 코드가 코드를 능가하지 않습니다. 구현이 동일한 알고리즘을 따르고 동등한 데이터 구조를 사용하는 경우 C로 작성됩니다.