Skip to content

micro optimisations #1290

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 0 commits into from
Closed

micro optimisations #1290

wants to merge 0 commits into from

Conversation

abebus
Copy link

@abebus abebus commented Jul 12, 2025

I forked Flake8 and hardcoded benchmarking utilities for pycodestyle. Below are performance comparisons before and after the changes.

flake8 src -q -j=1  

(Executed inside the Flake8 directory.)

Benchmark Results

Before Changes (3 Sample Runs)

{'ambiguous_identifier': 6266.212970376301,
 'bare_except': 1362.7702161729383,
 'blank_lines': 2238.630904723779,
 'break_after_binary_operator': 6683.987189751801,
 'break_before_binary_operator': 9769.095276220976,
 'comparison_negative': 2196.917534027222,
 'comparison_to_singleton': 6174.019215372298,
 'comparison_type': 2575.740592473979,
 'compound_statements': 2771.457165732586,
 'continued_indentation': 4376.821457165733,
 'explicit_line_join': 4076.6613290632504,
 'extraneous_whitespace': 2853.963170536429,
 'imports_on_separate_lines': 661.9295436349079,
 'indentation': 1103.8831064851881,
 'maximum_doc_length': 582.9463570856685,
 'missing_whitespace': 6795.916733386709,
 'missing_whitespace_after_keyword': 3579.263410728583,
 'module_imports_on_top_of_file': 1686.7493995196157,
 'python_3000_invalid_escape_sequence': 5908.88710968775,
 'whitespace_around_comma': 1799.519615692554,
 'whitespace_around_keywords': 4659.007205764612,
 'whitespace_around_named_parameter_equals': 3716.1729383506804,
 'whitespace_around_operator': 4288.75100080064,
 'whitespace_before_comment': 2058.0064051240993,
 'whitespace_before_parameters': 2485.6685348278625}
 
 {'ambiguous_identifier': 5786.9895916733385,
 'bare_except': 1216.453162530024,
 'blank_lines': 1999.759807846277,
 'break_after_binary_operator': 6445.15612489992,
 'break_before_binary_operator': 9439.271417133707,
 'comparison_negative': 2072.3779023218576,
 'comparison_to_singleton': 5855.964771817454,
 'comparison_type': 2494.915932746197,
 'compound_statements': 2532.906325060048,
 'continued_indentation': 4167.574059247398,
 'explicit_line_join': 3900.6004803843075,
 'extraneous_whitespace': 2749.7197758206567,
 'imports_on_separate_lines': 627.7822257806245,
 'indentation': 1021.0968775020016,
 'maximum_doc_length': 539.3915132105684,
 'missing_whitespace': 6527.822257806245,
 'missing_whitespace_after_keyword': 3332.5460368294634,
 'module_imports_on_top_of_file': 1529.663730984788,
 'python_3000_invalid_escape_sequence': 5769.535628502802,
 'whitespace_around_comma': 1676.8214571657327,
 'whitespace_around_keywords': 4349.399519615693,
 'whitespace_around_named_parameter_equals': 3625.8606885508407,
 'whitespace_around_operator': 4205.284227381905,
 'whitespace_before_comment': 1969.9759807846276,
 'whitespace_before_parameters': 2379.1032826261007}
 
 {'ambiguous_identifier': 5631.184947958367,
 'bare_except': 1143.3546837469976,
 'blank_lines': 1941.4331465172138,
 'break_after_binary_operator': 6253.402722177742,
 'break_before_binary_operator': 9311.36909527622,
 'comparison_negative': 1979.9839871897518,
 'comparison_to_singleton': 5698.198558847078,
 'comparison_type': 2453.522818254604,
 'compound_statements': 2532.666132906325,
 'continued_indentation': 4045.476381104884,
 'explicit_line_join': 3837.9503602882305,
 'extraneous_whitespace': 2686.5892714171337,
 'imports_on_separate_lines': 638.3506805444356,
 'indentation': 994.755804643715,
 'maximum_doc_length': 489.8318654923939,
 'missing_whitespace': 6497.59807846277,
 'missing_whitespace_after_keyword': 3203.722978382706,
 'module_imports_on_top_of_file': 1501.8414731785429,
 'python_3000_invalid_escape_sequence': 5667.974379503603,
 'whitespace_around_comma': 1682.7061649319455,
 'whitespace_around_keywords': 4345.556445156125,
 'whitespace_around_named_parameter_equals': 3558.7269815852683,
 'whitespace_around_operator': 4118.57485988791,
 'whitespace_before_comment': 1920.896717373899,
 'whitespace_before_parameters': 2310.008006405124}

