Ruby / Python과 같은 동적 언어가 성능과 같은 C / C ++에 도달 할 수 있습니까?


64

Ruby와 같은 동적 언어에 대해 C / C ++와 비슷하고 비슷한 성능을 갖는 컴파일러를 빌드 할 수 있는지 궁금합니다. 예를 들어, 컴파일러에 대해 이해하고있는 루비를 예로 들어 보자. 루비가 리플렉션을 처리하는 방식, 정수에서 큰 정수로의 자동 유형 변환과 같은 기능, 정적 타이핑이 없기 때문에 루비 코드를 효율적으로 컴파일 할 수 없다. 루비에게 매우 어려운.

Ruby 또는 다른 동적 언어를 C / C ++에 매우 근접한 바이너리로 컴파일 할 수있는 컴파일러를 빌드 할 수 있습니까? PyPy / Rubinius와 같은 JIT 컴파일러가 결국 성능상의 C / C ++와 일치하거나 절대 일치하지 않는 근본적인 이유가 있습니까?

참고 : "성능"이 모호 할 수 있음을 이해합니다. 성능 Y로 C / C ++에서 X를 수행 할 수 있다면 Ruby / Python에서 X를 Y에 가까운 성능으로 할 수 있습니까? 여기서 X는 장치 드라이버 및 OS 코드에서 웹 응용 프로그램에 이르는 모든 것입니다.


1
다른 사람에 대한 적절한 증거로 뒷받침되는 답변을 장려하도록 질문을 다시 쓸 수 있습니까?
Raphael

@Raphael 저는 진행하고 편집했습니다. 편집 내용이 근본적으로 질문의 의미를 바꾸지는 않지만 의견을 덜 초대한다고 생각합니다.
Gilles

1
특히 하나의 구체적인 성능 측정을 수정 해야 한다고 생각합니다 . 실행 시간? 공간 사용량? 에너지 사용량? 개발자 시간? 투자 회수? 이 질문 (또는 그 답)과 관련된 메타 에 대한 이 질문에 주목하십시오 .
Raphael

이 질문은 종교 전쟁의 전형적인 개시 자입니다. 그리고 우리가 답에서 볼 수 있듯이, 우리는 비록 문명화 된 것이지만 하나를 가지고 있습니다.
Andrej Bauer

선택적 유형 주석을 허용하는 동적 언어가 있습니다 (예 : Clojure). 내가 아는 바로는 형식 주석 기능과 관련된 성능은 언어가 정적으로 입력되는 시점과 같습니다.
Pedro Morte Rolo

답변:


68

“예”라고 말한 모든 사람들에게 나는 의도적으로 대답이 “아니오” 라는 반론을 제시 할 것 입니다. 이러한 언어는 정적으로 컴파일 된 언어의 성능과 절대 일치 할 수 없습니다 .

Kos는 동적 언어가 코드를 최적화하는 데 사용할 수있는 런타임시 시스템에 대한 자세한 정보를 가지고 있다는 (매우 유효한) 포인트를 제공했습니다.

그러나 동전의 또 다른 측면이 있습니다.이 추가 정보는 추적해야합니다. 현대 건축에서는 이것이 성능을 저하시킵니다.

윌리엄 에드워즈는 논쟁좋은 개요를 제공합니다 .

특히 Kos가 언급 한 최적화 는 Devin에서 언급 한 것처럼 언어의 표현력을 크게 제한 하지 않는 한 매우 제한된 범위를 넘어 적용 할 수 없습니다 . 이것은 물론 가능한 절충점이지만 토론을 위해 동적 언어가 아닌 정적 언어로 끝납니다 . 이러한 언어는 대부분의 사람들이 이해하는 것처럼 Python 또는 Ruby와 근본적으로 다릅니다.

William은 흥미로운 IBM 슬라이드를 인용합니다 .

  • 모든 변수는 동적으로 형식화 할 수 있습니다. 유형 검사 필요
  • 유형 불일치 등으로 인해 모든 명령문에서 잠재적으로 예외가 발생할 수 있습니다. 예외 점검 필요
  • 모든 필드와 심볼은 런타임에 추가, 삭제 및 변경 될 수 있습니다 : 액세스 점검 필요
  • 모든 객체의 유형과 클래스 계층 구조는 런타임에 변경 될 수 있습니다 : 클래스 계층 구조 점검 필요

일부 이러한 검사는 분석 후에 제거 될 수있다 (주의 :이 분석은 또한 시간이 걸린다 - 런타임).

또한 Kos는 동적 언어가 C ++ 성능을 능가 할 수 있다고 주장합니다. JIT는 실제로 프로그램의 동작을 분석하고 적절한 최적화를 적용 할 수 있습니다.

그러나 C ++ 컴파일러도 똑같이 할 수 있습니다! 최신 컴파일러는 적절한 입력이 제공되면 프로그램 런타임 동작을 모델링하고 JIT가 적용하는 것과 동일한 최적화를 적용 할 수있는 소위 프로파일 가이드 최적화를 제공합니다.

물론, 이것은 모두 현실적인 훈련 데이터의 존재에 달려 있으며, 또한 사용 패턴이 실행 도중 변경되면 프로그램은 런타임 특성을 조정할 수 없습니다. JIT는 이론적으로이를 처리 할 수 ​​있습니다. 최적화를 전환하기 위해 JIT는 지속적으로 사용 데이터를 수집해야하므로 실행 속도가 느려지기 때문에이 요금이 실제로 어떻게 적용되는지 알고 싶습니다.

요약하자면, 런타임 핫스팟 최적화 가 정적 분석 및 최적화와 비교 하여 장기적으로 런타임 정보 추적 오버 헤드보다 더 크다고 확신하지 않습니다 .


2
@Raphael 그것은 컴파일러의“약점”입니다. 특히 javac프로파일 가이드 최적화를 해 본 적이 있습니까? 내가 아는 한 일반적으로 JIT가이를 처리 할 수 ​​있기 때문에 JITted 언어의 컴파일러를 최적화하는 것이 합리적이지 않습니다. 따라서 ( javac알 수 있듯이) 내가 아는 한 최적화 프로그램 에 많은 노력을 기울인 적이 없었습니다 (.NET 언어의 경우 이것이 사실입니다).
Konrad Rudolph

