Skip to content

Commit

Permalink
Merge branch 'main' into pythongh-115999-load-attr-module
Browse files Browse the repository at this point in the history
  • Loading branch information
mpage committed Dec 9, 2024
2 parents c6ddd69 + d8d12b3 commit 550f955
Show file tree
Hide file tree
Showing 56 changed files with 1,795 additions and 1,369 deletions.
10 changes: 5 additions & 5 deletions Doc/tutorial/datastructures.rst
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,8 @@ Using Lists as Stacks

The list methods make it very easy to use a list as a stack, where the last
element added is the first element retrieved ("last-in, first-out"). To add an
item to the top of the stack, use :meth:`!~list.append`. To retrieve an item from the
top of the stack, use :meth:`!~list.pop` without an explicit index. For example::
item to the top of the stack, use :meth:`!append`. To retrieve an item from the
top of the stack, use :meth:`!pop` without an explicit index. For example::

>>> stack = [3, 4, 5]
>>> stack.append(6)
Expand Down Expand Up @@ -340,7 +340,7 @@ The :keyword:`!del` statement
=============================

There is a way to remove an item from a list given its index instead of its
value: the :keyword:`del` statement. This differs from the :meth:`!~list.pop` method
value: the :keyword:`del` statement. This differs from the :meth:`!pop` method
which returns a value. The :keyword:`!del` statement can also be used to remove
slices from a list or clear the entire list (which we did earlier by assignment
of an empty list to the slice). For example::
Expand Down Expand Up @@ -500,8 +500,8 @@ any immutable type; strings and numbers can always be keys. Tuples can be used
as keys if they contain only strings, numbers, or tuples; if a tuple contains
any mutable object either directly or indirectly, it cannot be used as a key.
You can't use lists as keys, since lists can be modified in place using index
assignments, slice assignments, or methods like :meth:`!~list.append` and
:meth:`!~list.extend`.
assignments, slice assignments, or methods like :meth:`!append` and
:meth:`!extend`.

It is best to think of a dictionary as a set of *key: value* pairs,
with the requirement that the keys are unique (within one dictionary). A pair of
Expand Down
8 changes: 8 additions & 0 deletions Include/internal/pycore_pystate.h
Original file line number Diff line number Diff line change
Expand Up @@ -190,10 +190,18 @@ static inline void
_Py_EnsureFuncTstateNotNULL(const char *func, PyThreadState *tstate)
{
if (tstate == NULL) {
#ifndef Py_GIL_DISABLED
_Py_FatalErrorFunc(func,
"the function must be called with the GIL held, "
"after Python initialization and before Python finalization, "
"but the GIL is released (the current Python thread state is NULL)");
#else
_Py_FatalErrorFunc(func,
"the function must be called with an active thread state, "
"after Python initialization and before Python finalization, "
"but it was called without an active thread state. "
"Are you trying to call the C API inside of a Py_BEGIN_ALLOW_THREADS block?");
#endif
}
}

Expand Down
4 changes: 2 additions & 2 deletions InternalDocs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ Runtime Objects
Program Execution
---

- [The Interpreter](interpreter.md)
- [The Bytecode Interpreter](interpreter.md)

- [Adaptive Instruction Families](adaptive.md)
- [The JIT](jit.md)

- [Garbage Collector Design](garbage_collector.md)

Expand Down
146 changes: 0 additions & 146 deletions InternalDocs/adaptive.md

This file was deleted.