After Changes (3 Sample Runs)

{'ambiguous_identifier': 6414.651721377101,
 'bare_except': 1218.1745396317053,
 'blank_lines': 2019.3755004003203,
 'break_after_binary_operator': 8678.943154523618,
 'break_before_binary_operator': 10450.360288230584,
 'comparison_negative': 2133.5068054443555,
 'comparison_to_singleton': 5898.759007205765,
 'comparison_type': 2517.8943154523618,
 'compound_statements': 5820.736589271417,
 'continued_indentation': 4096.116893514812,
 'explicit_line_join': 3992.2337870296237,
 'extraneous_whitespace': 2831.345076060849,
 'imports_on_separate_lines': 634.1873498799039,
 'indentation': 1037.590072057646,
 'maximum_doc_length': 542.2738190552442,
 'missing_whitespace': 6435.948759007206,
 'missing_whitespace_after_keyword': 2955.244195356285, !!!
 'module_imports_on_top_of_file': 1290.8726981585269, !!!
 'python_3000_invalid_escape_sequence': 3430.864691753403, !!!
 'whitespace_around_comma': 1715.4523618895116,
 'whitespace_around_keywords': 4547.237790232186,
 'whitespace_around_named_parameter_equals': 3835.268214571657,
 'whitespace_around_operator': 4169.095276220977,
 'whitespace_before_comment': 2086.188951160929,
 'whitespace_before_parameters': 2497.0376301040833}
 
 {'ambiguous_identifier': 6061.233480176212,
 'bare_except': 1179.8157789347217,
 'blank_lines': 1969.2831397677212,
 'break_after_binary_operator': 8506.407689227073,
 'break_before_binary_operator': 10938.606327593112,
 'comparison_negative': 2050.5406487785344,
 'comparison_to_singleton': 5764.4773728474165,
 'comparison_type': 2515.1782138566277,
 'compound_statements': 5542.450941129356,
 'continued_indentation': 4147.096515818983,
 'explicit_line_join': 3990.508610332399,
 'extraneous_whitespace': 2844.2931517821385,
 'imports_on_separate_lines': 651.5018021625951,
 'indentation': 1041.7701241489788,
 'maximum_doc_length': 496.79615538646374,
 'missing_whitespace': 6387.545054064878,
 'missing_whitespace_after_keyword': 2838.9667601121346,
 'module_imports_on_top_of_file': 1260.352422907489,
 'python_3000_invalid_escape_sequence': 3382.9395274329195,
 'whitespace_around_comma': 1665.678814577493,
 'whitespace_around_keywords': 4435.20224269123,
 'whitespace_around_named_parameter_equals': 3719.1830196235483,
 'whitespace_around_operator': 4171.565879054866,
 'whitespace_before_comment': 1989.9879855826991,
 'whitespace_before_parameters': 2549.7797356828196}
 
 {'ambiguous_identifier': 5939.8718975180145,
 'bare_except': 1133.9871897518015,
 'blank_lines': 1917.0536429143315,
 'break_after_binary_operator': 8452.562049639711,
 'break_before_binary_operator': 10740.952762209768,
 'comparison_negative': 1995.9567654123298,
 'comparison_to_singleton': 5816.413130504404,
 'comparison_type': 2463.530824659728,
 'compound_statements': 5400.80064051241,
 'continued_indentation': 3966.773418734988,
 'explicit_line_join': 3941.633306645316,
 'extraneous_whitespace': 2829.0632506004804,
 'imports_on_separate_lines': 626.4611689351481,
 'indentation': 999.2794235388311,
 'maximum_doc_length': 494.55564451561247,
 'missing_whitespace': 6276.981585268215,
 'missing_whitespace_after_keyword': 2802.522017614091,
 'module_imports_on_top_of_file': 1211.048839071257,
 'python_3000_invalid_escape_sequence': 3363.130504403523,
 'whitespace_around_comma': 1645.796637309848,
 'whitespace_around_keywords': 4389.551641313051,
 'whitespace_around_named_parameter_equals': 3716.1729383506804,
 'whitespace_around_operator': 4188.470776621297,
 'whitespace_before_comment': 2017.5740592473978,
 'whitespace_before_parameters': 2473.7389911929545}

Analysis

  • Some metrics regressed
  • Notable improvements in the following checks (marked with ! in the results):
    • missing_whitespace_after_keyword (↓ ~15–20%)
    • module_imports_on_top_of_file (↓ ~20–25%)
    • python_3000_invalid_escape_sequence (↓ ~40–45%)