1
@Raphael 핵심어는 아마도 : 그것은 어느 쪽이든 보여주지 않습니다. 그게 내가 말하고 싶은 전부입니다. 나는 앞의 단락에서 나의 가정에 대한 이유를 제시했다.
Konrad Rudolph

1
@Ben은 복잡하다는 것을 부정하지 않습니다. 이것은 단지 직관 일뿐입니다. 런타임에 모든 정보를 추적하는 것은 결국 비용이 듭니다. IO에 대한 당신의 요점에 대해서는 확신하지 않습니다. 이것이 예측 가능한 경우 (= 일반적인 사용 사례), PGO는이를 예측할 수 있습니다. 의심 스러우면 JIT가 최적화 할 수 있다고 확신하지 못합니다. 어쩌면 한 번만, 운이 좋지 않을 수도 있습니다. 그러나 확실하게? ...
콘라드 루돌프

2
@ Konrad : 직감이 거짓입니다. 그것은 런타임에 변화하는 것이 아니라 컴파일 타임예측할 수없는 것에 관한 입니다. JIT와 정적 최적화의 장점은 프로파일 링을 위해 런타임시 프로그램 동작이 너무 빠를 때가 아니라 프로그램의 각 개별 실행에서 프로그램 동작을 최적화하기 쉽지만 실제로는 매우 다양합니다. 실행합니다. 정적 옵티마이 저는 일반적으로 하나의 조건 세트에 대해서만 최적화해야하는 반면, JIT는 해당 실행에서 발생하는 조건에 대해 각 실행을 개별적으로 최적화합니다.
Ben

3
주의 사항 : 이 댓글 스레드는 미니 대화방으로 발전하고 있습니다. 이 토론을 계속하려면 채팅으로 가져 오십시오. 의견은 원래 게시물을 개선하는 데 사용해야합니다. 이 대화를 여기서 중단 하십시오. 감사.
Robert Cartaino

20

성능 Y로 C / C ++에서 X를 수행 할 수 있다면 Y에 가까운 성능으로 Ruby / Python에서 X를 수행 할 수 있습니까?

예. PyPy를 예로 들어 보겠습니다. 그것은 해석을 할 때 C에 가깝게 수행하는 파이썬 코드 모음입니다 (그 모든 것이 아니라 멀리 떨어져있는 것은 아닙니다). 소스 코드에 대해 전체 프로그램 분석을 수행하여 각 변수에 정적 유형을 지정하고 (자세한 내용은 주석Rtyper 문서 참조) C에 제공 한 것과 동일한 유형 정보로 무장 한 후에도 동일한 기능을 수행 할 수 있습니다. 최적화의 종류. 적어도 이론 상으로는.

물론 RPython에서는 Python 코드의 하위 집합 만 받아들이고 일반적으로 이러한 제한이 해제 되더라도 Python 코드의 하위 집합 만 분석 할 수 있습니다.이 하위 집합은 분석하고 정적 유형을 지정할 수 있습니다.

Python을 충분히 제한하면 제한된 하위 집합을 활용하고 효율적인 코드로 컴파일 할 수있는 옵티 마이저를 빌드 할 수 있습니다. 이것은 실제로 흥미로운 이점은 아니지만 잘 알려져 있습니다. 그러나 처음에 파이썬 (또는 루비)을 사용하는 요점은 분석이 잘되지 않고 성능이 좋은 흥미로운 기능을 사용하고 싶었다는 것입니다! 흥미로운 질문은 실제로 ...

또한 PyPy / Rubinius와 같은 JIT 컴파일러는 성능면에서 C / C ++와 일치합니까?

아냐

즉, 아마도 코드 실행이 누적되면 충분한 코드 정보와 머신 코드까지 모든 코드를 컴파일하기에 충분한 핫스팟을 얻을 수 있습니다. 그리고 일부 코드에서는 C보다 성능이 좋을 수도 있습니다. 나는 그것이 논란의 여지가 없다고 생각합니다. 그러나 여전히 "웜업 (warm up)"해야하고, 성능은 여전히 ​​예측하기 어렵고, 일관되고 예측 가능하게 높은 성능을 요구하는 특정 작업에는 C 또는 C ++만큼 좋지 않습니다.

Python 또는 Ruby보다 유형 정보가 많고 Python 또는 Ruby보다 JIT 컴파일러가 더 나은 Java의 기존 성능 데이터는 여전히 C / C ++와 일치하지 않습니다. 그러나 같은 야구장에 있습니다.


1
"물론, 파이썬 코드의 서브셋 만이 받아 들여 지거나 오히려 파이썬 코드의 서브셋 만이 잘 될 수 있다는 것입니다. 분석하고 정적 타입을 부여 할 수있는 서브셋입니다." 이것은 정확하지 않습니다. RPython 컴파일러는 서브 세트 만 허용 할 수 있습니다. 파이썬의 컴파일하기 어려운 부분이 RPython 프로그램에서 절대로 발생하지 않기 때문에 RPython에서 합리적으로 효율적인 C로 컴파일하는 것은 정확하게 작동합니다. 단순히 발생하는 경우 최적화되지 않습니다. 모든 파이썬을 처리하는 컴파일 된 인터프리터입니다.
Ben

1
JIT 소개 : Java가 여러 가지 C (++)를 능가하는 벤치 마크를 두 개 이상 보았다. 부스트가있는 C ++ 만 안정적으로 앞설 것으로 보입니다. 이 경우 개발자 시간당 성능에 대해 궁금하지만 다른 주제입니다.
Raphael

@Ben : 일단 RPython을 가지고 있다면, RPython 컴파일러가 실패 할 때 CPython 인터프리터를 사용하는 것으로 돌아가는 컴파일러 / 인터프리터를 생성하는 것은 사소한 일이므로 "Python 코드의 하위 집합 만 잘 수행 할 수 있습니다 : ..."는 완전히 정확합니다.
Lie Ryan

