GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — GH-91432: Add more FOR_ITER specializations by sweeneyde · Pull Request #94096 · python/cpython

Conversation

@sweeneyde

@sweeneyde sweeneyde commented Jun 21, 2022

Copy link
Copy Markdown
Member

#91432

This does:

  • FOR_ITER(tuple)
  • FOR_ITER(dict_items) + UNPACK_SEQUENCE(2)
    • Eliminates handling the tuple, one dispatch
  • FOR_ITER(enumerate) + UNPACK_SEQUENCE(2) + STORE_FAST
    • Eliminates handling the tuple, two dispatches, allocating the PyLongObject

I'm not sure whether all of these are worth it, but I want to see how this moves stats and micro- and macro- benchmarks.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
sweeneyde added 3 commits June 21, 2022 14:37
@sweeneyde
get FOR_ITER_TUPLE working
b899628
@sweeneyde
get dict items working
7b8c26a
@sweeneyde
get enumerate working
1319529
@bedevere-bot bedevere-bot added the awaiting core review label Jun 21, 2022
sweeneyde added 2 commits June 21, 2022 17:38
@sweeneyde
git add the new file
1c421c0
@sweeneyde
comments, ifdefs so no unused variable
11da110
@sweeneyde

sweeneyde commented Jun 21, 2022

Copy link
Copy Markdown
Member Author

In microbenchmarks, it seems adding these extra opcodes bumped some things around and made FOR_ITER_RANGE and FOR_ITER_LIST a bit slower. Maybe the compiler decided not to inline _PyLong_AssignValue after this change, since this code uses it in two places.

But dict items, enumerate, and tuple did speed up, as expected.

microbenchmark script
from pyperf import Runner, perf_counter
from itertools import repeat

def for_range(loops, length):
    repetitions = repeat(None, loops)
    R = range(length)

    t0 = perf_counter()
    for _ in repetitions:
        for x in R:
            pass
    t1 = perf_counter()

    return t1 - t0

def for_list(loops, length):
    repetitions = repeat(None, loops)
    L = list(map(float, range(length)))

    t0 = perf_counter()
    for _ in repetitions:
        for x in L:
            pass
    t1 = perf_counter()

    return t1 - t0

def for_tuple(loops, length):
    repetitions = repeat(None, loops)
    T = tuple(map(float, range(length)))

    t0 = perf_counter()
    for _ in repetitions:
        for x in T:
            pass
    t1 = perf_counter()

    return t1 - t0

def for_dict(loops, length):
    repetitions = repeat(None, loops)
    D = dict.fromkeys(map(float, range(length)))

    t0 = perf_counter()
    for _ in repetitions:
        for x, y in D.items():
            pass
    t1 = perf_counter()

    return t1 - t0

def for_enumerate(loops, length):
    repetitions = repeat(None, loops)
    L = [None] * length

    t0 = perf_counter()
    for _ in repetitions:
        for i, x in enumerate(L):
            pass
    t1 = perf_counter()

    return t1 - t0

def for_map(loops, length):
    repetitions = repeat(None, loops)
    L = [()] * length

    t0 = perf_counter()
    for _ in repetitions:
        for x in map(len, L):
            pass
    t1 = perf_counter()

    return t1 - t0


def for_string(loops, length):
    repetitions = repeat(None, loops)
    S = "a" * length

    t0 = perf_counter()
    for _ in repetitions:
        for x in S:
            pass
    t1 = perf_counter()

    return t1 - t0

def for_set(loops, length):
    repetitions = repeat(None, loops)
    S = {f"a{i}" for i in range(length)}

    t0 = perf_counter()
    for _ in repetitions:
        for x in S:
            pass
    t1 = perf_counter()

    return t1 - t0


bench = Runner().bench_time_func
for n in [20, 200, 2_000, 20_000]:
    bench(f"for_range {n:_}", for_range, n, inner_loops=n)
    bench(f"for_list {n:_}", for_list, n, inner_loops=n)
    bench(f"for_tuple {n:_}", for_tuple, n, inner_loops=n)
    bench(f"for_dict {n:_}", for_dict, n, inner_loops=n)
    bench(f"for_enumerate {n:_}", for_enumerate, n, inner_loops=n)
    bench(f"for_map {n:_}", for_map, n, inner_loops=n)
    bench(f"for_string {n:_}", for_string, n, inner_loops=n)
    bench(f"for_set {n:_}", for_set, n, inner_loops=n)
Slower (7):
- for_range 2_000: 4.01 ns +- 0.02 ns -> 5.58 ns +- 0.14 ns: 1.39x slower
- for_range 200: 4.47 ns +- 0.06 ns -> 6.04 ns +- 0.43 ns: 1.35x slower
- for_range 20_000: 4.19 ns +- 0.03 ns -> 5.57 ns +- 0.35 ns: 1.33x slower
- for_range 20: 6.03 ns +- 0.32 ns -> 7.58 ns +- 0.27 ns: 1.26x slower
- for_list 20: 5.95 ns +- 0.44 ns -> 6.19 ns +- 0.85 ns: 1.04x slower
- for_list 20_000: 4.56 ns +- 0.17 ns -> 4.73 ns +- 0.38 ns: 1.04x slower
- for_list 200: 4.69 ns +- 0.26 ns -> 4.84 ns +- 0.36 ns: 1.03x slower