So only the improvements in the three checks above (missing_whitespace_after_keyword, module_imports_on_top_of_file, and python_3000_invalid_escape_sequence) are significant enough to justify applying the changes.

@asottile
Copy link
Member

you're definitely going to need to split this up and demonstrate actual value from each of the changes

pycodestyle.py Outdated
len_chunks = len(chunks)
if ((len_chunks == 1 and multiline) or
(len_chunks == 2 and chunks[0] == '#')) and \
length - len(chunks[-1]) < max_line_length - 7:
Copy link
Author

@abebus abebus Jul 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reduced calling len(chunks) and len(line) from two times to one

pycodestyle.py Outdated
Comment on lines 410 to 412
line_indents = expand_indent(line)
if line.strip() and line_indents < ancestor_level:
ancestor_level = line_indents
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this reduces calling expand_indent(line)) from two times to one

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Except that now, we always call expand_indent and previously we called it twice if and only if line.strip() was not False. So we're reducing how often we calculate expand_indent but we're not calculating it unconditionally, including for empty lines that do not need it calculated.

Any benchmark you perform here needs to show the impact across a set of lines that cover a significant number of cases, not just the singular, less than representative benchmark case you've already shown.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

okay, i will cover and benchmark each change with different cases eventually

pycodestyle.py Outdated
'u',
'U',
]
valid = python_3000_valid
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why bother aliasing at all? This doesn't seem worth the indirection

Copy link
Author

@abebus abebus Jul 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i did it just to keep changes minimal as possible, but i think it will be okay to rename valid to python_3000_valid in the rest of the function

pycodestyle.py Outdated
@@ -1576,7 +1580,8 @@ def ambiguous_identifier(logical_line, tokens):
brace_depth = 0
idents_to_avoid = ('l', 'O', 'I')
prev_type, prev_text, prev_start, prev_end, __ = tokens[0]
for index in range(1, len(tokens)):
len_tokens = len(tokens)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❯ python -m timeit -s 'l = [0] * 10000' -- <<EOF
> if len(l):
>   if len(l):
>     pass
> EOF
20000000 loops, best of 5: 6.54 nsec per loop

And

❯ python -m timeit -s 'l = [0] * 10000' -- 'len(l)'
5000000 loops, best of 5: 20.9 nsec per loop

This is not even a microoptimization. len() is not expensive not for the number of tokens we're looking at. Even if I make the list significantly larger:

❯ python -m timeit -s 'l = [0] * 1000000000' -- <<EOF
if len(l):
  if len(l):
    pass
EOF
50000000 loops, best of 5: 7.08 nsec per loop

It doesn't make that significant of a difference.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i agree that difference is negligible, whole pr is about some tiny tiny micro optimisations. but i think it still worth to not have one function call inside a loop. more details here

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My point is that a micro optimization might be tiny but still impactful. This is an increase in cognitive complexity without meaningful impact it seems.

@@ -337,7 +337,7 @@ def test_check_nullbytes(self):
"stdin:1:1: E901 SyntaxError: source code string cannot contain null bytes", # noqa: E501
"stdin:1:1: E901 TokenError: source code cannot contain null bytes", # noqa: E501
]
self.assertEqual(stdout.splitlines(), expected)
self.assertEqual(stdout.splitlines().sort(), expected.sort())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shouldn't be necessary. I wonder if this fails without this

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, it fails... and i don't know why. but i think it's okay to have a different order of yields, they could even change in different versions of python, even if the code is the same

Copy link
Author

@abebus abebus Jul 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

after checking out to the latest remote commit, i've found that is was not my changes that reordered yields. running tox on platform linux -- Python 3.11.11[pypy-7.3.19-final], pytest-8.4.1, pluggy-1.6.0 fails with


self = <tests.test_api.APITestCase testMethod=test_check_nullbytes>

    def test_check_nullbytes(self):
        pycodestyle.register_check(DummyChecker, ['Z701'])
    
        pep8style = pycodestyle.StyleGuide()
        count_errors = pep8style.input_file('stdin', lines=['\x00\n'])
    
        stdout = sys.stdout.getvalue()
        if sys.version_info < (3, 11, 4):  # pragma: <3.11 cover
            expected = ["stdin:1:1: E901 ValueError: source code string cannot contain null bytes"]  # noqa: E501
        elif sys.version_info < (3, 12):  # pragma: <3.12 cover  # pragma: >=3.11 cover  # noqa: E501
            expected = ["stdin:1:1: E901 SyntaxError: source code string cannot contain null bytes"]  # noqa: E501
        else:  # pragma: >=3.12 cover
            expected = [
                "stdin:1:1: E901 SyntaxError: source code string cannot contain null bytes",   # noqa: E501
                "stdin:1:1: E901 TokenError: source code cannot contain null bytes",   # noqa: E501
            ]