9
@Raphael 잘 작성된 C ++ 코드가 Java를 능가 하는 것으로 여러 번 나타났습니다 . "잘 작성된"부분은 C ++에서 다루기가 다소 어렵 기 때문에 많은 벤치에서 Java가 C ++보다 우수한 결과를 볼 수 있습니다. 따라서 C ++은 더 비싸지 만, 막힘 제어 및 금속 그릿이 필요할 때는 C / C ++가 필요합니다. 특히 C는 ac / p 어셈블러입니다.
TC1

7
C / C ++와 같은 언어와 다른 언어의 최대 성능을 비교하는 것은 언어 사양의 일부로 직접 어셈블리를 인라인 할 수 있기 때문에 무용지물입니다. 기계가 어떤 언어로든 프로그램을 실행할 수있는 모든 것은 실행 된 명령의 흔적에서 어셈블리를 작성함으로써 최악의 복제를 할 수 있습니다. @Raphael이 이전 의견에서 개발 노력 (성능 시간, 코드 라인 등) 당 성능을 제안한 것처럼 훨씬 더 흥미로운 지표가 될 것입니다.
Patrick87

18

짧은 대답은 : 우리는 모른다 , 100 년 후에 다시 묻습니다. (아직도 알지 못할 수도 있으며, 아마도 알지 못할 수도 있습니다.)

이론적으로는 가능합니다. 지금까지 작성된 모든 프로그램을 가져 와서 가장 효율적인 기계 코드로 수동 변환하고 소스 코드를 기계 코드에 매핑하는 인터프리터를 작성하십시오. 한정된 수의 프로그램 만 작성 되었기 때문에 가능합니다 (더 많은 프로그램이 작성됨에 따라 수동 번역을 유지하십시오). 물론 이것은 실질적인 용어에 대해서는 완전히 바보입니다.

이론적으로, 고급 언어는 머신 코드의 성능에 도달 할 수 있지만,이를 능가하지는 않습니다. 실제 용어로 우리는 기계 코드 작성에 거의 의존하지 않기 때문에 이것은 여전히 ​​매우 이론적입니다. 이 주장은 더 높은 수준의 언어를 비교하는 데 적용되지 않습니다. C가 Python보다 효율적이어야한다는 것을 의미하지는 않으며 기계 코드 만 Python보다 나을 수는 없습니다.

순수하게 실험적인 용어로 볼 , 대부분 해석 된 고급 언어는 컴파일 된 저급 언어보다 성능이 떨어집니다. 우리는 C 및 Python과 같은 언어가 사용되는 매우 고급 언어 및 시간 결정적인 내부 루프에서 시간에 민감하지 않은 코드를 작성하는 경향이 있습니다. 나는 이것을 뒷받침 할 통계가 없지만, 이것이 실제로 대부분의 최선의 결정이라고 생각합니다.

그러나 고급 언어가 현실적으로 작성하는 코드를 능가하는 특수한 목적의 프로그래밍 환경 인 경쟁이없는 사례가 있습니다. Matlab 및 Mathematica와 같은 프로그램은 단순한 필사자가 쓸 수있는 것보다 특정 종류의 수학 문제를 해결하는 데 훨씬 능숙합니다. 라이브러리 함수는 C 또는 C ++로 작성되었을 수 있지만 (“저수준 언어가 더 효율적입니다”캠프에 연료가 될 것입니다), Mathematica 코드를 작성하면 라이브러리가 블랙 박스입니다.

이론적으로 파이썬이 C보다 최적의 성능에 가까워 지거나 더 가까워 질 수 있습니까? 위에서 볼 수 있듯이, 그렇습니다. 그러나 우리는 오늘날 그것과는 거리가 멀습니다. 다시 말하지만, 컴파일러는 지난 수십 년 동안 많은 진전을 이루 었으며 그 진전이 느려지지 않습니다.

고급 언어는 더 많은 작업을 자동으로 수행하는 경향이 있으므로 더 많은 작업을 수행하므로 효율성이 떨어집니다. 반면에 의미 정보가 더 많은 경향이 있으므로 최적화를보다 쉽게 ​​찾을 수 있습니다 (Haskell 컴파일러를 작성하는 경우 다른 스레드가 코 아래에서 변수를 수정할지 걱정할 필요가 없습니다). 사과와 오렌지의 다른 프로그래밍 언어 를 비교하려는 몇 가지 노력 중 하나는 Computer Language Benchmark Game (이전의 총격 사건)입니다. 포트란은 수치 작업에서 빛을 발하는 경향이있다. 그러나 구조화 된 데이터 또는 고속 스레드 정류 조작과 관련하여F #과 Scala가 잘 작동합니다. 이 결과를 복음으로 받아들이지 마십시오. 그들이 측정하는 많은 것은 각 언어로 시험 프로그램을 만든 사람이 얼마나 훌륭했는지입니다.

고급 언어를 선호하는 주장은 현대 시스템의 성능이 실행되는 명령 수와 시간이 지남에 따라 크게 상관되지 않는다는 것입니다. 저수준 언어는 간단한 순차 시스템에 적합합니다. 고급 언어가 두 배 많은 명령을 실행하지만 캐시를보다 지능적으로 사용하여 캐시 누락을 절반으로 줄이면 승자가 될 수 있습니다.

서버 및 데스크탑 플랫폼에서 CPU는 거의 빠르지 않은 수준에 도달했습니다 (모바일 플랫폼도 마찬가지입니다). 이것은 병렬 처리가 쉬운 언어를 선호합니다. 많은 프로세서가 대부분의 시간을 I / O 응답을 기다리는 데 소비합니다. 계산에 소요되는 시간은 I / O의 양에 비해 거의 중요하지 않으며 프로그래머가 통신을 최소화 할 수있는 언어가 유리합니다.

대체로 고급 언어는 페널티로 시작하지만 개선의 여지가 더 많습니다. 그들은 얼마나 가까이 갈 수 있습니까? 100 년 후에 다시 질문하십시오.

