<div dir="ltr"><br><br><div class="gmail_quote"><div dir="ltr">On Sun, 19 Jun 2016 at 21:01 Mark Shannon <<a href="mailto:mark@hotpy.org">mark@hotpy.org</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><br>
<br>
On 19/06/16 18:29, Brett Cannon wrote:<br>
><br>
><br>
> On Sat, 18 Jun 2016 at 21:49 Guido van Rossum <<a href="mailto:guido@python.org" target="_blank">guido@python.org</a><br>
> <mailto:<a href="mailto:guido@python.org" target="_blank">guido@python.org</a>>> wrote:<br>
><br>
> Hi Brett,<br>
><br>
> I've got a few questions about the specific design. Probably you<br>
> know the answers, it would be nice to have them in the PEP.<br>
><br>
><br>
> Once you're happy with my answers I'll update the PEP.<br>
><br>
><br>
> First, why not have a global hook? What does a hook per interpreter<br>
> give you? Would even finer granularity buy anything?<br>
><br>
><br>
> We initially considered a per-code object hook, but we figured it was<br>
> unnecessary to have that level of control, especially since people like<br>
> Numba have gotten away with not needing it for this long (although I<br>
> suspect that's because they are a decorator so they can just return an<br>
> object that overrides __call__()). We didn't think that a global one was<br>
> appropriate as different workloads may call for different<br>
> JITs/debuggers/etc. and there is no guarantee that you are executing<br>
> every interpreter with the same workload. Plus we figured people might<br>
> simply import their JIT of choice and as a side-effect set the hook, and<br>
> since imports are a per-interpreter thing that seemed to suggest the<br>
> granularity of interpreters.<br>
><br>
> IOW it seemed to be more in line with sys.settrace() than some global<br>
> thing for the process.<br>
><br>
><br>
> Next, I'm a bit (but no more than a bit) concerned about the extra 8<br>
> bytes per code object, especially since for most people this is just<br>
> waste (assuming most people won't be using Pyjion or Numba). Could<br>
> it be a compile-time feature (requiring recompilation of CPython but<br>
> not extensions)?<br>
><br>
><br>
> Probably. It does water down potential usage thanks to needing a special<br>
> build. If the decision is "special build or not", I would simply pull<br>
> out this part of the proposal as I wouldn't want to add a flag that<br>
> influences what is or is not possible for an interpreter.<br>
><br>
> Could you figure out some other way to store per-code-object data?<br>
> It seems you considered this but decided that the co_extra field was<br>
> simpler and faster; I'm basically pushing a little harder on this.<br>
> Of course most of the PEP would disappear without this feature; the<br>
> extra interpreter field is fine.<br>
><br>
><br>
> Dino and I thought of two potential alternatives, neither of which we<br>
> have taken the time to implement and benchmark. One is to simply have a<br>
> hash table of memory addresses to JIT data that is kept on the JIT side<br>
> of things. Obviously it would be nice to avoid the overhead of a hash<br>
> table lookup on every function call. This also doesn't help minimize<br>
> memory when the code object gets GC'ed.<br>
<br>
Hash lookups aren't that slow.</blockquote><div><br></div><div>There's "slow" and there's "slower".<br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"> If you combine it with the custom flags<br>
suggested by MRAB, then you would only suffer the lookup penalty when<br>
actually entering the special interpreter.<br></blockquote><div><br></div><div>You actually will always need the lookup in the JIT case to increment the execution count if you're not always immediately JIT-ing. That means MRAB's flag won't necessarily be that useful in the JIT case (it could in the debugging case, though, if you're really aiming for the fastest debugger possible).<br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
You can use a weakref callback to ensure things get GC'd properly.<br></blockquote><div><br></div><div>Yes, that was already the plan if we lost co_extra.<br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<br>
Also, if there is a special extra field on code-object, then everyone<br>
will want to use it. How do you handle clashes?<br></blockquote><div><br></div><div>As already explained in the PEP in <a href="https://www.python.org/dev/peps/pep-0523/#expanding-pycodeobject">https://www.python.org/dev/peps/pep-0523/#expanding-pycodeobject</a>, like consenting adults. The expectation is that there will not be multiple users of the object at the same time.<br><br></div><div>-Brett<br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<br>
><br>
> The other potential solution we came up with was to use weakrefs. I have<br>
> not looked into the details, but we were thinking that if we registered<br>
> the JIT data object as a weakref on the code object, couldn't we iterate<br>
> through the weakrefs attached to the code object to look for the JIT<br>
> data object, and then get the reference that way? It would let us avoid<br>
> a more expensive hash table lookup if we assume most code objects won't<br>
> have a weakref on it (assuming weakrefs are stored in a list), and it<br>
> gives us the proper cleanup semantics we want by getting the weakref<br>
> cleanup callback execution to make sure we decref the JIT data object<br>
> appropriately. But as I said, I have not looked into the feasibility of<br>
> this at all to know if I'm remembering the weakref implementation<br>
> details correctly.<br>
><br>
><br>
> Finally, there are some error messages from pep2html.py:<br>
> <a href="https://www.python.org/dev/peps/pep-0523/#copyright" rel="noreferrer" target="_blank">https://www.python.org/dev/peps/pep-0523/#copyright</a><br>
><br>
><br>
> All fixed in<br>
> <a href="https://github.com/python/peps/commit/6929f850a5af07e51d0163558a5fe8d6b85dccfe" rel="noreferrer" target="_blank">https://github.com/python/peps/commit/6929f850a5af07e51d0163558a5fe8d6b85dccfe</a> .<br>
><br>
> -Brett<br>
><br>
><br>
><br>
> --Guido<br>
><br>
> On Fri, Jun 17, 2016 at 7:58 PM, Brett Cannon <<a href="mailto:brett@python.org" target="_blank">brett@python.org</a><br>
> <mailto:<a href="mailto:brett@python.org" target="_blank">brett@python.org</a>>> wrote:<br>
><br>
> I have taken PEP 523 for this:<br>
> <a href="https://github.com/python/peps/blob/master/pep-0523.txt" rel="noreferrer" target="_blank">https://github.com/python/peps/blob/master/pep-0523.txt</a> .<br>
><br>
> I'm waiting until Guido gets back from vacation, at which point<br>
> I'll ask for a pronouncement or assignment of a BDFL delegate.<br>
><br>
> On Fri, 3 Jun 2016 at 14:37 Brett Cannon <<a href="mailto:brett@python.org" target="_blank">brett@python.org</a><br>
> <mailto:<a href="mailto:brett@python.org" target="_blank">brett@python.org</a>>> wrote:<br>
><br>
> For those of you who follow python-ideas or were at the<br>
> PyCon US 2016 language summit, you have already seen/heard<br>
> about this PEP. For those of you who don't fall into either<br>
> of those categories, this PEP proposed a frame evaluation<br>
> API for CPython. The motivating example of this work has<br>
> been Pyjion, the experimental CPython JIT Dino Viehland and<br>
> I have been working on in our spare time at Microsoft. The<br>
> API also works for debugging, though, as already<br>
> demonstrated by Google having added a very similar API<br>
> internally for debugging purposes.<br>
><br>
> The PEP is pasted in below and also available in rendered<br>
> form at<br>
> <a href="https://github.com/Microsoft/Pyjion/blob/master/pep.rst" rel="noreferrer" target="_blank">https://github.com/Microsoft/Pyjion/blob/master/pep.rst</a> (I<br>
> will assign myself a PEP # once discussion is finished as<br>
> it's easier to work in git for this for the rich rendering<br>
> of the in-progress PEP).<br>
><br>
> I should mention that the difference from python-ideas and<br>
> the language summit in the PEP are the listed support from<br>
> Google's use of a very similar API as well as clarifying the<br>
> co_extra field on code objects doesn't change their<br>
> immutability (at least from the view of the PEP).<br>
><br>
> ----------<br>
> PEP: NNN<br>
> Title: Adding a frame evaluation API to CPython<br>
> Version: $Revision$<br>
> Last-Modified: $Date$<br>
> Author: Brett Cannon <<a href="mailto:brett@python.org" target="_blank">brett@python.org</a><br>
> <mailto:<a href="mailto:brett@python.org" target="_blank">brett@python.org</a>>>,<br>
> Dino Viehland <<a href="mailto:dinov@microsoft.com" target="_blank">dinov@microsoft.com</a><br>
> <mailto:<a href="mailto:dinov@microsoft.com" target="_blank">dinov@microsoft.com</a>>><br>
> Status: Draft<br>
> Type: Standards Track<br>
> Content-Type: text/x-rst<br>
> Created: 16-May-2016<br>
> Post-History: 16-May-2016<br>
> 03-Jun-2016<br>
><br>
><br>
> Abstract<br>
> ========<br>
><br>
> This PEP proposes to expand CPython's C API [#c-api]_ to<br>
> allow for<br>
> the specification of a per-interpreter function pointer to<br>
> handle the<br>
> evaluation of frames [#pyeval_evalframeex]_. This proposal also<br>
> suggests adding a new field to code objects [#pycodeobject]_<br>
> to store<br>
> arbitrary data for use by the frame evaluation function.<br>
><br>
><br>
> Rationale<br>
> =========<br>
><br>
> One place where flexibility has been lacking in Python is in<br>
> the direct<br>
> execution of Python code. While CPython's C API [#c-api]_<br>
> allows for<br>
> constructing the data going into a frame object and then<br>
> evaluating it<br>
> via ``PyEval_EvalFrameEx()`` [#pyeval_evalframeex]_, control<br>
> over the<br>
> execution of Python code comes down to individual objects<br>
> instead of a<br>
> hollistic control of execution at the frame level.<br>
><br>
> While wanting to have influence over frame evaluation may<br>
> seem a bit<br>
> too low-level, it does open the possibility for things such as a<br>
> method-level JIT to be introduced into CPython without<br>
> CPython itself<br>
> having to provide one. By allowing external C code to<br>
> control frame<br>
> evaluation, a JIT can participate in the execution of Python<br>
> code at<br>
> the key point where evaluation occurs. This then allows for<br>
> a JIT to<br>
> conditionally recompile Python bytecode to machine code as<br>
> desired<br>
> while still allowing for executing regular CPython bytecode when<br>
> running the JIT is not desired. This can be accomplished by<br>
> allowing<br>
> interpreters to specify what function to call to evaluate a<br>
> frame. And<br>
> by placing the API at the frame evaluation level it allows for a<br>
> complete view of the execution environment of the code for<br>
> the JIT.<br>
><br>
> This ability to specify a frame evaluation function also<br>
> allows for<br>
> other use-cases beyond just opening CPython up to a JIT. For<br>
> instance,<br>
> it would not be difficult to implement a tracing or<br>
> profiling function<br>
> at the call level with this API. While CPython does provide the<br>
> ability to set a tracing or profiling function at the Python<br>
> level,<br>
> this would be able to match the data collection of the<br>
> profiler and<br>
> quite possibly be faster for tracing by simply skipping per-line<br>
> tracing support.<br>
><br>
> It also opens up the possibility of debugging where the frame<br>
> evaluation function only performs special debugging work when it<br>
> detects it is about to execute a specific code object. In that<br>
> instance the bytecode could be theoretically rewritten<br>
> in-place to<br>
> inject a breakpoint function call at the proper point for<br>
> help in<br>
> debugging while not having to do a heavy-handed approach as<br>
> required by ``sys.settrace()``.<br>
><br>
> To help facilitate these use-cases, we are also proposing<br>
> the adding<br>
> of a "scratch space" on code objects via a new field. This<br>
> will allow<br>
> per-code object data to be stored with the code object<br>
> itself for easy<br>
> retrieval by the frame evaluation function as necessary. The<br>
> field<br>
> itself will simply be a ``PyObject *`` type so that any data<br>
> stored in<br>
> the field will participate in normal object memory management.<br>
><br>
><br>
> Proposal<br>
> ========<br>
><br>
> All proposed C API changes below will not be part of the<br>
> stable ABI.<br>
><br>
><br>
> Expanding ``PyCodeObject``<br>
> --------------------------<br>
><br>
> One field is to be added to the ``PyCodeObject`` struct<br>
> [#pycodeobject]_::<br>
><br>
> typedef struct {<br>
> ...<br>
> PyObject *co_extra; /* "Scratch space" for the code<br>
> object. */<br>
> } PyCodeObject;<br>
><br>
> The ``co_extra`` will be ``NULL`` by default and will not be<br>
> used by<br>
> CPython itself. Third-party code is free to use the field as<br>
> desired.<br>
> Values stored in the field are expected to not be required<br>
> in order<br>
> for the code object to function, allowing the loss of the<br>
> data of the<br>
> field to be acceptable (this keeps the code object as<br>
> immutable from<br>
> a functionality point-of-view; this is slightly contentious<br>
> and so is<br>
> listed as an open issue in `Is co_extra needed?`_). The<br>
> field will be<br>
> freed like all other fields on ``PyCodeObject`` during<br>
> deallocation<br>
> using ``Py_XDECREF()``.<br>
><br>
> It is not recommended that multiple users attempt to use the<br>
> ``co_extra`` simultaneously. While a dictionary could<br>
> theoretically be<br>
> set to the field and various users could use a key specific<br>
> to the<br>
> project, there is still the issue of key collisions as well as<br>
> performance degradation from using a dictionary lookup on<br>
> every frame<br>
> evaluation. Users are expected to do a type check to make<br>
> sure that<br>
> the field has not been previously set by someone else.<br>
><br>
><br>
> Expanding ``PyInterpreterState``<br>
> --------------------------------<br>
><br>
> The entrypoint for the frame evalution function is<br>
> per-interpreter::<br>
><br>
> // Same type signature as PyEval_EvalFrameEx().<br>
> typedef PyObject* (__stdcall<br>
> *PyFrameEvalFunction)(PyFrameObject*, int);<br>
><br>
> typedef struct {<br>
> ...<br>
> PyFrameEvalFunction eval_frame;<br>
> } PyInterpreterState;<br>
><br>
> By default, the ``eval_frame`` field will be initialized to<br>
> a function<br>
> pointer that represents what ``PyEval_EvalFrameEx()``<br>
> currently is<br>
> (called ``PyEval_EvalFrameDefault()``, discussed later in<br>
> this PEP).<br>
> Third-party code may then set their own frame evaluation<br>
> function<br>
> instead to control the execution of Python code. A pointer<br>
> comparison<br>
> can be used to detect if the field is set to<br>
> ``PyEval_EvalFrameDefault()`` and thus has not been mutated yet.<br>
><br>
><br>
> Changes to ``Python/ceval.c``<br>
> -----------------------------<br>
><br>
> ``PyEval_EvalFrameEx()`` [#pyeval_evalframeex]_ as it<br>
> currently stands<br>
> will be renamed to ``PyEval_EvalFrameDefault()``. The new<br>
> ``PyEval_EvalFrameEx()`` will then become::<br>
><br>
> PyObject *<br>
> PyEval_EvalFrameEx(PyFrameObject *frame, int throwflag)<br>
> {<br>
> PyThreadState *tstate = PyThreadState_GET();<br>
> return tstate->interp->eval_frame(frame, throwflag);<br>
> }<br>
><br>
> This allows third-party code to place themselves directly in<br>
> the path<br>
> of Python code execution while being backwards-compatible<br>
> with code<br>
> already using the pre-existing C API.<br>
><br>
><br>
> Updating ``python-gdb.py``<br>
> --------------------------<br>
><br>
> The generated ``python-gdb.py`` file used for Python support<br>
> in GDB<br>
> makes some hard-coded assumptions about<br>
> ``PyEval_EvalFrameEx()``, e.g.<br>
> the names of local variables. It will need to be updated to<br>
> work with<br>
> the proposed changes.<br>
><br>
><br>
> Performance impact<br>
> ==================<br>
><br>
> As this PEP is proposing an API to add pluggability, performance<br>
> impact is considered only in the case where no third-party<br>
> code has<br>
> made any changes.<br>
><br>
> Several runs of pybench [#pybench]_ consistently showed no<br>
> performance<br>
> cost from the API change alone.<br>
><br>
> A run of the Python benchmark suite [#py-benchmarks]_ showed no<br>
> measurable cost in performance.<br>
><br>
> In terms of memory impact, since there are typically not<br>
> many CPython<br>
> interpreters executing in a single process that means the<br>
> impact of<br>
> ``co_extra`` being added to ``PyCodeObject`` is the only worry.<br>
> According to [#code-object-count]_, a run of the Python test<br>
> suite<br>
> results in about 72,395 code objects being created. On a 64-bit<br>
> CPU that would result in 579,160 bytes of extra memory being<br>
> used if<br>
> all code objects were alive at once and had nothing set in their<br>
> ``co_extra`` fields.<br>
><br>
><br>
> Example Usage<br>
> =============<br>
><br>
> A JIT for CPython<br>
> -----------------<br>
><br>
> Pyjion<br>
> ''''''<br>
><br>
> The Pyjion project [#pyjion]_ has used this proposed API to<br>
> implement<br>
> a JIT for CPython using the CoreCLR's JIT [#coreclr]_. Each code<br>
> object has its ``co_extra`` field set to a<br>
> ``PyjionJittedCode`` object<br>
> which stores four pieces of information:<br>
><br>
> 1. Execution count<br>
> 2. A boolean representing whether a previous attempt to JIT<br>
> failed<br>
> 3. A function pointer to a trampoline (which can be type<br>
> tracing or not)<br>
> 4. A void pointer to any JIT-compiled machine code<br>
><br>
> The frame evaluation function has (roughly) the following<br>
> algorithm::<br>
><br>
> def eval_frame(frame, throw_flag):<br>
> pyjion_code = frame.code.co_extra<br>
> if not pyjion_code:<br>
> frame.code.co_extra = PyjionJittedCode()<br>
> elif not pyjion_code.jit_failed:<br>
> if not pyjion_code.jit_code:<br>
> return<br>
> pyjion_code.eval(pyjion_code.jit_code, frame)<br>
> elif pyjion_code.exec_count > 20_000:<br>
> if jit_compile(frame):<br>
> return<br>
> pyjion_code.eval(pyjion_code.jit_code, frame)<br>
> else:<br>
> pyjion_code.jit_failed = True<br>
> pyjion_code.exec_count += 1<br>
> return PyEval_EvalFrameDefault(frame, throw_flag)<br>
><br>
> The key point, though, is that all of this work and logic is<br>
> separate<br>
> from CPython and yet with the proposed API changes it is able to<br>
> provide a JIT that is compliant with Python semantics (as of<br>
> this<br>
> writing, performance is almost equivalent to CPython without<br>
> the new<br>
> API). This means there's nothing technically preventing<br>
> others from<br>
> implementing their own JITs for CPython by utilizing the<br>
> proposed API.<br>
><br>
><br>
> Other JITs<br>
> ''''''''''<br>
><br>
> It should be mentioned that the Pyston team was consulted on an<br>
> earlier version of this PEP that was more JIT-specific and<br>
> they were<br>
> not interested in utilizing the changes proposed because<br>
> they want<br>
> control over memory layout they had no interest in directly<br>
> supporting<br>
> CPython itself. An informal discusion with a developer on<br>
> the PyPy<br>
> team led to a similar comment.<br>
><br>
> Numba [#numba]_, on the other hand, suggested that they would be<br>
> interested in the proposed change in a post-1.0 future for<br>
> themselves [#numba-interest]_.<br>
><br>
> The experimental Coconut JIT [#coconut]_ could have<br>
> benefitted from<br>
> this PEP. In private conversations with Coconut's creator we<br>
> were told<br>
> that our API was probably superior to the one they developed for<br>
> Coconut to add JIT support to CPython.<br>
><br>
><br>
> Debugging<br>
> ---------<br>
><br>
> In conversations with the Python Tools for Visual Studio<br>
> team (PTVS)<br>
> [#ptvs]_, they thought they would find these API changes<br>
> useful for<br>
> implementing more performant debugging. As mentioned in the<br>
> Rationale_<br>
> section, this API would allow for switching on debugging<br>
> functionality<br>
> only in frames where it is needed. This could allow for either<br>
> skipping information that ``sys.settrace()`` normally<br>
> provides and<br>
> even go as far as to dynamically rewrite bytecode prior to<br>
> execution<br>
> to inject e.g. breakpoints in the bytecode.<br>
><br>
> It also turns out that Google has provided a very similar API<br>
> internally for years. It has been used for performant debugging<br>
> purposes.<br>
><br>
><br>
> Implementation<br>
> ==============<br>
><br>
> A set of patches implementing the proposed API is available<br>
> through<br>
> the Pyjion project [#pyjion]_. In its current form it has more<br>
> changes to CPython than just this proposed API, but that is<br>
> for ease<br>
> of development instead of strict requirements to accomplish<br>
> its goals.<br>
><br>
><br>
> Open Issues<br>
> ===========<br>
><br>
> Allow ``eval_frame`` to be ``NULL``<br>
> -----------------------------------<br>
><br>
> Currently the frame evaluation function is expected to<br>
> always be set.<br>
> It could very easily simply default to ``NULL`` instead<br>
> which would<br>
> signal to use ``PyEval_EvalFrameDefault()``. The current<br>
> proposal of<br>
> not special-casing the field seemed the most<br>
> straight-forward, but it<br>
> does require that the field not accidentally be cleared,<br>
> else a crash<br>
> may occur.<br>
><br>
><br>
> Is co_extra needed?<br>
> -------------------<br>
><br>
> While discussing this PEP at PyCon US 2016, some core developers<br>
> expressed their worry of the ``co_extra`` field making code<br>
> objects<br>
> mutable. The thinking seemed to be that having a field that was<br>
> mutated after the creation of the code object made the<br>
> object seem<br>
> mutable, even though no other aspect of code objects changed.<br>
><br>
> The view of this PEP is that the `co_extra` field doesn't<br>
> change the<br>
> fact that code objects are immutable. The field is specified<br>
> in this<br>
> PEP as to not contain information required to make the code<br>
> object<br>
> usable, making it more of a caching field. It could be viewed as<br>
> similar to the UTF-8 cache that string objects have internally;<br>
> strings are still considered immutable even though they have<br>
> a field<br>
> that is conditionally set.<br>
><br>
> The field is also not strictly necessary. While the field<br>
> greatly<br>
> simplifies attaching extra information to code objects,<br>
> other options<br>
> such as keeping a mapping of code object memory addresses to<br>
> what<br>
> would have been kept in ``co_extra`` or perhaps using a weak<br>
> reference<br>
> of the data on the code object and then iterating through<br>
> the weak<br>
> references until the attached data is found is possible. But<br>
> obviously<br>
> all of these solutions are not as simple or performant as<br>
> adding the<br>
> ``co_extra`` field.<br>
><br>
><br>
> Rejected Ideas<br>
> ==============<br>
><br>
> A JIT-specific C API<br>
> --------------------<br>
><br>
> Originally this PEP was going to propose a much larger API<br>
> change<br>
> which was more JIT-specific. After soliciting feedback from<br>
> the Numba<br>
> team [#numba]_, though, it became clear that the API was<br>
> unnecessarily<br>
> large. The realization was made that all that was truly<br>
> needed was the<br>
> opportunity to provide a trampoline function to handle<br>
> execution of<br>
> Python code that had been JIT-compiled and a way to attach that<br>
> compiled machine code along with other critical data to the<br>
> corresponding Python code object. Once it was shown that<br>
> there was no<br>
> loss in functionality or in performance while minimizing the API<br>
> changes required, the proposal was changed to its current form.<br>
><br>
><br>
> References<br>
> ==========<br>
><br>
> .. [#pyjion] Pyjion project<br>
> (<a href="https://github.com/microsoft/pyjion" rel="noreferrer" target="_blank">https://github.com/microsoft/pyjion</a>)<br>
><br>
> .. [#c-api] CPython's C API<br>
> (<a href="https://docs.python.org/3/c-api/index.html" rel="noreferrer" target="_blank">https://docs.python.org/3/c-api/index.html</a>)<br>
><br>
> .. [#pycodeobject] ``PyCodeObject``<br>
> (<a href="https://docs.python.org/3/c-api/code.html#c.PyCodeObject" rel="noreferrer" target="_blank">https://docs.python.org/3/c-api/code.html#c.PyCodeObject</a>)<br>
><br>
> .. [#coreclr] .NET Core Runtime (CoreCLR)<br>
> (<a href="https://github.com/dotnet/coreclr" rel="noreferrer" target="_blank">https://github.com/dotnet/coreclr</a>)<br>
><br>
> .. [#pyeval_evalframeex] ``PyEval_EvalFrameEx()``<br>
><br>
> (<a href="https://docs.python.org/3/c-api/veryhigh.html?highlight=pyframeobject#c.PyEval_EvalFrameEx" rel="noreferrer" target="_blank">https://docs.python.org/3/c-api/veryhigh.html?highlight=pyframeobject#c.PyEval_EvalFrameEx</a>)<br>
><br>
> .. [#pycodeobject] ``PyCodeObject``<br>
> (<a href="https://docs.python.org/3/c-api/code.html#c.PyCodeObject" rel="noreferrer" target="_blank">https://docs.python.org/3/c-api/code.html#c.PyCodeObject</a>)<br>
><br>
> .. [#numba] Numba<br>
> (<a href="http://numba.pydata.org/" rel="noreferrer" target="_blank">http://numba.pydata.org/</a>)<br>
><br>
> .. [#numba-interest] numba-users mailing list:<br>
> "Would the C API for a JIT entrypoint being proposed by<br>
> Pyjion help out Numba?"<br>
><br>
> (<a href="https://groups.google.com/a/continuum.io/forum/#!topic/numba-users/yRl_0t8-m1g" rel="noreferrer" target="_blank">https://groups.google.com/a/continuum.io/forum/#!topic/numba-users/yRl_0t8-m1g</a>)<br>
><br>
> .. [#code-object-count] [Python-Dev] Opcode cache in ceval loop<br>
><br>
> (<a href="https://mail.python.org/pipermail/python-dev/2016-February/143025.html" rel="noreferrer" target="_blank">https://mail.python.org/pipermail/python-dev/2016-February/143025.html</a>)<br>
><br>
> .. [#py-benchmarks] Python benchmark suite<br>
> (<a href="https://hg.python.org/benchmarks" rel="noreferrer" target="_blank">https://hg.python.org/benchmarks</a>)<br>
><br>
> .. [#pyston] Pyston<br>
> (<a href="http://pyston.org" rel="noreferrer" target="_blank">http://pyston.org</a>)<br>
><br>
> .. [#pypy] PyPy<br>
> (<a href="http://pypy.org/" rel="noreferrer" target="_blank">http://pypy.org/</a>)<br>
><br>
> .. [#ptvs] Python Tools for Visual Studio<br>
> (<a href="http://microsoft.github.io/PTVS/" rel="noreferrer" target="_blank">http://microsoft.github.io/PTVS/</a>)<br>
><br>
> .. [#coconut] Coconut<br>
> (<a href="https://github.com/davidmalcolm/coconut" rel="noreferrer" target="_blank">https://github.com/davidmalcolm/coconut</a>)<br>
><br>
><br>
> Copyright<br>
> =========<br>
><br>
> This document has been placed in the public domain.<br>
><br>
><br>
><br>
> ..<br>
> Local Variables:<br>
> mode: indented-text<br>
> indent-tabs-mode: nil<br>
> sentence-end-double-space: t<br>
> fill-column: 70<br>
> coding: utf-8<br>
> End:<br>
><br>
><br>
> _______________________________________________<br>
> Python-Dev mailing list<br>
> <a href="mailto:Python-Dev@python.org" target="_blank">Python-Dev@python.org</a> <mailto:<a href="mailto:Python-Dev@python.org" target="_blank">Python-Dev@python.org</a>><br>
> <a href="https://mail.python.org/mailman/listinfo/python-dev" rel="noreferrer" target="_blank">https://mail.python.org/mailman/listinfo/python-dev</a><br>
> Unsubscribe:<br>
> <a href="https://mail.python.org/mailman/options/python-dev/guido%40python.org" rel="noreferrer" target="_blank">https://mail.python.org/mailman/options/python-dev/guido%40python.org</a><br>
><br>
><br>
><br>
><br>
> --<br>
> --Guido van Rossum (<a href="http://python.org/~guido" rel="noreferrer" target="_blank">python.org/~guido</a> <<a href="http://python.org/~guido" rel="noreferrer" target="_blank">http://python.org/~guido</a>>)<br>
><br>
><br>
><br>
> _______________________________________________<br>
> Python-Dev mailing list<br>
> <a href="mailto:Python-Dev@python.org" target="_blank">Python-Dev@python.org</a><br>
> <a href="https://mail.python.org/mailman/listinfo/python-dev" rel="noreferrer" target="_blank">https://mail.python.org/mailman/listinfo/python-dev</a><br>
> Unsubscribe: <a href="https://mail.python.org/mailman/options/python-dev/mark%40hotpy.org" rel="noreferrer" target="_blank">https://mail.python.org/mailman/options/python-dev/mark%40hotpy.org</a><br>
><br>
_______________________________________________<br>
Python-Dev mailing list<br>
<a href="mailto:Python-Dev@python.org" target="_blank">Python-Dev@python.org</a><br>
<a href="https://mail.python.org/mailman/listinfo/python-dev" rel="noreferrer" target="_blank">https://mail.python.org/mailman/listinfo/python-dev</a><br>
Unsubscribe: <a href="https://mail.python.org/mailman/options/python-dev/brett%40python.org" rel="noreferrer" target="_blank">https://mail.python.org/mailman/options/python-dev/brett%40python.org</a><br>
</blockquote></div></div>