Computer >> 컴퓨터 >  >> 프로그램 작성 >> Ruby

Ruby 2.6의 MJIT란 무엇이며 어떻게 작동합니까?

Ruby의 성능은 버전이 거듭될수록 많이 개선되고 있습니다. Ruby 개발 팀은 Ruby를 더욱 빠르게 만들기 위해 모든 노력을 기울이고 있습니다!

이러한 노력 중 하나가 3×3 프로젝트입니다.

목표?

Ruby 3.0은 Ruby 2.0보다 3배 빠릅니다. .

이 프로젝트의 일부는 이 기사의 주제인 새로운 MJIT 컴파일러입니다.

MJIT 설명

MJIT는 "Method Based Just-in-Time Compiler"의 약자입니다.

그게 무슨 뜻인가요?

Ruby는 코드를 YARV 명령어로 컴파일합니다. , 이 지침은 Ruby 가상 머신에 의해 실행됩니다.

JIT는 여기에 다른 레이어를 추가합니다.

자주 사용되는 명령어를 컴파일합니다. 바이너리 코드로.

그 결과 코드를 더 빠르게 실행하는 최적화된 바이너리가 생성됩니다.

작동 방식

MJIT가 더 잘 이해하기 위해 어떻게 작동하는지 살펴보겠습니다.

Ruby 2.6 및 --jit로 JIT를 활성화할 수 있습니다. 옵션.

좋아요 :

ruby --jit app.rb

Ruby 2.6에는 작동 방식을 정확히 찾는 데 도움이 되는 일련의 JIT 관련 옵션이 있습니다. ruby --help를 실행하여 이러한 옵션을 볼 수 있습니다. .

옵션 목록은 다음과 같습니다.

  • -잠깐
  • -지트 장황함
  • –jit-save-temps
  • –jit-max-cache
  • -jit-min-calls

장황한 옵션이 좋은 출발점인 것 같습니다!

우리는 또한 --jit-wait를 사용할 것입니다. , 이렇게 하면 Ruby가 JIT 코드 컴파일이 완료될 때까지 기다렸다가 실행합니다.

정상 작동 중 JIT는 작업자 스레드에서 코드를 컴파일 &완료될 때까지 기다리지 않습니다.

이를 테스트하기 위해 실행할 수 있는 명령은 다음과 같습니다.

ruby --disable-gems --jit --jit-verbose=1 --jit-wait -e "4.times { 123 }"

인쇄물 :

Successful MJIT finish

글쎄요, 별로 흥미롭지 않죠?

JIT는 아무것도 하지 않습니다.

왜?

기본적으로 JIT는 메서드가 5번 호출될 때만 작동하기 때문입니다. (jit-min-calls ) 이상입니다.

이것을 실행하면:

ruby --disable-gems --jit --jit-verbose=1 --jit-wait -e "5.times { 123 }"

이제 흥미로운 것을 얻을 수 있습니다. :

JIT success (32.1ms): block in <main>@-e:1 -> /tmp/_ruby_mjit_p13921u0.c
에서 차단

이것은 무엇을 의미합니까?

JIT는 블록을 5번 호출했기 때문에 블록을 컴파일했으며 다음을 알 수 있습니다.

  • 컴파일에 걸린 시간 (32.1ms ),
  • 정확히 컴파일된 내용 (block in <main> )
  • 생성된 파일(/tmp/_ruby_mjit_p13921u0.c ) 이 편집의 소스로

이 파일은 오브젝트 파일(.o)로 컴파일된 C 소스 코드입니다. ) 다음 공유 라이브러리 파일(.so ).

--jit-save-temps를 추가하면 이러한 파일에 액세스할 수 있습니다. 옵션.

예시 :

Ruby 2.6의 MJIT란 무엇이며 어떻게 작동합니까?

이것이 JIT의 작동 방식에 대한 나의 현재 이해입니다. :

  1. 메소드 호출 수
  2. 한 메소드가 5번 호출될 때(jit-min-calls의 기본값 ) JIT 트리거
  3. 이 방법에 대한 지침이 포함된 C 파일이 생성됩니다(YARV 지침이지만 인라인됨)
  4. 컴파일은 백그라운드에서 발생합니다(--jit-wait 제외). ) GCC와 같은 일반 C 컴파일러 사용
  5. 컴파일이 완료되면 이 메서드가 호출될 때 결과 공유 라이브러리 파일이 사용됩니다.

이것이 얼마나 효과적인지 봅시다.

MJIT 테스트:정말 더 빠릅니까?

MJIT의 목표는 Ruby를 더 빠르게 만드는 것입니다.

지금 그렇게 하는 것이 얼마나 좋은가요?

알아보자!

첫째, 마이크로벤치마크:

벤치마크 결과(JIT가 없는 Ruby 2.6과 비교)
동안 8배 빨라짐
문자열 추가가 있는 동안 10% 더 빨라짐
곱하는 동안(정수) 4배 빨라짐
곱하는 동안(Bignum) 20% 느려짐
문자열 대문자 10% 더 빨라짐
문자열 일치 2% 느려짐
문자열 일치? 10% 더 빨라짐
10,000개의 난수로 구성된 배열 20% 더 빨라짐

성능이 여기저기서 나오는 것 같지만, 이것에서 유추할 수 있는 것이 있습니다...

MJIT는 루프를 정말 좋아합니다!

그러나 더 복잡한 응용 프로그램에서는 어떻게 될까요?

간단한 Sinatra 앱으로 시도해 보겠습니다. :

require 'sinatra'

get '/' do
  "apples, oranges & bananas"
end

별 것 아닌 것 같지만 이 작은 코드는 500가지가 넘는 다양한 방법을 실행합니다. JIT가 할 일을 주기에 충분합니다!

<블록 인용>

정확히 말하면 Sinatra 2.0.4 with Thin 1.7.2입니다.

다음 명령으로 벤치마크를 실행할 수 있습니다(아파치 벤치):

ab -c 20 -t 10 https://localhost:4567/

결과입니다 :

Ruby 2.6의 MJIT란 무엇이며 어떻게 작동합니까?

Ruby 2.6이 2.5보다 빠르지만 JIT를 활성화하면 Sinatra가 11% 느려집니다. !

왜?

잘 모르겠습니다. JIT에 의해 오버헤드가 발생하거나 코드가 제대로 최적화되지 않았기 때문일 수 있습니다.

C 프로파일러(callgrind)로 테스트한 결과 JIT 최적화 코드(이전에 발견한 컴파일된 C 파일)의 사용이 Sinatra에서 매우 낮습니다(2% 미만). ), 하지만 매우 높습니다(24.22% ) 8배 속도 향상을 가져오는 while 문에 대해.

JIT를 사용한 while 벤치마크 결과 :

Ruby 2.6의 MJIT란 무엇이며 어떻게 작동합니까?

JIT를 사용한 Sinatra 벤치마크 결과 :

Ruby 2.6의 MJIT란 무엇이며 어떻게 작동합니까?

제가 컴파일러 전문가가 아니라서 어떤 결론도 내릴 수 없는 이유 중 하나일 수 있습니다.

요약

MJIT는 Ruby 2.6에서 사용할 수 있는 "Just-in-Time 컴파일러"이며 --jit로 활성화할 수 있습니다. 깃발. MJIT는 유망하며 일부 소규모 프로그램의 속도를 높일 수 있지만 아직 해야 할 일이 많습니다!

이 기사가 마음에 들면 Ruby 친구들과 공유하는 것을 잊지 마세요 🙂

읽어주셔서 감사합니다.