마지막 주 : 종종 비교는 언어 A 로 작성 될 수있는 가장 효율적인 프로그램 과 언어 B로 동일하게 작성 되는 것이 아니라 각 언어로 작성된 가장 효율적인 프로그램 사이 아니라 작성할 수있는 가장 효율적인 프로그램 사이 의 비교입니다. 각 언어로 특정 시간인간에 의해 . 이것은 원칙적으로 수학적으로 분석 할 수없는 요소를 소개합니다. 실제로 이것은 종종 최상의 성능이 성능 목표를 달성하기 위해 작성해야하는 저수준 코드와 릴리스 날짜를 충족하기 위해 작성해야하는 저수준 코드의 양 사이의 절충을 의미합니다.


나는 고수준과 저수준의 구별이 잘못된 것이라고 생각합니다. C ++은 (매우) 높은 수준 일 수 있습니다. 그러나 현대의 높은 수준의 C ++은 낮은 수준의 동등한 수준보다 (필수적으로) 성능이 떨어지지 않습니다. C ++ 및 해당 라이브러리는 성능 저하 없이 높은 수준의 추상화를 제공하도록 신중하게 설계되었습니다 . Haskell 예제에서도 마찬가지입니다. 높은 수준의 추상화는 종종 최적화를 방해하지 않고 가능하게 합니다. 동적 언어와 정적 언어의 원래 차이점은 이와 관련하여 더 의미가 있습니다.
Konrad Rudolph

@KonradRudolph 저수준 / 고수준은 다소 임의적 인 차이점입니다. 그러나 동적 언어와 정적 언어는 모든 것을 포착하지 못합니다. JIT는 많은 차이를 없앨 수 있습니다. 본질적으로이 질문에 대한 알려진 이론적 답변은 사소하고 쓸모가 없으며 실제 답변은“의존적”입니다.
Gilles

글쎄, 나는 JIT가 얼마나 잘 될 수 있는지, 그리고 정적 컴파일을 능가하면 정적으로 컴파일 된 언어도 이익을 얻을 수 있을까?”라고 생각합니다. 적어도 JIT를 고려하면 질문을 이해하는 것입니다. 그리고 네, 나는 당신의 평가에 동의하지만 확실히 "의존"을 넘어선 정보에 근거한 추측을 얻을 수 있습니다. ;-)
Konrad Rudolph

@KonradRudolph 추측을 원한다면 소프트웨어 엔지니어링에 문의하십시오 .
Gilles

1
언어 슛 아웃은 불행히도 정량적 벤치 마크를위한 의문의 여지가 있습니다. 언어의 전형적인 것으로 간주되는 모든 프로그램 만 허용하는 것은 아닙니다. 이것은 까다 롭고 매우 주관적인 요구 사항입니다. 그것은 실제로 총격전 구현이 좋은 것으로 가정 할 수 없다는 것을 의미합니다 (실제로 일부 구현에는 분명히 우수한 대안이 거부되었습니다). 플립 측면에서; 이것들은 마이크로 벤치 마크이며 일부 구현에서는 성능을 얻기 위해 더 일반적인 시나리오에서는 결코 고려하지 않을 특별한 기술을 사용합니다. 따라서 매우 신뢰할만한 데이터 소스가 아닌 게임입니다.
Eamon Nerbonne

10

C ++ 문과 x = a + bPython 문의 기본적인 차이점은 x = a + bC / C ++ 컴파일러가이 문 (및 , 및 x, 유형에 대해 쉽게 사용할 수 있다는 약간의 추가 정보 )에서 정확히 어떤 기계 코드를 실행해야하는지 알 수 있다는 것입니다. . 파이썬 문장이 어떤 작업을 수행 할 것인지 말하는 반면, Halting Problem을 해결해야합니다.ab

C에서 그 문장은 기본적으로 몇 가지 유형의 기계 추가 중 하나로 컴파일됩니다 (그리고 C 컴파일러는 어느 것을 알고 있습니다). C ++에서는 컴파일 방식으로 컴파일되거나 정적으로 알려진 함수를 호출하도록 컴파일되거나 (가장 최악의 경우) 가상 메소드 조회 및 호출로 컴파일해야하지만 기계 코드 오버 헤드는 상당히 적습니다. 더 중요한 것은 C ++ 컴파일러가 정적으로 알려진 유형에서 단일 빠른 추가 연산을 생성 할 수 있는지 아니면 느린 옵션 중 하나를 사용해야하는지 여부를 알 수 있습니다.

이 경우 파이썬에서, 컴파일러는 이론적으로 거의 좋은 할 수있는 알고ab모두했다 int에요. 추가적인 권투 오버 헤드가 있지만 유형이 정적으로 알려지면 아마도 그것을 제거 할 수 있습니다 (정수는 메소드, 수퍼 클래스의 계층 등을 가진 객체라는 인터페이스를 여전히 제시합니다). 문제는 파이썬 컴파일러가 할 수없는 것입니다.클래스는 런타임에 정의되고 런타임에 수정 될 수 있으며 정의 및 가져 오기를 수행하는 모듈도 런타임에 분석되며 실행시 가져 오는 명령문도 런타임에만 알 수있는 항목에 따라 다릅니다. 따라서 파이썬 컴파일러는 컴파일하는 명령문이 무엇을 수행하는지 알기 위해 어떤 코드가 실행되었는지 (즉, Halting Problem 해결) 알아야합니다.

따라서 이론적으로 가능한 가장 정교한 분석을 사용하더라도 주어진 Python 문이 미리 수행 할 작업에 대해 많은 것을 알 수 없습니다. 즉, 정교한 Python 컴파일러를 구현하더라도 거의 모든 경우에 파이썬 사전 조회 프로토콜에 따라 기계 클래스를 생성하여 객체의 클래스를 결정하고 메소드를 찾습니다 (클래스 계층의 MRO를 통과 함). 런타임에 동적으로 변경 될 수 있으므로 간단한 가상 메소드 테이블로 컴파일하기가 어렵고 기본적으로 (느린) 인터프리터가하는 일을 수행합니다. 이것이 동적 언어를위한 정교한 최적화 컴파일러가 실제로없는 이유입니다. 단지 하나를 만드는 것은 어렵지 않습니다. 가능한 최대 보상은