>       self.assertEqual(stdout.splitlines(), expected)
E       AssertionError: Lists differ: ['stdin:1:1: E901 ValueError: source code string cannot contain null bytes'] != ['stdin:1:1: E901 SyntaxError: source code string cannot contain null bytes']
E       
E       First differing element 0:
E       'stdin:1:1: E901 ValueError: source code string cannot contain null bytes'
E       'stdin:1:1: E901 SyntaxError: source code string cannot contain null bytes'
E       
E       - ['stdin:1:1: E901 ValueError: source code string cannot contain null bytes']
E       ?                   ^ ^^^
E       
E       + ['stdin:1:1: E901 SyntaxError: source code string cannot contain null bytes']
E       ?                   ^^^^ ^

tests/test_api.py:340: AssertionError

so i assume it's solely pypy`s responsibility

pycodestyle.py Outdated
@@ -786,7 +788,7 @@ def whitespace_before_parameters(logical_line, tokens):
E211: dict['key'] = list [index]
"""
prev_type, prev_text, __, prev_end, __ = tokens[0]
for index in range(1, len(tokens)):
for index, _ in enumerate(tokens):
Copy link
Author

@abebus abebus Jul 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reduces two function calls to one, plus this approach lets enumerate generate indexes without calculating len of tokens

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Calculating len(tokens) is not expensive. This is different in that we're still increasing the cost of what we do here.

Previously, we create a single range object and iterate over it. Now, we create an iterator and we always return both thus causing an incref for the _ binding above and eventually a decref. So it increases cost in the GC logic. Besides, if we're iterating this way, why not avoid the next line (790) by instead doing

for index, (token_type, text, start, end, __) in enumerate(tokens):

This would be yet more efficient given you're focused on (thus far) not meaningful "optimizations".

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, you're right, i've also spotted this a few hours ago and will change this line on your suggested one

pycodestyle.py Outdated
Comment on lines 1165 to 1170
def is_string_literal(line):
if line[0] in 'uUbB':
line = line[1:]
if line and line[0] in 'rR':
line = line[1:]
return line and (line[0] == '"' or line[0] == "'")
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this eliminates a tiny overhead of creating function inside each module_imports_on_top_of_file call

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Folks import pycodestyle as a library. This now exposes this as a public API interface which we do not want to maintain.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

simple fix is rename it to _is_string_literal

pycodestyle.py Outdated
Comment on lines 1609 to 1612
if prev_text in ('as', 'for', 'global', 'nonlocal') and \
text in idents_to_avoid:
ident = text
pos = start
Copy link
Author

@abebus abebus Jul 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

every time i've combined two nested if statements into one with and, it was only just to reduce indentation. no performance impact, the bytecode (in 3.13) is the same

pycodestyle.py Outdated
Comment on lines 1583 to 1584
len_tokens = len(tokens)
for index in range(1, len_tokens):
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this reduces calculating len(tokens) inside a loop, which is a small but unnecessary overhead. i didn't change range to enumerate due to we already calculated and will use len(tokens)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think that

for index in range(1, len(tokens)):

Re-calculates len(tokens) on every iteration of the loop?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no, it'not, it is happening in line 1617

pycodestyle.py Outdated
Comment on lines 1636 to 1655
python_3000_valid = frozenset([
'\n',
'\\',
'\'',
'"',
'a',
'b',
'f',
'n',
'r',
't',
'v',
'0', '1', '2', '3', '4', '5', '6', '7',
'x',

# Escape sequences only recognized in string literals
'N',
'u',
'U',
])
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

extracting this from function and making this a set have two benefits: 1- we don't need to create a new list on each python_3000_invalid_escape_sequence call. 2- lookups will become amortised constant instead of linear time, and this is very good since lookups are in a while loop

pycodestyle.py Outdated
Comment on lines 1712 to 1718
lines_to_skip = SKIP_COMMENTS.union([tokenize.STRING])
skip_lines = False
# Skip lines that
for token_type, text, start, end, line in tokens:
if token_type not in SKIP_COMMENTS.union([tokenize.STRING]):
skip_lines.add(line)
if token_type not in lines_to_skip:
skip_lines = True
break
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

honestly, i did not fully understood what those line are meant for, but considering that skip_lines will only be used only for checking are they empty or not, i assumed it is save to make it a bool and break from loop when it is set to True

pycodestyle.py Outdated
Comment on lines 1728 to 1743
lines_len = len(lines)
for line_num, physical_line in enumerate(lines):
if start[0] + line_num == 1 and line.startswith('#!'):
return
length = len(physical_line)
chunks = physical_line.split()
if token_type == tokenize.COMMENT:
if (len(chunks) == 2 and
length - len(chunks[-1]) < MAX_DOC_LENGTH):
continue
if len(chunks) == 1 and line_num + 1 < len(lines):
if (len(chunks) == 1 and
length - len(chunks[-1]) < MAX_DOC_LENGTH):
continue
len_chunks = len(chunks)
len_last_chunk = len(chunks[-1]) if chunks else None
if token_type == tokenize.COMMENT and \
(len_chunks == 2 and
length - len_last_chunk < MAX_DOC_LENGTH):
continue
if len_chunks == 1 and line_num + 1 < lines_len and \
(len_chunks == 1 and
length - len_last_chunk < MAX_DOC_LENGTH):
continue
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this reduces unnecessary recalculation of length of those objects

pycodestyle.py Outdated
Comment on lines 2161 to 2170
elif not parens and token_type in NEWLINE:
if token_type == tokenize.NEWLINE:
self.check_logical()
self.blank_before = 0
elif len(self.tokens) == 1:
# The physical line contains only this token.
self.blank_lines += 1
del self.tokens[0]
else:
self.check_logical()
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

only for indentation

@@ -337,7 +337,7 @@ def test_check_nullbytes(self):
"stdin:1:1: E901 SyntaxError: source code string cannot contain null bytes", # noqa: E501
"stdin:1:1: E901 TokenError: source code cannot contain null bytes", # noqa: E501
]
self.assertEqual(stdout.splitlines(), expected)
self.assertEqual(stdout.splitlines().sort(), expected.sort())
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is strange, but changing nested ifs to one with and results in different order of yields

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then this isn't a optimization any longer and it's a change in real behaviour that will be obvious to the uesr

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, it'not, i will revert changes that affect indentation

pycodestyle.py Outdated
Comment on lines 1623 to 1628
if prev_text == 'class' and \
text in idents_to_avoid:
yield start, "E742 ambiguous class definition '%s'" % text
if prev_text == 'def' and \
text in idents_to_avoid:
yield start, "E743 ambiguous function definition '%s'" % text
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indentation

@abebus
Copy link
Author

abebus commented Jul 13, 2025

i'm currently working on benchmarking the affected code, but overall there is no significant performance gain
i will definetly make a full report, but here is a little snippet:

import pyperf
import tokenize
from io import StringIO
from pycodestyle import maximum_doc_length

logical_line = "This is a sample docstring that exceeds the maximum length. This is a sample docstring that exceeds the maximum length."
max_doc_length = 72
noqa = False

tokens = list(tokenize.generate_tokens(StringIO(logical_line).readline))

def benchmark_maximum_doc_length():
    list(maximum_doc_length(logical_line, max_doc_length, noqa, tokens))

runner = pyperf.Runner()
runner.timeit(
    name="maximum_doc_length",
    stmt="benchmark_maximum_doc_length()",
    globals=globals()
)

running this before changes resulted in maximum_doc_length: Mean +- std dev: 12.5 us +- 0.8 us and now it is maximum_doc_length: Mean +- std dev: 4.18 us +- 0.34 us

@abebus
Copy link
Author

abebus commented Jul 14, 2025

Work in progress, albeit slow, I need to make sure all the changes are actually valuable.

By the way, @asottile, could you please clarify what you mean by "split it up"? Split each function change into a separate PR, or create separate PRs for each "microoptimisation" technique?

@asottile
Copy link
Member

Work in progress, albeit slow, I need to make sure all the changes are actually valuable.

By the way, @asottile, could you please clarify what you mean by "split it up"? Split each function change into a separate PR, or create separate PRs for each "microoptimisation" technique?

yeah generally each improvement should be done as a separate PR with some benchmarks showing it actually improves things

@abebus
Copy link
Author

abebus commented Jul 17, 2025

I edited the description, added benchmarks and described how I conducted them, I found that only 3 changes are worth attention, the rest either became worse, or the results are slightly less but within the margin of error. PRs that I opened: #1291 #1292 #1293

@abebus
Copy link
Author

abebus commented Jul 17, 2025

Also, I want to express my deep gratitude to you all for creating and supporting this utilities. I believe and want these tools to live and develop and become only better and cooler, thank you again

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants