Virtualizables

참고: 이 문서에는 기본 사항을 이해하는 방법에 대한 제대로 된 소개가 없습니다. 이에 대해 몇 가지 내용을 작성해야 합니다. 혹시 이 문서를 보다가 맥락이 부족하다고 느끼신다면, 편하게 IRC에서 저희를 귀찮게 해 주셔도 좋습니다.

문제 설명

JIT은 객체가 트레이스를 벗어나지 않으면 그 객체가 절대 할당되지 않도록 보장하는 데 매우 뛰어납니다. 이러한 객체를 virtuals라고 부릅니다. 그러나 프레임을 다루는 경우에는 virtuals만으로는 흔히 충분하지 않습니다. 프레임은 벗어날 수도 있고, JIT에 진입하는 시점에 이미 할당되어 있을 수도 있습니다. 이런 경우에는 힙에 존재하더라도 여전히 최적화되어 제거될 수 있는 추가 객체가 필요합니다.

해결책

virtualizable을 소개합니다. 이들은 힙(heap)에 존재하는 객체이지만, 그 필드는 어셈블러에서 일어나는 일과 항상 동기화되어 있지는 않습니다. 한 가지 예로, virtualizable 필드는 virtual 객체를 강제(forcing)하지 않고도 저장할 수 있습니다. 이는 프레임에 매우 유용합니다. 객체를 virtualizable로 선언하는 방법은 다음과 같습니다:

class Frame(object):
   _virtualizable_ = ['locals[*]', 'stackdepth']

그리고 이를 다음과 같이 JitDriver에서 사용합니다:

jitdriver = JitDriver(greens=[], reds=['frame'], virtualizables=['frame'])

이 선언은 stackdepth가 가상화 가능(virtualizable) 필드이고, locals는 가상화 가능한 것에 저장되는 리스트인 가상화 가능 배열임을 의미합니다. virtualizable을 사용하는 데는 다양한 규칙이 있으며, 특히 가상화 가능 배열을 사용할 때는 매우 혼란스러울 수 있습니다. 이러한 경우는 대개 (이상한 동작이 아니라) 컴파일 타임 오류로 끝나게 됩니다. 규칙은 다음과 같습니다:

  • virtualizable 배열은 고정 크기 리스트여야 합니다. 초기화된 후에는 (예: Frame.__init__에서) 전혀 크기를 조정할 수 없습니다. 해당 필드에 다른 리스트를 할당할 수도 없고, 리스트를 주변으로 전달할 수도 없습니다. frame.array[index]에만 직접 접근할 수 있습니다.

  • 각 배열 접근은 IndexError를 발생시킬 수 없는, 알려진 양의 인덱스로 이루어져야 합니다. index = jit.hint(index, promote=True)를 사용하면 상수 인덱스 접근을 얻는 데 유용할 수 있습니다. 이는 사용자 코드의 맥락에서 인덱스가 실제로 상수이거나 드물게 변경되는 경우에만 안전합니다.

  • JIT에서 새로운 virtualizable을 초기화하는 경우, 다음과 같이 해야 합니다(예를 들어 Frame.__init__에 있는 경우):

    self = hint(self, access_directly=True, fresh_virtualizable=True)
    

    그렇게 하면 필드를 직접 채울 수 있습니다.

  • JIT 외부에서 virtualizable을 사용하면 매우 비용이 많이 들며, 때로는 트레이싱을 중단시키기도 합니다. 이를 디버깅 목적으로만 사용하고 매번 사용하지 않도록 어떻게 할지 신중히 고려하십시오(예: sys._getframe 호출).

  • Python 제너레이터에 상응하는 것이 있어서 virtualizable이 더 오래 유지되는 경우, 반환하기 전에 이를 강제화(force)해야 합니다. 그렇게 하는 편이 나중에 외부 호출로 하는 것보다 낫습니다. 이는 jit.hint(frame, force_virtualizable=True)를 사용하여 수행합니다.

  • 여러분의 인터프리터는 위의 frame과 유사한 지역 변수를 가지고 있어야 합니다. 이 변수는 jit_merge_point 루프를 실행하는 동안 수정되어서는 안 되며, 루프 안에서는 jit_merge_point()can_enter_jit() 호출에 직접 전달되어야 합니다. JIT 생성기는 동일하게 수정되지 않은 지역 변수를 재사용하는 대신 매 반복마다 어딘가에서 virtualizable을 가져오면 버그가 있는 코드를 생성하는 것으로 알려져 있습니다.