mirror of
https://github.com/micropython/micropython.git
synced 2025-07-21 13:01:10 +02:00
py/vm: Avoid heap-allocating slices when subscripting built-ins.
Some checks failed
JavaScript code lint and formatting with Biome / eslint (push) Has been cancelled
Check code formatting / code-formatting (push) Has been cancelled
Check spelling with codespell / codespell (push) Has been cancelled
Build docs / build (push) Has been cancelled
Check examples / embedding (push) Has been cancelled
Package mpremote / build (push) Has been cancelled
.mpy file format and tools / test (push) Has been cancelled
Build ports metadata / build (push) Has been cancelled
alif port / build_alif (alif_ae3_build) (push) Has been cancelled
cc3200 port / build (push) Has been cancelled
esp32 port / build_idf (esp32_build_cmod_spiram_s2) (push) Has been cancelled
esp32 port / build_idf (esp32_build_s3_c3) (push) Has been cancelled
esp8266 port / build (push) Has been cancelled
mimxrt port / build (push) Has been cancelled
nrf port / build (push) Has been cancelled
powerpc port / build (push) Has been cancelled
qemu port / build_and_test_arm (bigendian) (push) Has been cancelled
qemu port / build_and_test_arm (sabrelite) (push) Has been cancelled
qemu port / build_and_test_arm (thumb) (push) Has been cancelled
qemu port / build_and_test_rv32 (push) Has been cancelled
renesas-ra port / build_renesas_ra_board (push) Has been cancelled
rp2 port / build (push) Has been cancelled
samd port / build (push) Has been cancelled
stm32 port / build_stm32 (stm32_misc_build) (push) Has been cancelled
stm32 port / build_stm32 (stm32_nucleo_build) (push) Has been cancelled
stm32 port / build_stm32 (stm32_pyb_build) (push) Has been cancelled
unix port / minimal (push) Has been cancelled
unix port / reproducible (push) Has been cancelled
unix port / standard (push) Has been cancelled
unix port / standard_v2 (push) Has been cancelled
unix port / coverage (push) Has been cancelled
unix port / coverage_32bit (push) Has been cancelled
unix port / nanbox (push) Has been cancelled
unix port / float (push) Has been cancelled
unix port / stackless_clang (push) Has been cancelled
unix port / float_clang (push) Has been cancelled
unix port / settrace_stackless (push) Has been cancelled
unix port / macos (push) Has been cancelled
unix port / qemu_mips (push) Has been cancelled
unix port / qemu_arm (push) Has been cancelled
unix port / qemu_riscv64 (push) Has been cancelled
unix port / sanitize_address (push) Has been cancelled
unix port / sanitize_undefined (push) Has been cancelled
webassembly port / build (push) Has been cancelled
windows port / build-vs (Debug, x64, dev, 2017, [15, 16)) (push) Has been cancelled
windows port / build-vs (Debug, x64, dev, 2022, [17, 18)) (push) Has been cancelled
windows port / build-vs (Debug, x86, dev, 2017, [15, 16)) (push) Has been cancelled
windows port / build-vs (Debug, x86, dev, 2022, [17, 18)) (push) Has been cancelled
windows port / build-vs (Release, x64, dev, 2017, [15, 16)) (push) Has been cancelled
windows port / build-vs (Release, x64, dev, 2019, [16, 17)) (push) Has been cancelled
windows port / build-vs (Release, x64, dev, 2022, [17, 18)) (push) Has been cancelled
windows port / build-vs (Release, x64, standard, 2017, [15, 16)) (push) Has been cancelled
windows port / build-vs (Release, x64, standard, 2019, [16, 17)) (push) Has been cancelled
windows port / build-vs (Release, x64, standard, 2022, [17, 18)) (push) Has been cancelled
windows port / build-vs (Release, x86, dev, 2017, [15, 16)) (push) Has been cancelled
windows port / build-vs (Release, x86, dev, 2019, [16, 17)) (push) Has been cancelled
windows port / build-vs (Release, x86, dev, 2022, [17, 18)) (push) Has been cancelled
windows port / build-vs (Release, x86, standard, 2017, [15, 16)) (push) Has been cancelled
windows port / build-vs (Release, x86, standard, 2019, [16, 17)) (push) Has been cancelled
windows port / build-vs (Release, x86, standard, 2022, [17, 18)) (push) Has been cancelled
windows port / build-mingw (i686, mingw32, dev) (push) Has been cancelled
windows port / build-mingw (i686, mingw32, standard) (push) Has been cancelled
windows port / build-mingw (x86_64, mingw64, dev) (push) Has been cancelled
windows port / build-mingw (x86_64, mingw64, standard) (push) Has been cancelled
windows port / cross-build-on-linux (push) Has been cancelled
zephyr port / build (push) Has been cancelled
Python code lint and formatting with ruff / ruff (push) Has been cancelled
Some checks failed
JavaScript code lint and formatting with Biome / eslint (push) Has been cancelled
Check code formatting / code-formatting (push) Has been cancelled
Check spelling with codespell / codespell (push) Has been cancelled
Build docs / build (push) Has been cancelled
Check examples / embedding (push) Has been cancelled
Package mpremote / build (push) Has been cancelled
.mpy file format and tools / test (push) Has been cancelled
Build ports metadata / build (push) Has been cancelled
alif port / build_alif (alif_ae3_build) (push) Has been cancelled
cc3200 port / build (push) Has been cancelled
esp32 port / build_idf (esp32_build_cmod_spiram_s2) (push) Has been cancelled
esp32 port / build_idf (esp32_build_s3_c3) (push) Has been cancelled
esp8266 port / build (push) Has been cancelled
mimxrt port / build (push) Has been cancelled
nrf port / build (push) Has been cancelled
powerpc port / build (push) Has been cancelled
qemu port / build_and_test_arm (bigendian) (push) Has been cancelled
qemu port / build_and_test_arm (sabrelite) (push) Has been cancelled
qemu port / build_and_test_arm (thumb) (push) Has been cancelled
qemu port / build_and_test_rv32 (push) Has been cancelled
renesas-ra port / build_renesas_ra_board (push) Has been cancelled
rp2 port / build (push) Has been cancelled
samd port / build (push) Has been cancelled
stm32 port / build_stm32 (stm32_misc_build) (push) Has been cancelled
stm32 port / build_stm32 (stm32_nucleo_build) (push) Has been cancelled
stm32 port / build_stm32 (stm32_pyb_build) (push) Has been cancelled
unix port / minimal (push) Has been cancelled
unix port / reproducible (push) Has been cancelled
unix port / standard (push) Has been cancelled
unix port / standard_v2 (push) Has been cancelled
unix port / coverage (push) Has been cancelled
unix port / coverage_32bit (push) Has been cancelled
unix port / nanbox (push) Has been cancelled
unix port / float (push) Has been cancelled
unix port / stackless_clang (push) Has been cancelled
unix port / float_clang (push) Has been cancelled
unix port / settrace_stackless (push) Has been cancelled
unix port / macos (push) Has been cancelled
unix port / qemu_mips (push) Has been cancelled
unix port / qemu_arm (push) Has been cancelled
unix port / qemu_riscv64 (push) Has been cancelled
unix port / sanitize_address (push) Has been cancelled
unix port / sanitize_undefined (push) Has been cancelled
webassembly port / build (push) Has been cancelled
windows port / build-vs (Debug, x64, dev, 2017, [15, 16)) (push) Has been cancelled
windows port / build-vs (Debug, x64, dev, 2022, [17, 18)) (push) Has been cancelled
windows port / build-vs (Debug, x86, dev, 2017, [15, 16)) (push) Has been cancelled
windows port / build-vs (Debug, x86, dev, 2022, [17, 18)) (push) Has been cancelled
windows port / build-vs (Release, x64, dev, 2017, [15, 16)) (push) Has been cancelled
windows port / build-vs (Release, x64, dev, 2019, [16, 17)) (push) Has been cancelled
windows port / build-vs (Release, x64, dev, 2022, [17, 18)) (push) Has been cancelled
windows port / build-vs (Release, x64, standard, 2017, [15, 16)) (push) Has been cancelled
windows port / build-vs (Release, x64, standard, 2019, [16, 17)) (push) Has been cancelled
windows port / build-vs (Release, x64, standard, 2022, [17, 18)) (push) Has been cancelled
windows port / build-vs (Release, x86, dev, 2017, [15, 16)) (push) Has been cancelled
windows port / build-vs (Release, x86, dev, 2019, [16, 17)) (push) Has been cancelled
windows port / build-vs (Release, x86, dev, 2022, [17, 18)) (push) Has been cancelled
windows port / build-vs (Release, x86, standard, 2017, [15, 16)) (push) Has been cancelled
windows port / build-vs (Release, x86, standard, 2019, [16, 17)) (push) Has been cancelled
windows port / build-vs (Release, x86, standard, 2022, [17, 18)) (push) Has been cancelled
windows port / build-mingw (i686, mingw32, dev) (push) Has been cancelled
windows port / build-mingw (i686, mingw32, standard) (push) Has been cancelled
windows port / build-mingw (x86_64, mingw64, dev) (push) Has been cancelled
windows port / build-mingw (x86_64, mingw64, standard) (push) Has been cancelled
windows port / cross-build-on-linux (push) Has been cancelled
zephyr port / build (push) Has been cancelled
Python code lint and formatting with ruff / ruff (push) Has been cancelled
This commit adds a fast-path optimisation for when a BUILD_SLICE is immediately followed by a LOAD/STORE_SUBSCR for a native type, to avoid needing to allocate the slice on the heap. In some cases (e.g. `a[1:3] = x`) this can result in no allocations at all. We can't do this for instance types because the get/set/delattr implementation may keep a reference to the slice. Adds more tests to the basic slice tests to ensure that a stack-allocated slice never makes it to Python, and also a heapalloc test that verifies (when using bytecode) that assigning to a slice is no-alloc. This work was funded through GitHub Sponsors. Signed-off-by: Jim Mussared <jim.mussared@gmail.com> Signed-off-by: Damien George <damien@micropython.org>
This commit is contained in:
committed by
Damien George
parent
aa2362d4de
commit
5e9189d6d1
32
py/vm.c
32
py/vm.c
@@ -195,6 +195,22 @@
|
|||||||
#define TRACE_TICK(current_ip, current_sp, is_exception)
|
#define TRACE_TICK(current_ip, current_sp, is_exception)
|
||||||
#endif // MICROPY_PY_SYS_SETTRACE
|
#endif // MICROPY_PY_SYS_SETTRACE
|
||||||
|
|
||||||
|
#if MICROPY_PY_BUILTINS_SLICE
|
||||||
|
// This function is marked "no inline" so it doesn't increase the C stack usage of the main VM function.
|
||||||
|
MP_NOINLINE static mp_obj_t *build_slice_stack_allocated(byte op, mp_obj_t *sp, mp_obj_t step) {
|
||||||
|
mp_obj_t stop = sp[2];
|
||||||
|
mp_obj_t start = sp[1];
|
||||||
|
mp_obj_slice_t slice = { .base = { .type = &mp_type_slice }, .start = start, .stop = stop, .step = step };
|
||||||
|
if (op == MP_BC_LOAD_SUBSCR) {
|
||||||
|
SET_TOP(mp_obj_subscr(TOP(), MP_OBJ_FROM_PTR(&slice), MP_OBJ_SENTINEL));
|
||||||
|
} else { // MP_BC_STORE_SUBSCR
|
||||||
|
mp_obj_subscr(TOP(), MP_OBJ_FROM_PTR(&slice), sp[-1]);
|
||||||
|
sp -= 2;
|
||||||
|
}
|
||||||
|
return sp;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// fastn has items in reverse order (fastn[0] is local[0], fastn[-1] is local[1], etc)
|
// fastn has items in reverse order (fastn[0] is local[0], fastn[-1] is local[1], etc)
|
||||||
// sp points to bottom of stack which grows up
|
// sp points to bottom of stack which grows up
|
||||||
// returns:
|
// returns:
|
||||||
@@ -849,9 +865,19 @@ unwind_jump:;
|
|||||||
// 3-argument slice includes step
|
// 3-argument slice includes step
|
||||||
step = POP();
|
step = POP();
|
||||||
}
|
}
|
||||||
mp_obj_t stop = POP();
|
if ((*ip == MP_BC_LOAD_SUBSCR || *ip == MP_BC_STORE_SUBSCR) && mp_obj_is_native_type(mp_obj_get_type(sp[-2]))) {
|
||||||
mp_obj_t start = TOP();
|
// Fast path optimisation for when the BUILD_SLICE is immediately followed
|
||||||
SET_TOP(mp_obj_new_slice(start, stop, step));
|
// by a LOAD/STORE_SUBSCR for a native type to avoid needing to allocate
|
||||||
|
// the slice on the heap. In some cases (e.g. a[1:3] = x) this can result
|
||||||
|
// in no allocations at all. We can't do this for instance types because
|
||||||
|
// the get/set/delattr implementation may keep a reference to the slice.
|
||||||
|
byte op = *ip++;
|
||||||
|
sp = build_slice_stack_allocated(op, sp - 2, step);
|
||||||
|
} else {
|
||||||
|
mp_obj_t stop = POP();
|
||||||
|
mp_obj_t start = TOP();
|
||||||
|
SET_TOP(mp_obj_new_slice(start, stop, step));
|
||||||
|
}
|
||||||
DISPATCH();
|
DISPATCH();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@@ -1,11 +1,44 @@
|
|||||||
# test builtin slice
|
# test builtin slice
|
||||||
|
|
||||||
|
# ensures that slices passed to user types are heap-allocated and can be
|
||||||
|
# safely stored as well as not overriden by subsequent slices.
|
||||||
|
|
||||||
# print slice
|
# print slice
|
||||||
class A:
|
class A:
|
||||||
def __getitem__(self, idx):
|
def __getitem__(self, idx):
|
||||||
print(idx)
|
print("get", idx)
|
||||||
|
print("abc"[1:])
|
||||||
|
print("get", idx)
|
||||||
return idx
|
return idx
|
||||||
s = A()[1:2:3]
|
|
||||||
|
def __setitem__(self, idx, value):
|
||||||
|
print("set", idx)
|
||||||
|
print("abc"[1:])
|
||||||
|
print("set", idx)
|
||||||
|
self.saved_idx = idx
|
||||||
|
return idx
|
||||||
|
|
||||||
|
def __delitem__(self, idx):
|
||||||
|
print("del", idx)
|
||||||
|
print("abc"[1:])
|
||||||
|
print("del", idx)
|
||||||
|
return idx
|
||||||
|
|
||||||
|
|
||||||
|
a = A()
|
||||||
|
s = a[1:2:3]
|
||||||
|
a[4:5:6] = s
|
||||||
|
del a[7:8:9]
|
||||||
|
|
||||||
|
print(a.saved_idx)
|
||||||
|
|
||||||
|
# nested slicing
|
||||||
|
print(A()[1 : A()[A()[2:3:4] : 5]])
|
||||||
|
|
||||||
|
# tuple slicing
|
||||||
|
a[1:2, 4:5, 7:8]
|
||||||
|
a[1, 4:5, 7:8, 2]
|
||||||
|
a[1:2, a[3:4], 5:6]
|
||||||
|
|
||||||
# check type
|
# check type
|
||||||
print(type(s) is slice)
|
print(type(s) is slice)
|
||||||
|
18
tests/micropython/heapalloc_slice.py
Normal file
18
tests/micropython/heapalloc_slice.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# slice operations that don't require allocation
|
||||||
|
try:
|
||||||
|
from micropython import heap_lock, heap_unlock
|
||||||
|
except (ImportError, AttributeError):
|
||||||
|
heap_lock = heap_unlock = lambda: 0
|
||||||
|
|
||||||
|
b = bytearray(range(10))
|
||||||
|
|
||||||
|
m = memoryview(b)
|
||||||
|
|
||||||
|
heap_lock()
|
||||||
|
|
||||||
|
b[3:5] = b"aa"
|
||||||
|
m[5:7] = b"bb"
|
||||||
|
|
||||||
|
heap_unlock()
|
||||||
|
|
||||||
|
print(b)
|
@@ -854,6 +854,9 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1):
|
|||||||
skip_tests.add(
|
skip_tests.add(
|
||||||
"micropython/emg_exc.py"
|
"micropython/emg_exc.py"
|
||||||
) # because native doesn't have proper traceback info
|
) # because native doesn't have proper traceback info
|
||||||
|
skip_tests.add(
|
||||||
|
"micropython/heapalloc_slice.py"
|
||||||
|
) # because native doesn't do the stack-allocated slice optimisation
|
||||||
skip_tests.add(
|
skip_tests.add(
|
||||||
"micropython/heapalloc_traceback.py"
|
"micropython/heapalloc_traceback.py"
|
||||||
) # because native doesn't have proper traceback info
|
) # because native doesn't have proper traceback info
|
||||||
|
Reference in New Issue
Block a user