이것은 코드 수행하는 작업을 기반으로하지 않으며 코드 수행 할 수 있는 작업을 기반으로합니다 . 간단한 일련의 정수 산술 연산 인 Python 코드조차도 임의의 클래스 연산을 호출하는 것처럼 컴파일 해야 합니다. 정적 언어는 코드가 수행 할 수있는 가능성에 대해 더 큰 제한이 있으므로 결과적으로 컴파일러가 더 많은 가정을 할 수 있습니다.

JIT 컴파일러는 런타임이 컴파일 / 최적화 될 때까지 기다림으로써이를 얻습니다. 이를 통해 코드 수행 할 수있는 것이 아니라 수행중인 작업에 적합한 코드를 생성 할 수 있습니다. 그리고이 JIT 컴파일러로 인해 정적 언어보다 동적 언어에 대한 잠재적 인 보상이 훨씬 큽니다. 보다 정적 인 언어의 경우 옵티마이 저가 알고 싶어하는 많은 부분을 미리 알 수 있으므로 JIT 컴파일러가 수행하는 작업을 줄이면서 최적화 할 수 있습니다.

컴파일 및 최적화 된 C / C ++의 실행 속도와 비슷한 실행 속도를 달성한다고 주장하는 동적 언어를위한 다양한 JIT 컴파일러가 있습니다. 어떤 언어에 대해서도 사전 컴파일러가 수행 할 수없는 JIT 컴파일러에 의해 최적화 될 수 있기 때문에 이론적으로 JIT 컴파일 (일부 프로그램의 경우)은 최고의 정적 컴파일러보다 성능이 우수 할 수 있습니다. 그러나 Devin이 올바르게 지적했듯이 JIT 컴파일의 속성 ( "핫스팟"만 빠르고 워밍업 기간이 지난 후에야)은 JIT 컴파일 된 동적 언어가 모든 응용 프로그램에 적합하지 않을 수 있음을 의미합니다. 정적으로 컴파일 된 언어보다 일반적으로 빠르거나 빠릅니다.


1
그것은 이제 의견이없는 두 개의 투표권입니다. 이 답변을 개선하는 방법에 대한 제안을 환영합니다!

공감하지는 않았지만 "중지 문제를 해결해야합니다"에 대해서는 틀 렸습니다. 동적 언어로 된 코드는 최적의 대상 코드로 컴파일 될 수 있다는 것이 많은 상황에서 입증되었지만, 내 지식으로는 이러한 데모 중 어느 것도 정지 문제에 대한 해결책을 포함하지 않았다 :-)
mikera

@ mikera 죄송 합니다만, 귀하는 정확하지 않습니다. 완전한 일반 파이썬 또는 다른 동적 언어를 위한 컴파일러 (GCC가 컴파일러라는 것을 이해한다는 의미)를 구현 한 사람은 없습니다 . 이러한 모든 시스템은 언어의 일부 또는 특정 프로그램에서만 작동하거나 기본적으로 하드 코딩 된 프로그램을 포함하는 인터프리터 인 코드를 생성합니다. 원한다면 foo = x + y컴파일 타임에 더하기 연산자의 동작을 예측하는 것이 중단 문제 해결에 달려 있는 줄을 포함하는 Python 프로그램을 작성하겠습니다 .
Ben

맞습니다. 요점을 놓친 것 같습니다. 나는 "많은 상황에서"말했다. 나는 가능한 모든 상황에서 말하지 않았다. 정지 문제와 관련된 유념 한 사례를 구성 할 수 있는지 여부는 실제 환경과는 크게 관련이 없습니다. FWIW, C ++에 대한 유사한 예제를 구성하여 아무 것도 증명하지 않을 수 있습니다. 어쨌든, 나는 당신의 대답을 개선하기 위해 제안하기 위해 여기에 오지 않았습니다. 가져 가든지 남겨 두든지.
mikera

@ mikea 나는 당신이 요점을 놓치고 있다고 생각합니다. x + y효율적인 기계 추가 작업 으로 컴파일 하려면 컴파일시 여부를 알아야합니다. 시간의 일부만이 아니라 항상. 동적 언어의 경우 합리적인 휴리스틱이 대부분의 시간을 추측하더라도 현실적인 프로그램으로는 거의 불가능합니다. 컴파일에는 컴파일 타임 보증이 필요합니다 . "많은 상황에서"에 대해 말함으로써 당신은 실제로 나의 대답을 전혀 다루지 않습니다.
Ben

9

동적 언어에 대한 최악의 시나리오를 간략히 설명하는 간단한 포인터 :

펄 파싱은 계산할 수 없다

결과적으로 (전체) Perl은 정적으로 컴파일 될 수 없습니다 .


일반적으로 항상 그렇듯이 다릅니다. 정적으로 컴파일 된 언어로 동적 기능을 에뮬레이션하려고하면 잘 고안된 해석기 또는 (일부) 컴파일 된 변형이 정적으로 컴파일 된 언어의 성능에 근접하거나 언더컷 할 수 있다고 확신합니다.

명심해야 할 또 다른 요점은 다이나믹 언어 는 C 이외의 다른 문제를 해결 한다는 것입니다. 다이나믹 언어는 풍부한 추상화를 제공하는 반면 C는 어셈블러를위한 훌륭한 구문 이상은 아닙니다. 예를 들어 시장 출시 시간은 개발자가 짧은 시간 안에 복잡한 고품질 시스템을 작성할 수 있는지에 달려 있습니다. 재 컴파일이없는 확장 성 (예 : 플러그인)은 또 다른 인기있는 기능입니다. 이 경우 어떤 언어를 선호하십니까?


5

이 질문에 대해보다 객관적인 과학적 답변을 제공하기 위해 다음과 같이 주장합니다. 동적 언어에는 런타임에 의사 결정을 내리기 위해 인터프리터 또는 런타임이 필요합니다. 이 인터프리터 또는 런타임은 컴퓨터 프로그램이므로 정적 또는 동적 프로그래밍 언어로 작성되었습니다.

인터프리터 / 런타임이 정적 언어로 작성된 경우, (a) 해석하는 동적 프로그램과 동일한 기능을 수행하고 (b) 최소한 수행하는 정적 언어로 프로그램을 작성할 수 있습니다. 이러한 주장에 대한 엄격한 증거를 제공하기 위해서는 추가적인 노력이 필요할 수 있기 때문에 이것은 자명 한 일입니다.

