GeezerVison is showing github.com — gh-132657: Add maybe_enable_deferred_ref_count() (gh-142843) · thunder-coding/cpython@5a55f82
File treeExpand file treeCollapse file tree Open diff view settings Misc/NEWS.d/next/Core_and_Builtins
Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: Include/internal/pycore_dict.h +1Lines changed: 1 addition & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -114,6 +114,7 @@ extern Py_ssize_t _Py_dict_lookup_threadsafe_stackref(PyDictObject *mp, PyObject | 114 | 114 |
| 115 | 115 | extern int _PyDict_GetMethodStackRef(PyDictObject *dict, PyObject *name, _PyStackRef *method); | 116 | 116 |
| | 117 | +extern Py_ssize_t _PyDict_LookupIndexAndValue(PyDictObject *, PyObject *, PyObject **); | 117 | 118 | extern Py_ssize_t _PyDict_LookupIndex(PyDictObject *, PyObject *); | 118 | 119 | extern Py_ssize_t _PyDictKeys_StringLookup(PyDictKeysObject* dictkeys, PyObject *key); | 119 | 120 |
| |
Collapse file Copy file name to clipboard +6Lines changed: 6 additions & 0 deletions - Display the source diff
- Display the rich diff
| Original file line number | Diff line number | Diff line change |
|---|
| | 1 | +If we are specializing to ``LOAD_GLOBAL_MODULE`` or ``LOAD_ATTR_MODULE``, try | | 2 | +to enable deferred reference counting for the value, if the object is owned by | | 3 | +a different thread. This applies to the free-threaded build only and should | | 4 | +improve scaling of multi-threaded programs. Note that when deferred reference | | 5 | +counting is enabled, the object will be deallocated by the GC, rather than by | | 6 | +:c:func:`Py_DECREF`. |
Collapse file Copy file name to clipboardExpand all lines: Objects/dictobject.c +9-3Lines changed: 9 additions & 3 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -2349,10 +2349,9 @@ dict_unhashable_type(PyObject *key) | 2349 | 2349 | } | 2350 | 2350 |
| 2351 | 2351 | Py_ssize_t | 2352 | | -_PyDict_LookupIndex(PyDictObject *mp, PyObject *key) | | 2352 | +_PyDict_LookupIndexAndValue(PyDictObject *mp, PyObject *key, PyObject **value) | 2353 | 2353 | { | 2354 | 2354 | // TODO: Thread safety | 2355 | | - PyObject *value; | 2356 | 2355 | assert(PyDict_CheckExact((PyObject*)mp)); | 2357 | 2356 | assert(PyUnicode_CheckExact(key)); | 2358 | 2357 |
| @@ -2362,7 +2361,14 @@ _PyDict_LookupIndex(PyDictObject *mp, PyObject *key) | 2362 | 2361 | return -1; | 2363 | 2362 | } | 2364 | 2363 |
| 2365 | | - return _Py_dict_lookup(mp, key, hash, &value); | | 2364 | + return _Py_dict_lookup(mp, key, hash, value); | | 2365 | +} | | 2366 | + | | 2367 | +Py_ssize_t | | 2368 | +_PyDict_LookupIndex(PyDictObject *mp, PyObject *key) | | 2369 | +{ | | 2370 | + PyObject *value; // discarded | | 2371 | + return _PyDict_LookupIndexAndValue(mp, key, &value); | 2366 | 2372 | } | 2367 | 2373 |
| 2368 | 2374 | /* Same as PyDict_GetItemWithError() but with hash supplied by caller. | |
Collapse file Copy file name to clipboardExpand all lines: Python/specialize.c +28-2Lines changed: 28 additions & 2 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -358,6 +358,21 @@ static int function_kind(PyCodeObject *code); | 358 | 358 | static bool function_check_args(PyObject *o, int expected_argcount, int opcode); | 359 | 359 | static uint32_t function_get_version(PyObject *o, int opcode); | 360 | 360 |
| | 361 | +#ifdef Py_GIL_DISABLED | | 362 | +static void | | 363 | +maybe_enable_deferred_ref_count(PyObject *op) | | 364 | +{ | | 365 | + if (!_Py_IsOwnedByCurrentThread(op)) { | | 366 | + // For module level variables that are heavily used from multiple | | 367 | + // threads, deferred reference counting provides good scaling | | 368 | + // benefits. The downside is that the object will only be deallocated | | 369 | + // by a GC run. | | 370 | + PyUnstable_Object_EnableDeferredRefcount(op); | | 371 | + } | | 372 | +} | | 373 | +#endif | | 374 | + | | 375 | + | 361 | 376 | static int | 362 | 377 | specialize_module_load_attr_lock_held(PyDictObject *dict, _Py_CODEUNIT *instr, PyObject *name) | 363 | 378 | { | @@ -366,7 +381,8 @@ specialize_module_load_attr_lock_held(PyDictObject *dict, _Py_CODEUNIT *instr, P | 366 | 381 | SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_NON_STRING); | 367 | 382 | return -1; | 368 | 383 | } | 369 | | - Py_ssize_t index = _PyDict_LookupIndex(dict, name); | | 384 | + PyObject *value; | | 385 | + Py_ssize_t index = _PyDict_LookupIndexAndValue(dict, name, &value); | 370 | 386 | assert(index != DKIX_ERROR); | 371 | 387 | if (index != (uint16_t)index) { | 372 | 388 | SPECIALIZATION_FAIL(LOAD_ATTR, | @@ -381,6 +397,9 @@ specialize_module_load_attr_lock_held(PyDictObject *dict, _Py_CODEUNIT *instr, P | 381 | 397 | SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_OUT_OF_VERSIONS); | 382 | 398 | return -1; | 383 | 399 | } | | 400 | +#ifdef Py_GIL_DISABLED | | 401 | + maybe_enable_deferred_ref_count(value); | | 402 | +#endif | 384 | 403 | write_u32(cache->version, keys_version); | 385 | 404 | cache->index = (uint16_t)index; | 386 | 405 | specialize(instr, LOAD_ATTR_MODULE); | @@ -1269,7 +1288,6 @@ specialize_attr_loadclassattr(PyObject *owner, _Py_CODEUNIT *instr, | 1269 | 1288 | return 1; | 1270 | 1289 | } | 1271 | 1290 |
| 1272 | | - | 1273 | 1291 | static void | 1274 | 1292 | specialize_load_global_lock_held( | 1275 | 1293 | PyObject *globals, PyObject *builtins, | @@ -1289,7 +1307,12 @@ specialize_load_global_lock_held( | 1289 | 1307 | SPECIALIZATION_FAIL(LOAD_GLOBAL, SPEC_FAIL_LOAD_GLOBAL_NON_STRING_OR_SPLIT); | 1290 | 1308 | goto fail; | 1291 | 1309 | } | | 1310 | +#ifdef Py_GIL_DISABLED | | 1311 | + PyObject *value; | | 1312 | + Py_ssize_t index = _PyDict_LookupIndexAndValue((PyDictObject *)globals, name, &value); | | 1313 | +#else | 1292 | 1314 | Py_ssize_t index = _PyDictKeys_StringLookup(globals_keys, name); | | 1315 | +#endif | 1293 | 1316 | if (index == DKIX_ERROR) { | 1294 | 1317 | SPECIALIZATION_FAIL(LOAD_GLOBAL, SPEC_FAIL_EXPECTED_ERROR); | 1295 | 1318 | goto fail; | @@ -1310,6 +1333,9 @@ specialize_load_global_lock_held( | 1310 | 1333 | SPECIALIZATION_FAIL(LOAD_GLOBAL, SPEC_FAIL_OUT_OF_RANGE); | 1311 | 1334 | goto fail; | 1312 | 1335 | } | | 1336 | +#ifdef Py_GIL_DISABLED | | 1337 | + maybe_enable_deferred_ref_count(value); | | 1338 | +#endif | 1313 | 1339 | cache->index = (uint16_t)index; | 1314 | 1340 | cache->module_keys_version = (uint16_t)keys_version; | 1315 | 1341 | specialize(instr, LOAD_GLOBAL_MODULE); | |
Collapse file Copy file name to clipboardExpand all lines: Tools/ftscalingbench/ftscalingbench.py +9Lines changed: 9 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 21 | 21 | # > echo "0" | sudo tee /sys/devices/system/cpu/cpufreq/boost | 22 | 22 | # | 23 | 23 |
| | 24 | +import copy | 24 | 25 | import math | 25 | 26 | import os | 26 | 27 | import queue | @@ -214,6 +215,14 @@ def instantiate_dataclass(): | 214 | 215 | for _ in range(1000 * WORK_SCALE): | 215 | 216 | obj = MyDataClass(x=1, y=2, z=3) | 216 | 217 |
| | 218 | + | | 219 | +@register_benchmark | | 220 | +def deepcopy(): | | 221 | + x = {'list': [1, 2], 'tuple': (1, None)} | | 222 | + for i in range(40 * WORK_SCALE): | | 223 | + copy.deepcopy(x) | | 224 | + | | 225 | + | 217 | 226 | def bench_one_thread(func): | 218 | 227 | t0 = time.perf_counter_ns() | 219 | 228 | func() | |
|