Faster (24):
- for_enumerate 20_000: 22.2 ns +- 0.4 ns -> 9.52 ns +- 0.39 ns: 2.34x faster
- for_enumerate 2_000: 21.2 ns +- 0.4 ns -> 9.58 ns +- 0.37 ns: 2.21x faster
- for_dict 20_000: 13.5 ns +- 0.1 ns -> 7.52 ns +- 0.05 ns: 1.80x faster
- for_dict 2_000: 13.4 ns +- 0.1 ns -> 7.52 ns +- 0.13 ns: 1.79x faster
- for_dict 200: 13.8 ns +- 0.2 ns -> 7.80 ns +- 0.13 ns: 1.77x faster
- for_dict 20: 17.5 ns +- 0.2 ns -> 11.6 ns +- 0.1 ns: 1.51x faster
- for_enumerate 200: 13.5 ns +- 0.3 ns -> 9.71 ns +- 0.41 ns: 1.39x faster
- for_tuple 20_000: 6.49 ns +- 0.05 ns -> 4.97 ns +- 0.04 ns: 1.31x faster
- for_enumerate 20: 17.5 ns +- 0.5 ns -> 13.4 ns +- 0.4 ns: 1.30x faster
- for_tuple 20: 8.20 ns +- 0.06 ns -> 6.31 ns +- 0.23 ns: 1.30x faster
- for_tuple 2_000: 6.49 ns +- 0.05 ns -> 5.01 ns +- 0.22 ns: 1.29x faster
- for_tuple 200: 6.65 ns +- 0.07 ns -> 5.16 ns +- 0.14 ns: 1.29x faster
- for_string 2_000: 6.75 ns +- 0.06 ns -> 5.85 ns +- 0.21 ns: 1.16x faster
- for_string 20_000: 6.74 ns +- 0.04 ns -> 5.87 ns +- 0.30 ns: 1.15x faster
- for_string 200: 6.90 ns +- 0.09 ns -> 6.16 ns +- 0.39 ns: 1.12x faster
- for_string 20: 8.59 ns +- 0.08 ns -> 7.70 ns +- 0.26 ns: 1.12x faster
- for_set 200: 8.50 ns +- 0.10 ns -> 7.71 ns +- 0.10 ns: 1.10x faster
- for_set 20: 12.4 ns +- 0.1 ns -> 11.4 ns +- 0.2 ns: 1.09x faster
- for_map 20_000: 17.8 ns +- 0.3 ns -> 16.4 ns +- 0.3 ns: 1.08x faster
- for_map 2_000: 17.9 ns +- 0.4 ns -> 16.5 ns +- 0.5 ns: 1.08x faster
- for_map 200: 18.5 ns +- 0.3 ns -> 17.2 ns +- 0.2 ns: 1.07x faster
- for_map 20: 22.0 ns +- 0.3 ns -> 20.9 ns +- 0.4 ns: 1.05x faster
- for_set 20_000: 22.4 ns +- 0.2 ns -> 21.4 ns +- 0.5 ns: 1.05x faster
- for_set 2_000: 12.8 ns +- 0.1 ns -> 12.3 ns +- 0.3 ns: 1.04x faster

Benchmark hidden because not significant (1): for_list 2_000

Geometric mean: 1.18x faster
All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@corona10 corona10 mentioned this pull request Jun 22, 2022
GH-91432: Implement the FOR_ITER_SET specialization #94104
Closed
@markshannon

markshannon commented Jun 22, 2022 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member

Before adding any more specializations for builtin iterators, I'd like to try implementing faster-cpython/ideas#392 and add specialization for generators.

sweeneyde reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@sweeneyde sweeneyde closed this Jul 27, 2022
@sweeneyde sweeneyde mentioned this pull request Dec 12, 2022
Avoid BUILD_TUPLE followed by RETURN_VALUE if UNPACK_SEQUENCE is right after CALL faster-cpython/ideas#509
Closed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@markshannon markshannon Awaiting requested review from markshannon markshannon will be requested when the pull request is marked ready for review markshannon is a code owner
@methane methane Awaiting requested review from methane methane will be requested when the pull request is marked ready for review methane is a code owner
@erlend-aasland erlend-aasland Awaiting requested review from erlend-aasland erlend-aasland will be requested when the pull request is marked ready for review erlend-aasland is a code owner
@AA-Turner AA-Turner Awaiting requested review from AA-Turner AA-Turner will be requested when the pull request is marked ready for review AA-Turner is a code owner
@emmatyping emmatyping Awaiting requested review from emmatyping emmatyping will be requested when the pull request is marked ready for review emmatyping is a code owner
@itamaro itamaro Awaiting requested review from itamaro itamaro will be requested when the pull request is marked ready for review itamaro is a code owner

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

awaiting core review

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

3 participants

@sweeneyde @markshannon @bedevere-bot
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.