이러한 주장이 사실이라고 가정하면 통역사 / 런타임을 동적 언어로 작성해야합니다. 그러나 이전과 같은 문제가 발생합니다. 인터프리터가 동적 인 경우 인터프리터 / 런타임이 필요하며 프로그래밍 언어로 동적 또는 정적으로 작성되어야합니다.

인터프리터의 인스턴스가 런타임에 자신을 해석 할 수 있다고 가정하지 않는 한 (정확하게 말도 안되기를 바랍니다) 정적 언어를 이길 수있는 유일한 방법은 각 인터프리터 인스턴스를 별도의 인터프리터 인스턴스에서 해석하는 것입니다. 이것은 무한한 퇴보 (이것은 자명 한 말이 되었으면 좋겠다) 또는 통역사들의 폐쇄 된 루프 (나도 자명 한 말이 되었으면한다)로 이어진다.

그러므로 이론적으로도 동적 언어는 일반적으로 정적 언어보다 더 나은 성능을 발휘할 수없는 것 같습니다. 실제 컴퓨터 모델을 사용할 때는 훨씬 더 그럴듯 해 보입니다. 결국, 머신은 일련의 머신 명령어 만 실행할 수 있으며 모든 머신 명령어 시퀀스는 정적으로 컴파일 될 수 있습니다.

실제로, 동적 언어의 성능과 정적 언어를 일치 시키려면 정적 언어로 인터프리터 / 런타임을 다시 구현해야합니다. 그러나 당신이 할 수있는 것은이 주장의 요점과 요점입니다. 그것은 닭고기와 계란 문제이며, 위에서 언급 한 입증되지 않은 (내 의견으로는 대부분 자명 한) 가정에 동의하면 실제로 대답 할 수 있습니다. 우리는 정적이 아닌 정적 언어에 끄덕 여야합니다.

이 논의에 비추어 질문에 대답하는 또 다른 방법은 다음과 같습니다. 저장된 컴퓨팅, 현대 컴퓨팅의 핵심에 위치한 컴퓨팅 제어 데이터 모델에서 정적 컴파일과 동적 컴파일의 구별은 잘못된 이분법입니다. 정적으로 컴파일 된 언어에는 런타임에 임의의 코드를 생성하고 실행하는 수단이 있어야합니다. 기본적으로 범용 계산과 관련이 있습니다.


이것을 다시 읽으면서, 나는 그것이 사실이라고 생각하지 않습니다. JIT 컴파일은 당신의 주장을 깨뜨립니다. 심지어 가장 간단한 코드, 예를 들면 main(args) { for ( i=0; i<1000000; i++ ) { if ( args[0] == "1" ) {...} else {...} }상당히 값 일단 가속화 args알려져있다 (그 가정은 우리가 주장 할 수있다, 변경되지 않음). 정적 컴파일러는 비교를 중단시키는 코드를 만들 수 없습니다. (물론,이 예 if에서는 루프 에서 빠져 나옵니다.하지만 상황이 더 복잡 할 수 있습니다.)
Raphael

@Raphael 나는 JIT 아마 생각 하게 내 주장을. JIT 컴파일을 수행하는 프로그램 (예 : JVM)은 일반적으로 정적으로 컴파일 된 프로그램입니다. 정적으로 컴파일 된 JIT 프로그램이 다른 정적 프로그램이 동일한 작업을 수행 할 수있는 것보다 빠르게 스크립트를 실행할 수있는 경우 JIT 컴파일러로 스크립트를 "번들링"하고 정적으로 컴파일 된 프로그램을 번들로 호출하면됩니다. 이것은 최소한 별도의 동적 프로그램에서 작동하는 JIT뿐만 아니라 JIT가 더 잘해야한다는 주장과 모순되어야합니다.
Patrick87

흠, 그것은 인터프리터와 함께 루비 스크립트를 번들로 제공하면 정적으로 컴파일 된 프로그램을 제공한다는 것과 같습니다. 동의하지 않습니다 (이 점에서 언어의 모든 구별을 제거하기 때문에). 그것은 개념이 아니라 의미론의 문제입니다. 개념적으로 런타임시 프로그램 (JIT)을 조정하면 컴파일 타임 (정적 컴파일러)에서 수행 할 수없는 최적화를 수행 할 수 있으며 이것이 바로 요점입니다.
Raphael

@Raphael 의미있는 구별이 없다는 것은 대답의 요점입니다. 일부 언어를 정적으로 분류하여 성능 제한으로 어려움을 겪는 시도는 정확히 이런 이유로 실패합니다. , script) 번들 및 C 프로그램. 만약 (루비, 스크립트)가 기계가 주어진 문제를 효율적으로 해결하기 위해 올바른 순서의 명령을 수행하게 할 수 있다면, 영리하게 조작 된 C 프로그램이 될 수 있습니다.
Patrick87

그러나 차이점을 정의 할 수 있습니다 . 한 변형은 현재 코드를 변경되지 않은 프로세서 (C)로 보내고 다른 변형은 런타임에 컴파일합니다 (Ruby, Java 등). 첫 번째는 "정적 컴파일"의 의미이고, 후자는 "적시 컴파일"(데이터에 따라 최적화 할 수 있음)입니다.
Raphael

4

C / C ++와 비슷하고 비슷한 성능을 갖도록 Ruby와 같은 동적 언어 용 컴파일러를 빌드 할 수 있습니까?

나는 그 대답이 "예" 라고 생각합니다 . 또한 효율성 측면에서 보면 현재 C / C ++ 아키텍처를 능가 할 수 있다고 생각합니다 .

이유는 간단합니다. 컴파일 타임보다 런타임에 더 많은 정보가 있습니다.

동적 유형은 약간의 장애물입니다. 함수가 항상 또는 거의 항상 동일한 인수 유형으로 실행되는 경우 JIT 최적화 프로그램은 해당 특정 사례에 대한 분기 및 기계 코드를 생성 할 수 있습니다. 그리고 할 수있는 일이 훨씬 더 많습니다.

