Virtualizables ============== **참고:** 이 문서에는 기본 사항을 이해하는 방법에 대한 제대로 된 소개가 없습니다. 이에 대해 몇 가지 내용을 작성해야 합니다. 혹시 이 문서를 보다가 맥락이 부족하다고 느끼신다면, 편하게 IRC에서 저희를 귀찮게 해 주셔도 좋습니다. 문제 설명 --------- JIT은 객체가 트레이스를 벗어나지 않으면 그 객체가 절대 할당되지 않도록 보장하는 데 매우 뛰어납니다. 이러한 객체를 ``virtuals``\ 라고 부릅니다. 그러나 프레임을 다루는 경우에는 virtuals만으로는 흔히 충분하지 않습니다. 프레임은 벗어날 수도 있고, JIT에 진입하는 시점에 이미 할당되어 있을 수도 있습니다. 이런 경우에는 힙에 존재하더라도 여전히 최적화되어 제거될 수 있는 추가 객체가 필요합니다. 해결책 ------ virtualizable을 소개합니다. 이들은 힙(heap)에 존재하는 객체이지만, 그 필드는 어셈블러에서 일어나는 일과 항상 동기화되어 있지는 않습니다. 한 가지 예로, virtualizable 필드는 virtual 객체를 강제(forcing)하지 않고도 저장할 수 있습니다. 이는 프레임에 매우 유용합니다. 객체를 virtualizable로 선언하는 방법은 다음과 같습니다: .. code-block:: python class Frame(object): _virtualizable_ = ['locals[*]', 'stackdepth'] 그리고 이를 다음과 같이 ``JitDriver``\ 에서 사용합니다: .. code-block:: python 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__``\ 에 있는 경우): .. code-block:: python 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을 가져오면 버그가 있는 코드를 생성하는 것으로 알려져 있습니다.