From b867b609bebd07f2afbdf97826d400e593468b44 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Fri, 24 Oct 2025 16:26:56 -0400 Subject: [PATCH 01/21] [mypyc] feat: reorder gens for speed in ForZip Not all generators have an equally fast gen_condition, some are quite fast while others quite the opposite This PR orders them in what I see to be a reasonably fastest -> slowest order --- mypyc/irbuild/for_helpers.py | 38 +++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py index 715f5432cd13..404f6afbe905 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -1185,7 +1185,43 @@ def init(self, indexes: list[Lvalue], exprs: list[Expression]) -> None: self.gens.append(gen) def gen_condition(self) -> None: - for i, gen in enumerate(self.gens): + # We don't necessarily need to check the gens in order, + # we just need to know which gen ends first. Some gens + # are quicker to check than others, so we will check the + # specialized ForHelpers before we check any generic + # ForIterable + gens = self.gens + + def check_type(obj: Any, typ: Type[Any]) -> bool: + # ForEnumerate gen_condition is as fast as it's underlying generator's + return isinstance(obj, typ) or isinstance(obj, ForEnumerate) and isinstance(obj.gen, typ) + + # these are slowest, they invoke Python's iteration protocol + for_iterable = [g for g in gens if check_type(g, ForSequence)] + + # These aren't the slowest but they're slow, we need to pack an RTuple and then get and item and do a comparison + for_dict = [g for g in gens if check_type(g, ForDictionaryCommon)] + + # These are faster than ForIterable but not as fast as others (faster than ForDict?) + for_native = [g for g in gens if check_type(g, ForNativeGenerator)] + + # forward involves in the best case one pyssize_t comparison, else one length check + the comparison + # reverse is slightly slower than forward, with one extra check + for_sequence_reverse = [g for g in gens if check_type(g, ForSequence) and (g.gen if isinstance(g, ForEnumerate) else g).reverse] + for_sequence_forward = [g for g in gens if check_type(g, ForSequence) and not (g.gen if isinstance(g, ForEnumerate) else g).reverse] + + # these are really fast, just a C int equality check + for_range = [g for g in gens if isinstance(g, ForRange)] + + ordered = for_range + for_sequence_forward + for_sequence_reverse + for_native + for_dict + + # this is a failsafe for ForHelper classes which might have been added after this commit but not added to this function's code + others = [g for g in gens if g not in ordered + for_iterable] + + ordered += others + ordered += for_iterable + + for i, gen in enumerate(ordered): gen.gen_condition() if i < len(self.gens) - 1: self.builder.activate_block(self.cond_blocks[i]) From f59a95d47ac537deef648eacc25f303eb87324b3 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Fri, 24 Oct 2025 16:28:19 -0400 Subject: [PATCH 02/21] Update for_helpers.py --- mypyc/irbuild/for_helpers.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py index 404f6afbe905..b6cea18fee37 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -1216,12 +1216,9 @@ def check_type(obj: Any, typ: Type[Any]) -> bool: ordered = for_range + for_sequence_forward + for_sequence_reverse + for_native + for_dict # this is a failsafe for ForHelper classes which might have been added after this commit but not added to this function's code - others = [g for g in gens if g not in ordered + for_iterable] - - ordered += others - ordered += for_iterable + leftovers = [g for g in gens if g not in ordered + for_iterable] - for i, gen in enumerate(ordered): + for i, gen in enumerate(ordered + leftovers + for_iterable): gen.gen_condition() if i < len(self.gens) - 1: self.builder.activate_block(self.cond_blocks[i]) From 8ccf2c3ae29f7ae7c6f40709a93ac8d3d128673b Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 24 Oct 2025 20:29:53 +0000 Subject: [PATCH 03/21] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mypyc/irbuild/for_helpers.py | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py index b6cea18fee37..5b5e1e1ae40a 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -1194,30 +1194,41 @@ def gen_condition(self) -> None: def check_type(obj: Any, typ: Type[Any]) -> bool: # ForEnumerate gen_condition is as fast as it's underlying generator's - return isinstance(obj, typ) or isinstance(obj, ForEnumerate) and isinstance(obj.gen, typ) + return ( + isinstance(obj, typ) or isinstance(obj, ForEnumerate) and isinstance(obj.gen, typ) + ) # these are slowest, they invoke Python's iteration protocol for_iterable = [g for g in gens if check_type(g, ForSequence)] # These aren't the slowest but they're slow, we need to pack an RTuple and then get and item and do a comparison for_dict = [g for g in gens if check_type(g, ForDictionaryCommon)] - + # These are faster than ForIterable but not as fast as others (faster than ForDict?) for_native = [g for g in gens if check_type(g, ForNativeGenerator)] - + # forward involves in the best case one pyssize_t comparison, else one length check + the comparison # reverse is slightly slower than forward, with one extra check - for_sequence_reverse = [g for g in gens if check_type(g, ForSequence) and (g.gen if isinstance(g, ForEnumerate) else g).reverse] - for_sequence_forward = [g for g in gens if check_type(g, ForSequence) and not (g.gen if isinstance(g, ForEnumerate) else g).reverse] + for_sequence_reverse = [ + g + for g in gens + if check_type(g, ForSequence) and (g.gen if isinstance(g, ForEnumerate) else g).reverse + ] + for_sequence_forward = [ + g + for g in gens + if check_type(g, ForSequence) + and not (g.gen if isinstance(g, ForEnumerate) else g).reverse + ] # these are really fast, just a C int equality check for_range = [g for g in gens if isinstance(g, ForRange)] ordered = for_range + for_sequence_forward + for_sequence_reverse + for_native + for_dict - + # this is a failsafe for ForHelper classes which might have been added after this commit but not added to this function's code leftovers = [g for g in gens if g not in ordered + for_iterable] - + for i, gen in enumerate(ordered + leftovers + for_iterable): gen.gen_condition() if i < len(self.gens) - 1: From 4f6e09d047953bc42ba7f86a7d71a3c905cc0a11 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Fri, 24 Oct 2025 16:35:09 -0400 Subject: [PATCH 04/21] Update for_helpers.py --- mypyc/irbuild/for_helpers.py | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py index 5b5e1e1ae40a..5e84f92e0631 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -1209,22 +1209,33 @@ def check_type(obj: Any, typ: Type[Any]) -> bool: # forward involves in the best case one pyssize_t comparison, else one length check + the comparison # reverse is slightly slower than forward, with one extra check - for_sequence_reverse = [ + for_sequence_reverse_with_len_check = [ g for g in gens - if check_type(g, ForSequence) and (g.gen if isinstance(g, ForEnumerate) else g).reverse + if check_type(g, ForSequence) and (g := (g.gen if isinstance(g, ForEnumerate) else g)).reverse and g.len_reg is not None ] - for_sequence_forward = [ + for_sequence_reverse_no_len_check = [ + g + for g in gens + if check_type(g, ForSequence) and (g := (g.gen if isinstance(g, ForEnumerate) else g)).reverse and g.len_reg is None + ] + for_sequence_forward_with_len_check = [ + g + for g in gens + if check_type(g, ForSequence) + and not (g := (g.gen if isinstance(g, ForEnumerate) else g)).reverse and g.len_reg is not None + ] + for_sequence_forward_no_len_check = [ g for g in gens if check_type(g, ForSequence) - and not (g.gen if isinstance(g, ForEnumerate) else g).reverse + and not (g := (g.gen if isinstance(g, ForEnumerate) else g)).reverse and g.len_reg is None ] # these are really fast, just a C int equality check for_range = [g for g in gens if isinstance(g, ForRange)] - ordered = for_range + for_sequence_forward + for_sequence_reverse + for_native + for_dict + ordered = for_range + for_sequence_forward_no_len_check + for_sequence_forward_no_len_check + for_sequence_forward_with_len_check + for_sequence_reverse_with_len_check + for_native + for_dict # this is a failsafe for ForHelper classes which might have been added after this commit but not added to this function's code leftovers = [g for g in gens if g not in ordered + for_iterable] From 77b79539703c70998551f636d7d2123d5254fcbb Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 24 Oct 2025 20:37:43 +0000 Subject: [PATCH 05/21] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mypyc/irbuild/for_helpers.py | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py index 5e84f92e0631..33750979fe69 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -1212,30 +1212,44 @@ def check_type(obj: Any, typ: Type[Any]) -> bool: for_sequence_reverse_with_len_check = [ g for g in gens - if check_type(g, ForSequence) and (g := (g.gen if isinstance(g, ForEnumerate) else g)).reverse and g.len_reg is not None + if check_type(g, ForSequence) + and (g := (g.gen if isinstance(g, ForEnumerate) else g)).reverse + and g.len_reg is not None ] for_sequence_reverse_no_len_check = [ g for g in gens - if check_type(g, ForSequence) and (g := (g.gen if isinstance(g, ForEnumerate) else g)).reverse and g.len_reg is None + if check_type(g, ForSequence) + and (g := (g.gen if isinstance(g, ForEnumerate) else g)).reverse + and g.len_reg is None ] for_sequence_forward_with_len_check = [ g for g in gens if check_type(g, ForSequence) - and not (g := (g.gen if isinstance(g, ForEnumerate) else g)).reverse and g.len_reg is not None + and not (g := (g.gen if isinstance(g, ForEnumerate) else g)).reverse + and g.len_reg is not None ] for_sequence_forward_no_len_check = [ g for g in gens if check_type(g, ForSequence) - and not (g := (g.gen if isinstance(g, ForEnumerate) else g)).reverse and g.len_reg is None + and not (g := (g.gen if isinstance(g, ForEnumerate) else g)).reverse + and g.len_reg is None ] # these are really fast, just a C int equality check for_range = [g for g in gens if isinstance(g, ForRange)] - ordered = for_range + for_sequence_forward_no_len_check + for_sequence_forward_no_len_check + for_sequence_forward_with_len_check + for_sequence_reverse_with_len_check + for_native + for_dict + ordered = ( + for_range + + for_sequence_forward_no_len_check + + for_sequence_forward_no_len_check + + for_sequence_forward_with_len_check + + for_sequence_reverse_with_len_check + + for_native + + for_dict + ) # this is a failsafe for ForHelper classes which might have been added after this commit but not added to this function's code leftovers = [g for g in gens if g not in ordered + for_iterable] From 9ef69757b000eede814b95b208013654d02fbf66 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Fri, 24 Oct 2025 16:45:15 -0400 Subject: [PATCH 06/21] Update for_helpers.py --- mypyc/irbuild/for_helpers.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py index 33750979fe69..5ba619ff9687 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -1213,29 +1213,29 @@ def check_type(obj: Any, typ: Type[Any]) -> bool: g for g in gens if check_type(g, ForSequence) - and (g := (g.gen if isinstance(g, ForEnumerate) else g)).reverse - and g.len_reg is not None + and (for_seq := (g.gen if isinstance(g, ForEnumerate) else g)).reverse + and for_seq.len_reg is not None ] for_sequence_reverse_no_len_check = [ g for g in gens if check_type(g, ForSequence) - and (g := (g.gen if isinstance(g, ForEnumerate) else g)).reverse - and g.len_reg is None + and (for_seq := (g.gen if isinstance(g, ForEnumerate) else g)).reverse + and for_seq.len_reg is None ] for_sequence_forward_with_len_check = [ g for g in gens if check_type(g, ForSequence) - and not (g := (g.gen if isinstance(g, ForEnumerate) else g)).reverse - and g.len_reg is not None + and not (for_seq := (g.gen if isinstance(g, ForEnumerate) else g)).reverse + and for_seq.len_reg is not None ] for_sequence_forward_no_len_check = [ g for g in gens if check_type(g, ForSequence) - and not (g := (g.gen if isinstance(g, ForEnumerate) else g)).reverse - and g.len_reg is None + and not (for_seq := (g.gen if isinstance(g, ForEnumerate) else g)).reverse + and for_seq.len_reg is None ] # these are really fast, just a C int equality check @@ -1244,7 +1244,7 @@ def check_type(obj: Any, typ: Type[Any]) -> bool: ordered = ( for_range + for_sequence_forward_no_len_check - + for_sequence_forward_no_len_check + + for_sequence_reverse_no_len_check + for_sequence_forward_with_len_check + for_sequence_reverse_with_len_check + for_native From 3460d1094707fef736a26ee63a7aad057411d26f Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Fri, 24 Oct 2025 16:45:40 -0400 Subject: [PATCH 07/21] Update for_helpers.py --- mypyc/irbuild/for_helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py index 5ba619ff9687..3f21c56bfd1b 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -7,7 +7,7 @@ from __future__ import annotations -from typing import Callable, ClassVar +from typing import Any, Callable, ClassVar, Type from mypy.nodes import ( ARG_POS, From cf77a9f447b3e13a72ab7e89ec4c3670946b90f0 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Fri, 24 Oct 2025 16:53:14 -0400 Subject: [PATCH 08/21] Update for_helpers.py --- mypyc/irbuild/for_helpers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py index 3f21c56bfd1b..42c4ca98429f 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -7,7 +7,7 @@ from __future__ import annotations -from typing import Any, Callable, ClassVar, Type +from typing import Any, Callable, ClassVar from mypy.nodes import ( ARG_POS, @@ -1192,7 +1192,7 @@ def gen_condition(self) -> None: # ForIterable gens = self.gens - def check_type(obj: Any, typ: Type[Any]) -> bool: + def check_type(obj: Any, typ: type[Any]) -> bool: # ForEnumerate gen_condition is as fast as it's underlying generator's return ( isinstance(obj, typ) or isinstance(obj, ForEnumerate) and isinstance(obj.gen, typ) From 13c05332785b708738975de4478f7b93458fa3c0 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Fri, 24 Oct 2025 17:24:43 -0400 Subject: [PATCH 09/21] Update for_helpers.py --- mypyc/irbuild/for_helpers.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py index 42c4ca98429f..148870829335 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -1195,7 +1195,7 @@ def gen_condition(self) -> None: def check_type(obj: Any, typ: type[Any]) -> bool: # ForEnumerate gen_condition is as fast as it's underlying generator's return ( - isinstance(obj, typ) or isinstance(obj, ForEnumerate) and isinstance(obj.gen, typ) + isinstance(obj, typ) or isinstance(obj, ForEnumerate) and isinstance(obj.main_gen, typ) ) # these are slowest, they invoke Python's iteration protocol @@ -1213,28 +1213,28 @@ def check_type(obj: Any, typ: type[Any]) -> bool: g for g in gens if check_type(g, ForSequence) - and (for_seq := (g.gen if isinstance(g, ForEnumerate) else g)).reverse + and (for_seq := cast(ForSequence, g.main_gen if isinstance(g, ForEnumerate) else g))).reverse and for_seq.len_reg is not None ] for_sequence_reverse_no_len_check = [ g for g in gens if check_type(g, ForSequence) - and (for_seq := (g.gen if isinstance(g, ForEnumerate) else g)).reverse + and (for_seq := cast(ForSequence, g.main_gen if isinstance(g, ForEnumerate) else g)).reverse and for_seq.len_reg is None ] for_sequence_forward_with_len_check = [ g for g in gens if check_type(g, ForSequence) - and not (for_seq := (g.gen if isinstance(g, ForEnumerate) else g)).reverse + and not (for_seq := cast(ForSequence, g.main_gen if isinstance(g, ForEnumerate) else g)).reverse and for_seq.len_reg is not None ] for_sequence_forward_no_len_check = [ g for g in gens if check_type(g, ForSequence) - and not (for_seq := (g.gen if isinstance(g, ForEnumerate) else g)).reverse + and not (for_seq := cast(ForSequence, g.main_gen if isinstance(g, ForEnumerate) else g)).reverse and for_seq.len_reg is None ] From 1e57307c209f93383f432e6ef73391642590a4f2 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Fri, 24 Oct 2025 17:25:16 -0400 Subject: [PATCH 10/21] Update for_helpers.py --- mypyc/irbuild/for_helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py index 148870829335..641747baad4c 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -1192,7 +1192,7 @@ def gen_condition(self) -> None: # ForIterable gens = self.gens - def check_type(obj: Any, typ: type[Any]) -> bool: + def check_type(obj: Any, typ: type[ForGenerator]) -> bool: # ForEnumerate gen_condition is as fast as it's underlying generator's return ( isinstance(obj, typ) or isinstance(obj, ForEnumerate) and isinstance(obj.main_gen, typ) From ed38a48e065adac3547d86115ba889a5c29de9ad Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Fri, 24 Oct 2025 17:26:53 -0400 Subject: [PATCH 11/21] Update for_helpers.py --- mypyc/irbuild/for_helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py index 641747baad4c..52dfd9c6a55b 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -1213,7 +1213,7 @@ def check_type(obj: Any, typ: type[ForGenerator]) -> bool: g for g in gens if check_type(g, ForSequence) - and (for_seq := cast(ForSequence, g.main_gen if isinstance(g, ForEnumerate) else g))).reverse + and (for_seq := cast(ForSequence, g.main_gen if isinstance(g, ForEnumerate) else g)).reverse and for_seq.len_reg is not None ] for_sequence_reverse_no_len_check = [ From 1d4304488acc73e81dfb8357e88d456b8af611d2 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 24 Oct 2025 21:28:48 +0000 Subject: [PATCH 12/21] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mypyc/irbuild/for_helpers.py | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py index 52dfd9c6a55b..0f6f7c4a4021 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -1195,7 +1195,9 @@ def gen_condition(self) -> None: def check_type(obj: Any, typ: type[ForGenerator]) -> bool: # ForEnumerate gen_condition is as fast as it's underlying generator's return ( - isinstance(obj, typ) or isinstance(obj, ForEnumerate) and isinstance(obj.main_gen, typ) + isinstance(obj, typ) + or isinstance(obj, ForEnumerate) + and isinstance(obj.main_gen, typ) ) # these are slowest, they invoke Python's iteration protocol @@ -1213,28 +1215,36 @@ def check_type(obj: Any, typ: type[ForGenerator]) -> bool: g for g in gens if check_type(g, ForSequence) - and (for_seq := cast(ForSequence, g.main_gen if isinstance(g, ForEnumerate) else g)).reverse + and ( + for_seq := cast(ForSequence, g.main_gen if isinstance(g, ForEnumerate) else g) + ).reverse and for_seq.len_reg is not None ] for_sequence_reverse_no_len_check = [ g for g in gens if check_type(g, ForSequence) - and (for_seq := cast(ForSequence, g.main_gen if isinstance(g, ForEnumerate) else g)).reverse + and ( + for_seq := cast(ForSequence, g.main_gen if isinstance(g, ForEnumerate) else g) + ).reverse and for_seq.len_reg is None ] for_sequence_forward_with_len_check = [ g for g in gens if check_type(g, ForSequence) - and not (for_seq := cast(ForSequence, g.main_gen if isinstance(g, ForEnumerate) else g)).reverse + and not ( + for_seq := cast(ForSequence, g.main_gen if isinstance(g, ForEnumerate) else g) + ).reverse and for_seq.len_reg is not None ] for_sequence_forward_no_len_check = [ g for g in gens if check_type(g, ForSequence) - and not (for_seq := cast(ForSequence, g.main_gen if isinstance(g, ForEnumerate) else g)).reverse + and not ( + for_seq := cast(ForSequence, g.main_gen if isinstance(g, ForEnumerate) else g) + ).reverse and for_seq.len_reg is None ] From be36b77e99e051857340e32d2b95e23f52ba5bfd Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Fri, 24 Oct 2025 17:28:58 -0400 Subject: [PATCH 13/21] Update for_helpers.py --- mypyc/irbuild/for_helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py index 0f6f7c4a4021..f4aa5e26d6ac 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -7,7 +7,7 @@ from __future__ import annotations -from typing import Any, Callable, ClassVar +from typing import Any, Callable, ClassVar, cast from mypy.nodes import ( ARG_POS, From 947647118149078c71e4594a56c4fdb8fa003480 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Fri, 24 Oct 2025 17:36:47 -0400 Subject: [PATCH 14/21] Update for_helpers.py --- mypyc/irbuild/for_helpers.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py index f4aa5e26d6ac..0fe2fa1bca2a 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -1218,7 +1218,7 @@ def check_type(obj: Any, typ: type[ForGenerator]) -> bool: and ( for_seq := cast(ForSequence, g.main_gen if isinstance(g, ForEnumerate) else g) ).reverse - and for_seq.len_reg is not None + and for_seq.length_reg is not None ] for_sequence_reverse_no_len_check = [ g @@ -1227,7 +1227,7 @@ def check_type(obj: Any, typ: type[ForGenerator]) -> bool: and ( for_seq := cast(ForSequence, g.main_gen if isinstance(g, ForEnumerate) else g) ).reverse - and for_seq.len_reg is None + and for_seq.length_reg is None ] for_sequence_forward_with_len_check = [ g @@ -1236,7 +1236,7 @@ def check_type(obj: Any, typ: type[ForGenerator]) -> bool: and not ( for_seq := cast(ForSequence, g.main_gen if isinstance(g, ForEnumerate) else g) ).reverse - and for_seq.len_reg is not None + and for_seq.length_reg is not None ] for_sequence_forward_no_len_check = [ g @@ -1245,7 +1245,7 @@ def check_type(obj: Any, typ: type[ForGenerator]) -> bool: and not ( for_seq := cast(ForSequence, g.main_gen if isinstance(g, ForEnumerate) else g) ).reverse - and for_seq.len_reg is None + and for_seq.length_reg is None ] # these are really fast, just a C int equality check From c2dd71f15befb84eabcc918f838cf1226aba374b Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Fri, 24 Oct 2025 18:09:20 -0400 Subject: [PATCH 15/21] Update for_helpers.py --- mypyc/irbuild/for_helpers.py | 64 ++++++++++++++++++++---------------- 1 file changed, 36 insertions(+), 28 deletions(-) diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py index 0fe2fa1bca2a..64deca3cd760 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -1184,13 +1184,36 @@ def init(self, indexes: list[Lvalue], exprs: list[Expression]) -> None: ) self.gens.append(gen) + self.conditions, self.cond_blocks = self.__sort_conditions() + def gen_condition(self) -> None: + for i, gen in enumerate(ordered + leftovers + for_iterable): + gen.gen_condition() + if i < len(self.gens) - 1: + self.builder.activate_block(self.cond_blocks[i]) + + def begin_body(self) -> None: + for gen in self.gens: + gen.begin_body() + + def gen_step(self) -> None: + for gen in self.gens: + gen.gen_step() + + def gen_cleanup(self) -> None: + for gen in self.gens: + gen.gen_cleanup() + + def __sort_conditions(self) -> List[ForSequence]: # We don't necessarily need to check the gens in order, # we just need to know which gen ends first. Some gens # are quicker to check than others, so we will check the # specialized ForHelpers before we check any generic # ForIterable + gens = self.gens + cond_blocks = self.cond_blocks[:] + cond_blocks.remove(self.body_block) def check_type(obj: Any, typ: type[ForGenerator]) -> bool: # ForEnumerate gen_condition is as fast as it's underlying generator's @@ -1201,19 +1224,18 @@ def check_type(obj: Any, typ: type[ForGenerator]) -> bool: ) # these are slowest, they invoke Python's iteration protocol - for_iterable = [g for g in gens if check_type(g, ForSequence)] + for_iterable = [(g, block) for g, block in zip(gens, cond_blocks) if check_type(g, ForSequence)] # These aren't the slowest but they're slow, we need to pack an RTuple and then get and item and do a comparison - for_dict = [g for g in gens if check_type(g, ForDictionaryCommon)] + for_dict = [(g, block) for g, block in zip(gens, cond_blocks) if check_type(g, ForDictionaryCommon)] # These are faster than ForIterable but not as fast as others (faster than ForDict?) - for_native = [g for g in gens if check_type(g, ForNativeGenerator)] + for_native = [(g, block) for g, block in zip(gens, cond_blocks) if check_type(g, ForNativeGenerator)] # forward involves in the best case one pyssize_t comparison, else one length check + the comparison # reverse is slightly slower than forward, with one extra check for_sequence_reverse_with_len_check = [ - g - for g in gens + (g, block) for g, block in zip(gens, cond_blocks) if check_type(g, ForSequence) and ( for_seq := cast(ForSequence, g.main_gen if isinstance(g, ForEnumerate) else g) @@ -1221,8 +1243,7 @@ def check_type(obj: Any, typ: type[ForGenerator]) -> bool: and for_seq.length_reg is not None ] for_sequence_reverse_no_len_check = [ - g - for g in gens + (g, block) for g, block in zip(gens, cond_blocks) if check_type(g, ForSequence) and ( for_seq := cast(ForSequence, g.main_gen if isinstance(g, ForEnumerate) else g) @@ -1230,8 +1251,7 @@ def check_type(obj: Any, typ: type[ForGenerator]) -> bool: and for_seq.length_reg is None ] for_sequence_forward_with_len_check = [ - g - for g in gens + (g, block) for g, block in zip(gens, cond_blocks) if check_type(g, ForSequence) and not ( for_seq := cast(ForSequence, g.main_gen if isinstance(g, ForEnumerate) else g) @@ -1239,8 +1259,7 @@ def check_type(obj: Any, typ: type[ForGenerator]) -> bool: and for_seq.length_reg is not None ] for_sequence_forward_no_len_check = [ - g - for g in gens + (g, block) for g, block in zip(gens, cond_blocks) if check_type(g, ForSequence) and not ( for_seq := cast(ForSequence, g.main_gen if isinstance(g, ForEnumerate) else g) @@ -1249,7 +1268,7 @@ def check_type(obj: Any, typ: type[ForGenerator]) -> bool: ] # these are really fast, just a C int equality check - for_range = [g for g in gens if isinstance(g, ForRange)] + for_range = [(g, block) for g, block in zip(gens, cond_blocks) if isinstance(g, ForRange)] ordered = ( for_range @@ -1262,24 +1281,13 @@ def check_type(obj: Any, typ: type[ForGenerator]) -> bool: ) # this is a failsafe for ForHelper classes which might have been added after this commit but not added to this function's code - leftovers = [g for g in gens if g not in ordered + for_iterable] - - for i, gen in enumerate(ordered + leftovers + for_iterable): - gen.gen_condition() - if i < len(self.gens) - 1: - self.builder.activate_block(self.cond_blocks[i]) - - def begin_body(self) -> None: - for gen in self.gens: - gen.begin_body() + leftovers = [(g, block) for g, block in zip(gens, cond_blocks) if g not in ordered + for_iterable] - def gen_step(self) -> None: - for gen in self.gens: - gen.gen_step() + gens_and_blocks = ordered + leftovers + for_iterable + conditons = [g for (g, block) in gens_and_blocks] + cond_blocks = [block for (g, block) in gens_and_blocks] + [self.body_block] - def gen_cleanup(self) -> None: - for gen in self.gens: - gen.gen_cleanup() + return conditions, cond_blocks def get_expr_length(builder: IRBuilder, expr: Expression) -> int | None: From ffb7d1c4d5f305c2fd2ff0f9bc19757ac89c5b8b Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Fri, 24 Oct 2025 18:09:57 -0400 Subject: [PATCH 16/21] Update for_helpers.py --- mypyc/irbuild/for_helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py index 64deca3cd760..03328b9a2ec9 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -1204,7 +1204,7 @@ def gen_cleanup(self) -> None: for gen in self.gens: gen.gen_cleanup() - def __sort_conditions(self) -> List[ForSequence]: + def __sort_conditions(self) -> tuple[list[ForSequence], list[BasicBlock]]: # We don't necessarily need to check the gens in order, # we just need to know which gen ends first. Some gens # are quicker to check than others, so we will check the From ddb9e0cf14031d7d6e936a3f1f28b61ec243e8d1 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 24 Oct 2025 22:10:40 +0000 Subject: [PATCH 17/21] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mypyc/irbuild/for_helpers.py | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py index 03328b9a2ec9..8673c47b8fc5 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -1210,7 +1210,7 @@ def __sort_conditions(self) -> tuple[list[ForSequence], list[BasicBlock]]: # are quicker to check than others, so we will check the # specialized ForHelpers before we check any generic # ForIterable - + gens = self.gens cond_blocks = self.cond_blocks[:] cond_blocks.remove(self.body_block) @@ -1224,18 +1224,25 @@ def check_type(obj: Any, typ: type[ForGenerator]) -> bool: ) # these are slowest, they invoke Python's iteration protocol - for_iterable = [(g, block) for g, block in zip(gens, cond_blocks) if check_type(g, ForSequence)] + for_iterable = [ + (g, block) for g, block in zip(gens, cond_blocks) if check_type(g, ForSequence) + ] # These aren't the slowest but they're slow, we need to pack an RTuple and then get and item and do a comparison - for_dict = [(g, block) for g, block in zip(gens, cond_blocks) if check_type(g, ForDictionaryCommon)] + for_dict = [ + (g, block) for g, block in zip(gens, cond_blocks) if check_type(g, ForDictionaryCommon) + ] # These are faster than ForIterable but not as fast as others (faster than ForDict?) - for_native = [(g, block) for g, block in zip(gens, cond_blocks) if check_type(g, ForNativeGenerator)] + for_native = [ + (g, block) for g, block in zip(gens, cond_blocks) if check_type(g, ForNativeGenerator) + ] # forward involves in the best case one pyssize_t comparison, else one length check + the comparison # reverse is slightly slower than forward, with one extra check for_sequence_reverse_with_len_check = [ - (g, block) for g, block in zip(gens, cond_blocks) + (g, block) + for g, block in zip(gens, cond_blocks) if check_type(g, ForSequence) and ( for_seq := cast(ForSequence, g.main_gen if isinstance(g, ForEnumerate) else g) @@ -1243,7 +1250,8 @@ def check_type(obj: Any, typ: type[ForGenerator]) -> bool: and for_seq.length_reg is not None ] for_sequence_reverse_no_len_check = [ - (g, block) for g, block in zip(gens, cond_blocks) + (g, block) + for g, block in zip(gens, cond_blocks) if check_type(g, ForSequence) and ( for_seq := cast(ForSequence, g.main_gen if isinstance(g, ForEnumerate) else g) @@ -1251,7 +1259,8 @@ def check_type(obj: Any, typ: type[ForGenerator]) -> bool: and for_seq.length_reg is None ] for_sequence_forward_with_len_check = [ - (g, block) for g, block in zip(gens, cond_blocks) + (g, block) + for g, block in zip(gens, cond_blocks) if check_type(g, ForSequence) and not ( for_seq := cast(ForSequence, g.main_gen if isinstance(g, ForEnumerate) else g) @@ -1259,7 +1268,8 @@ def check_type(obj: Any, typ: type[ForGenerator]) -> bool: and for_seq.length_reg is not None ] for_sequence_forward_no_len_check = [ - (g, block) for g, block in zip(gens, cond_blocks) + (g, block) + for g, block in zip(gens, cond_blocks) if check_type(g, ForSequence) and not ( for_seq := cast(ForSequence, g.main_gen if isinstance(g, ForEnumerate) else g) @@ -1281,13 +1291,15 @@ def check_type(obj: Any, typ: type[ForGenerator]) -> bool: ) # this is a failsafe for ForHelper classes which might have been added after this commit but not added to this function's code - leftovers = [(g, block) for g, block in zip(gens, cond_blocks) if g not in ordered + for_iterable] + leftovers = [ + (g, block) for g, block in zip(gens, cond_blocks) if g not in ordered + for_iterable + ] gens_and_blocks = ordered + leftovers + for_iterable conditons = [g for (g, block) in gens_and_blocks] cond_blocks = [block for (g, block) in gens_and_blocks] + [self.body_block] - return conditions, cond_blocks + return conditions, cond_blocks def get_expr_length(builder: IRBuilder, expr: Expression) -> int | None: From aeb8fe189aca5260946af1319d3211a8c80179ed Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Fri, 24 Oct 2025 18:28:45 -0400 Subject: [PATCH 18/21] Update for_helpers.py --- mypyc/irbuild/for_helpers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py index 8673c47b8fc5..487ee911c20f 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -1187,7 +1187,7 @@ def init(self, indexes: list[Lvalue], exprs: list[Expression]) -> None: self.conditions, self.cond_blocks = self.__sort_conditions() def gen_condition(self) -> None: - for i, gen in enumerate(ordered + leftovers + for_iterable): + for i, gen in enumerate(self.conditions): gen.gen_condition() if i < len(self.gens) - 1: self.builder.activate_block(self.cond_blocks[i]) @@ -1296,7 +1296,7 @@ def check_type(obj: Any, typ: type[ForGenerator]) -> bool: ] gens_and_blocks = ordered + leftovers + for_iterable - conditons = [g for (g, block) in gens_and_blocks] + conditions = [g for (g, block) in gens_and_blocks] cond_blocks = [block for (g, block) in gens_and_blocks] + [self.body_block] return conditions, cond_blocks From c6b41b7a5031dcadf00dba710e3b76866ed566da Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Fri, 24 Oct 2025 19:38:33 -0400 Subject: [PATCH 19/21] Update for_helpers.py --- mypyc/irbuild/for_helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py index 487ee911c20f..effecc6385d5 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -1204,7 +1204,7 @@ def gen_cleanup(self) -> None: for gen in self.gens: gen.gen_cleanup() - def __sort_conditions(self) -> tuple[list[ForSequence], list[BasicBlock]]: + def __sort_conditions(self) -> tuple[list[ForGenerator], list[BasicBlock]]: # We don't necessarily need to check the gens in order, # we just need to know which gen ends first. Some gens # are quicker to check than others, so we will check the From 21ae3cb77bc1bdb7b389299d1fd15024741c0b5a Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Fri, 24 Oct 2025 19:40:53 -0400 Subject: [PATCH 20/21] Update for_helpers.py --- mypyc/irbuild/for_helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py index effecc6385d5..8cc1295a570d 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -1292,7 +1292,7 @@ def check_type(obj: Any, typ: type[ForGenerator]) -> bool: # this is a failsafe for ForHelper classes which might have been added after this commit but not added to this function's code leftovers = [ - (g, block) for g, block in zip(gens, cond_blocks) if g not in ordered + for_iterable + g_and_block for g_and_block in zip(gens, cond_blocks) if g_and_block not in ordered + for_iterable ] gens_and_blocks = ordered + leftovers + for_iterable From 41c38bc74a5f3b81ce8b43cb7280ac442f2c84a1 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 24 Oct 2025 23:42:15 +0000 Subject: [PATCH 21/21] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mypyc/irbuild/for_helpers.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py index 8cc1295a570d..4b3645d909cc 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -1292,7 +1292,9 @@ def check_type(obj: Any, typ: type[ForGenerator]) -> bool: # this is a failsafe for ForHelper classes which might have been added after this commit but not added to this function's code leftovers = [ - g_and_block for g_and_block in zip(gens, cond_blocks) if g_and_block not in ordered + for_iterable + g_and_block + for g_and_block in zip(gens, cond_blocks) + if g_and_block not in ordered + for_iterable ] gens_and_blocks = ordered + leftovers + for_iterable