보다 동적 언어 스트라이크로 돌아 가기 구글의 스티브 예그하여 연설을 (내가 믿는 어딘가에 비디오 버전도있다). 그는 V8의 구체적인 JIT 최적화 기술에 대해 언급합니다. 고무적!

앞으로 5 년 안에 우리가 무엇을 가질 지 기대하고 있습니다!


2
나는 낙천주의를 좋아합니다.
Dave Clarke

나는 스티브의 대화에서 부정확성에 대한 매우 구체적인 비판이 있다고 생각합니다. 내가 찾으면 게시하겠습니다.
Konrad Rudolph

1
@DaveClarke 그게 나를 계속 달리게한다 :)
Kos

2

이것이 이론적으로 가능하거나 먼 미래에 있다고 생각하는 사람들은 제 생각에는 완전히 틀 렸습니다. 요점은 동적 언어가 완전히 다른 프로그래밍 스타일을 제공하고 부과한다는 사실에 있습니다. 실제로 두 측면이 서로 관련되어 있어도 그 차이는 두 가지입니다.

  • 기호 (vars 또는 오히려 모든 종류의 id-datum 바인딩)는 형식화되지 않습니다.
  • 구조 (데이터, 런타임에 존재하는 모든 것)는 요소 유형에 따라 유형이 지정되지 않습니다.

두 번째 요점은 무료로 일반성을 제공합니다. 여기의 구조는 복합 요소, 컬렉션뿐만 아니라 유형 자체 및 모든 종류의 함수 (!) 루틴 (함수, 동작, 연산)입니다 ... 요소 유형별로 구조를 입력 할 수 있지만 첫 번째 점으로 인해 어쨌든 런타임에 검사가 발생합니다. 우리는 문자를 입력 한 수 여전히 (배열이 자신의 요소 유형에 따라 유형이 지정되지 않은 사람을 구조 한 a그냥 int 배열로 배열로 입력 할 것),하지만 약간은 (동적 언어로 사실이 아니다 심지어 a뿐만 아니라 포함 할 수있다 문자열).

  • Element 모델에서 유형 )
  • 모든 기호는 해당 유형 Element이며 모든 요소를 보유 할 수 있습니다. 유형의
  • 모든 구조 (모델 루틴을 포함하여)는 Element 만받습니다.

이것은 단지 큰 퍼포먼스 페널티라는 것이 분명합니다. 그리고 다른 게시물에 잘 설명 된 모든 결과 (프로그램 감수성을 보장하는 데 필요한 모든 종류의 런타임 검사)조차 다루지 않습니다.


+1 매우 흥미 롭습니다. 내 대답을 읽었습니까? 당신의 대답은 더 자세하고 흥미로운 관점을 제공하지만 당신과 나의 생각은 비슷해 보입니다.
Patrick87

동적 언어는 타입을 지정할 필요가 없습니다. C에서 동적 언어의 모델을 구현하면 최적화 가능성이 심각하게 제한됩니다. 컴파일러가 높은 수준의 최적화 (예 : 불변의 데이터)를 인식하고 일부 기본 작업 (예 : 함수 호출)을 C로 강제 전달하는 것은 매우 어렵습니다. 설명하는 것은 바이트 코드 디코딩으로 바이트 코드 인터프리터와 멀지 않습니다. 사전 평가; 네이티브 컴파일러는 성능이 크게 향상되는 경향이 있습니다. 바이트 코드 디코딩이 그 차이를 정당화 할 수 있다고 의심합니다.
Gilles

@ 패트릭 87 : 당신 말이 맞아, 우리의 생각은 매우 비슷해 보인다 (이전에 읽지 않았지만, 미안, 내 반영은 현재 C에서 dyn lang을 구현함으로써 나온다).
spir 14'122012-04-18

@Gilles : 정적으로 입력 하지 않았다면 "... 입력하지 않아도됩니다"에 동의합니다 . 그러나 이것은 사람들이 일반적으로 dyn lang에 대해 생각하는 것이 아닙니다. 나는 개인적으로 제네릭 (위의 답변에 주어진 일반적인 의미에서) 없이는 훨씬 더 강력하고 살기 어려운 기능을 고려합니다. 주어진 (유형 다형성) 유형이 어떻게 정의 될 수 있는지에 대한 생각을 넓히거나 인스턴스에 더 많은 유연성을 부여함으로써 정적 유형과 관련된 방법을 쉽게 찾을 수 있습니다.
spir

1

나는 모든 답을 자세히 읽을 시간이 없었지만 나는 즐거웠다.

60 년대와 70 년대 초반에도 비슷한 논쟁이있었습니다 (컴퓨터 과학 역사는 종종 반복됩니다) : 프로그래머가 수동으로 생산 한 어셈블리 코드와 같이 기계어 코드만큼 효율적인 코드를 생성하기 위해 고급 언어를 컴파일 할 수 있습니까? 누구나 프로그래머가 어떤 프로그램보다 훨씬 똑똑하다는 것을 알고 있으며 매우 현명한 최적화 (실제로 대부분은 구멍 구멍 최적화라고 함)를 생각해 낼 수 있습니다. 이것은 물론 아이러니하다.

코드 확장이라는 개념도있었습니다. 컴파일러가 생성 한 코드 크기와 우수한 프로그래머가 생성 한 동일한 프로그램의 코드 크기 비율 (이러한 :-)이 너무 많은 것처럼 보입니다. 물론이 비율은 항상 1보다 큽니다. 당시의 언어는 지식인의 경우 Cobol and Fortran 4 또는 Algol 60이었습니다. 나는 Lisp이 고려되지 않았다고 생각합니다.

누군가가 때로는 1의 확장 비율을 얻을 수있는 컴파일러를 만들었다는 소문이있었습니다 ... 컴파일 된 코드가 수작업 코드보다 훨씬 낫다는 규칙이되었습니다 (더 신뢰할 수 있음). 사람들은 당시의 코드 크기 (작은 메모리)에 대해 걱정했지만 속도 나 에너지 소비에 대해서도 마찬가지입니다. 나는 그 이유에 들어 가지 않을 것이다.

이상한 기능, 언어의 동적 기능은 중요하지 않습니다. 중요한 것은 사용 여부, 사용 여부입니다. 어떤 단위 (코드 크기, 속도, 에너지 등)의 성능은 종종 프로그램의 아주 작은 부분에 의존합니다. 따라서 표현력을 제공하는 시설이 실제로 방해가되지 않을 가능성이 높습니다. 우수한 프로그래밍 실습을 통해 고급 시설은 훈련 된 방식으로 만 사용되어 새로운 구조를 상상할 수 있습니다.

언어에 정적 타이핑이 없다는 사실은 해당 언어로 작성된 프로그램이 정적으로 타이핑되지 않았 음을 의미하지 않습니다. 반면에 프로그램이 사용하는 형식 시스템이 형식 검사기가 존재하기에 아직 충분히 형식화되지 않았을 수 있습니다.

토론에서 최악의 사례 분석 ( "중지 문제", PERL 구문 분석)에 대한 몇 가지 참조가있었습니다. 그러나 최악의 경우 분석은 대부분 관련이 없습니다. 중요한 것은 대부분의 경우 또는 유용한 경우에 발생하지만 ... 정의되거나 이해되거나 경험 된 것입니다. 여기에 프로그램 최적화와 직접 관련된 또 다른 이야기가 있습니다. 그것은 텍사스의 한 주요 대학에서 박사 과정 학생과 그의 고문 (나중에 국가 아카데미에서 선출 된) 사이에서 오래 전에 일어났습니다. 내가 기억 하듯이, 학생은 고문이 다루기 어려운 것으로 나타 났던 분석 / 최적화 문제를 연구하는 데 열중했다. 곧 그들은 더 이상 말을하지 않았다. 그러나 학생은 옳았습니다. 문제는 대부분의 실제 사례에서 충분히 다루기 쉬워서 그가 작성한 논문이 참조 작업이되었습니다.

그리고 Perl parsing is not computable그 문장의 의미가 무엇이든 ML에 대해 비슷한 문제가 있다는 진술에 대해 더 언급하기 위해 ML은 비슷한 형식의 언어입니다.Type checking complexity in ML is a double exponential in the lenght of the program.그것은 최악의 경우에 매우 정확하고 공식적인 결과입니다. Afaik, ML 사용자는 여전히 유형 검사기를 폭발시킬 실용적인 프로그램을 기다리고 있습니다.

많은 경우에 이전과 마찬가지로 인간의 시간과 역량은 컴퓨팅 능력보다 부족합니다.

미래의 실제 문제는 여전히 사용되는 모든 레거시 소프트웨어를 다시 작성할 필요없이 새로운 지식과 새로운 프로그래밍 형식을 통합하기 위해 언어를 발전시키는 것입니다.

수학을 살펴보면 매우 큰 지식이 있습니다. 그것을 표현하는 데 사용 된 언어, 표기법 및 개념은 수세기에 걸쳐 진화 해 왔습니다. 새로운 개념으로 오래된 정리를 작성하는 것은 쉽습니다. 우리는 주요 증거를 적용하지만 많은 결과를 귀찮게하지 않습니다.

그러나 프로그래밍의 경우 모든 증거를 처음부터 다시 작성해야 할 수도 있습니다 (프로그램은 증거 임). 실제로 필요한 것은 매우 높은 수준의 개발 가능한 프로그래밍 언어 일 수 있습니다. 최적화 디자이너는 기꺼이 따라갈 것입니다.


0

몇 가지 메모 :

  • 모든 고급 언어가 동적 인 것은 아닙니다. Haskell은 매우 높은 수준이지만 완전히 정적으로 입력됩니다. Rust, Nim 및 D와 같은 시스템 프로그래밍 언어조차 간결하고 효율적으로 높은 수준의 추상화를 표현할 수 있습니다. 실제로 동적 언어만큼 간결 할 수 있습니다.

  • 동적 언어를위한 고도로 최적화 된 사전 컴파일러가 존재합니다. 좋은 Lisp 구현은 동등한 C 속도의 절반에 도달합니다.

  • JIT 컴파일 큰 승리 있습니다. CloudFlare의 웹 애플리케이션 방화벽은 LuaJIT에서 실행되는 Lua 코드를 생성합니다. LuaJIT은 실제로 수행 된 실행 경로 (일반적으로 비 공격 경로)를 크게 최적화하여 실제 워크로드에서 정적 컴파일러가 생성 한 코드보다 코드가 훨씬 빠르게 실행됩니다. 프로파일 가이드 최적화 기능이있는 정적 컴파일러와 달리 LuaJIT는 런타임시 실행 경로 변경에 적응합니다.

  • 역 최적화 또한 중요하다. 몽키 패칭 동작을 수행하는 JIT 컴파일 코드 대신 몽키 패칭 동작은 이전 정의에 의존하는 기계어 코드를 버리는 런타임 시스템에서 후크를 트리거합니다.


이것은 어떻게 대답합니까? 글쎄, 글 머리 기호 3은 참조를 추가 한 경우 일 수 있습니다.
Raphael

아주 PGO 일반적인 워크로드에서 웹 응용 프로그램 코드에 대한 LuaJIT의 성능을 일치 수없는 주장에 회의적.
Konrad Rudolph

@KonradRudolph JIT의 주요 장점은 다른 경로가 뜨거워 질 때 JIT가 코드를 조정한다는 것입니다.
데미

@Demetri 나는 이것을 알고있다. 그러나 이것이 이점인지 여부를 정량화하는 것은 매우 어렵습니다. 내 답변과 의견 토론을 참조하십시오. 간단히 말해서 : JIT는 사용량의 변화에 ​​적응할 수 있지만 런타임에 물건을 추적해야하기 때문에 오버 헤드가 발생합니다. 이를위한 손익분기 점은 행동의 빈번한 변화가 발생하는 경우에만 직관적입니다. 웹 응용 프로그램의 경우 최적화에 따른 단일 또는 거의 사용 패턴이있을 수 있으므로 적응성으로 인한 최소한의 성능 향상이 지속적인 프로파일 링의 오버 헤드를 상쇄하지 않습니다.
Konrad Rudolph
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.