with 3.12.1 I got: ``` Builtin any (true) : 0.043s Custom any (true) : 0.099s Builtin any (false): 0.351s Custom any (false): 1.617s Builtin all (true) : 0.301s Custom all (true) : 1.594s Builtin all (false): 0.044s Custom all (false): 0.074s ```
Your custom_any is not equivalent to the built-in any. In order to prove your point about a custom function possibly being faster that a built-in one, you must make a function equivalent to the built-in, and give it exatly the same input. Then and only then, the benchmark would make a valid comparison.
Love that you brought up the lesser known Set performance 👏I love leveraging them for that very reason! Also, nice to know direct dict assignment is more performant, and did not know some built-ins actually can hurt your performance 😓...good reminder to test test test
Another great video. I've watched a couple of other Python optimization vids that give general recommendations such as "use Cython" or "try concurrency", but these were some very specific, thoughtful recommendations. Cheers!
When using sets, keep in mind that initializing the set is more expensive than initializing the list. More often than not is it still better to perform the containment check using a list, because you are also initializing the set in the code.
t0 = timeit( "any(l)", globals={"numbers": numbers, "l": [n % 3 == 0 and n % 5 == 0 for n in numbers]}, number=RUNS, ) The flaw is you should pass into any an iterator (like a generator expression) for it to short circuit. Instead you pass in a list. So the majority of time contributing to any's perceived slowness is actually constructing that list, while any is actually very fast.
I was very surprised to see that the any and all builtins were slower that custom functions. I even checked on my machine with Python 3.9, 3.10, 3.11 and 3.12 and the outcome was the same each time. The builtins were slower.
That point was originally going to be about always using built-ins, just happened to select any/all as the example, and learned then and there they were actually slower. I was as amazed as you! I think it's an implementation detail as I believe it probably doesn't exit early as soon as the answer is known, but instead checks everything.
@@Carberra I found a Stackoverflow post (Just the title because CZcams does not allow links: "Python: any() unexpected performance") where this behaviour is explained. The problem lies in the fact that you passed a generator to any, not a list.
Ah that explains it. Though running it through all situations (on my benchmarks), the answer's logic is still slower in worst-case, though faster in best-case.
with 3.12.1 I got:
```
Builtin any (true) : 0.043s
Custom any (true) : 0.099s
Builtin any (false): 0.351s
Custom any (false): 1.617s
Builtin all (true) : 0.301s
Custom all (true) : 1.594s
Builtin all (false): 0.044s
Custom all (false): 0.074s
```
maybe for builtin case he included list creation into benchmark?
Your custom_any is not equivalent to the built-in any. In order to prove your point about a custom function possibly being faster that a built-in one, you must make a function equivalent to the built-in, and give it exatly the same input. Then and only then, the benchmark would make a valid comparison.
Love that you brought up the lesser known Set performance 👏I love leveraging them for that very reason! Also, nice to know direct dict assignment is more performant, and did not know some built-ins actually can hurt your performance 😓...good reminder to test test test
Another great video. I've watched a couple of other Python optimization vids that give general recommendations such as "use Cython" or "try concurrency", but these were some very specific, thoughtful recommendations. Cheers!
When using sets, keep in mind that initializing the set is more expensive than initializing the list. More often than not is it still better to perform the containment check using a list, because you are also initializing the set in the code.
I question your builtin any vs custom any test. Builtin any does short-circuit, there's no reason it would be slower.
t0 = timeit(
"any(l)",
globals={"numbers": numbers, "l": [n % 3 == 0 and n % 5 == 0 for n in numbers]},
number=RUNS,
)
The flaw is you should pass into any an iterator (like a generator expression) for it to short circuit. Instead you pass in a list. So the majority of time contributing to any's perceived slowness is actually constructing that list, while any is actually very fast.
I was very surprised to see that the any and all builtins were slower that custom functions. I even checked on my machine with Python 3.9, 3.10, 3.11 and 3.12 and the outcome was the same each time. The builtins were slower.
That point was originally going to be about always using built-ins, just happened to select any/all as the example, and learned then and there they were actually slower. I was as amazed as you! I think it's an implementation detail as I believe it probably doesn't exit early as soon as the answer is known, but instead checks everything.
Do you think there's any reason the devs just left it as is?
@@Carberra I found a Stackoverflow post (Just the title because CZcams does not allow links: "Python: any() unexpected performance") where this behaviour is explained. The problem lies in the fact that you passed a generator to any, not a list.
Ah that explains it. Though running it through all situations (on my benchmarks), the answer's logic is still slower in worst-case, though faster in best-case.
@@Carberra in all my benchmarks, the built-in is faster in all cases
Great video, thanks for sharing. Colorscheme, pls? 😊
Thanks! Ayu Mirage Bordered is the scheme -- there's a video for my full VS Code setup in the description 😄
My favourite Python optimisation is using C instead.
Just kidding, I love Python
Even better, use Assembly!
The fact that the dedent function takes a string is so strange. It should just evaluate the AST.
Dedent isn't specific to this use case -- I just used it to make the code more readable in the examples.