5 changes: 5 additions & 0 deletions InternalDocs/code_objects.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ Code objects are typically produced by the bytecode [compiler](compiler.md),
although they are often written to disk by one process and read back in by another.
The disk version of a code object is serialized using the
[marshal](https://docs.python.org/dev/library/marshal.html) protocol.
When a `CodeObject` is created, the function `_PyCode_Quicken()` from
[`Python/specialize.c`](../Python/specialize.c) is called to initialize
the caches of all adaptive instructions. This is required because the
on-disk format is a sequence of bytes, and some of the caches need to be
initialized with 16-bit values.

Code objects are nominally immutable.
Some fields (including `co_code_adaptive` and fields for runtime
Expand Down
10 changes: 0 additions & 10 deletions InternalDocs/compiler.md
Original file line number Diff line number Diff line change
Expand Up @@ -595,16 +595,6 @@ Objects
* [Exception Handling](exception_handling.md): Describes the exception table


Specializing Adaptive Interpreter
=================================

Adding a specializing, adaptive interpreter to CPython will bring significant
performance improvements. These documents provide more information:

* [PEP 659: Specializing Adaptive Interpreter](https://peps.python.org/pep-0659/).
* [Adding or extending a family of adaptive instructions](adaptive.md)


References
==========

Expand Down
50 changes: 44 additions & 6 deletions InternalDocs/garbage_collector.md
Original file line number Diff line number Diff line change
Expand Up @@ -199,22 +199,22 @@ unreachable:

```pycon
>>> import gc
>>>
>>>
>>> class Link:
... def __init__(self, next_link=None):
... self.next_link = next_link
...
...
>>> link_3 = Link()
>>> link_2 = Link(link_3)
>>> link_1 = Link(link_2)
>>> link_3.next_link = link_1
>>> A = link_1
>>> del link_1, link_2, link_3
>>>
>>>
>>> link_4 = Link()
>>> link_4.next_link = link_4
>>> del link_4
>>>
>>>
>>> # Collect the unreachable Link object (and its .__dict__ dict).
>>> gc.collect()
2
Expand Down Expand Up @@ -459,11 +459,11 @@ specifically in a generation by calling `gc.collect(generation=NUM)`.
>>> # Create a reference cycle.
>>> x = MyObj()
>>> x.self = x
>>>
>>>
>>> # Initially the object is in the young generation.
>>> gc.get_objects(generation=0)
[..., <__main__.MyObj object at 0x7fbcc12a3400>, ...]
>>>
>>>
>>> # After a collection of the youngest generation the object
>>> # moves to the old generation.
>>> gc.collect(generation=0)
Expand Down Expand Up @@ -515,6 +515,44 @@ increment. All objects directly referred to from those stack frames are
added to the working set.
Then the above algorithm is repeated, starting from step 2.

Determining how much work to do
-------------------------------

We need to do a certain amount of work to ensure that garbage is collected,
but doing too much work slows down execution.

To work out how much work we need to do, consider a heap with `L` live objects
and `G0` garbage objects at the start of a full scavenge and `G1` garbage objects
at the end of the scavenge. We don't want the amount of garbage to grow, `G1 ≤ G0`, and
we don't want too much garbage (say 1/3 of the heap maximum), `G0 ≤ L/2`.
For each full scavenge we must visit all objects, `T == L + G0 + G1`, during which
`G1` garbage objects are created.

The number of new objects created `N` must be at least the new garbage created, `N ≥ G1`,
assuming that the number of live objects remains roughly constant.
If we set `T == 4*N` we get `T > 4*G1` and `T = L + G0 + G1` => `L + G0 > 3G1`
For a steady state heap (`G0 == G1`) we get `L > 2G0` and the desired garbage ratio.

In other words, to keep the garbage fraction to 1/3 or less we need to visit
4 times as many objects as are newly created.

We can do better than this though. Not all new objects will be garbage.
Consider the heap at the end of the scavenge with `L1` live objects and `G1`
garbage. Also, note that `T == M + I` where `M` is the number of objects marked
as reachable and `I` is the number of objects visited in increments.
Everything in `M` is live, so `I ≥ G0` and in practice `I` is closer to `G0 + G1`.

If we choose the amount of work done such that `2*M + I == 6N` then we can do
less work in most cases, but are still guaranteed to keep up.
Since `I ≳ G0 + G1` (not strictly true, but close enough)
`T == M + I == (6N + I)/2` and `(6N + I)/2 ≳ 4G`, so we can keep up.

The reason that this improves performance is that `M` is usually much larger
than `I`. If `M == 10I`, then `T ≅ 3N`.

Finally, instead of using a fixed multiple of 8, we gradually increase it as the
heap grows. This avoids wasting work for small heaps and during startup.


Optimization: reusing fields to save memory
===========================================
Expand Down
Loading

0 comments on commit 550f955

Please sign in to comment.