Skip to content
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

Made many Rect/FRect methods fastcall #2043

Merged
merged 3 commits into from
Sep 24, 2023

Conversation

itzpr3d4t0r
Copy link
Member

@itzpr3d4t0r itzpr3d4t0r commented Mar 22, 2023

I created a new function that converts fastcall arguments in a rect to avoid repetition. I also made a bunch of easily modifiable Rect methods that take rects as arguments METH_FASTCALL. This should generally make them faster. I cleaned up the internal Rect init` function as well. This PR, along with #2041, should make non-rect conversions substantially faster.

Here are the changes I made:

  • Added RectExport_RectFromFastcallArgs internal function
  • Changed colliderect to use the new RectExport_RectFromFastcallArgs function
  • Cleaned Rect init method a bit.
  • Changed clip to use FASTCALL
  • Changed clamp/clamp_ip to use FASTCALL
  • Changed fit to use FASTCALL
  • Changed update to use FASTCALL
  • Changed union/union_ip to use FASTCALL

Results:

TLDR:

  • If a Rect/FRect or 4 args are passed, the new implementation is 2X faster on my PC.
  • Otherwise, they are generally 20-30-40% faster.

Note:
Rect constructors/colliderect might appear slightly slower, but they are results that are in the noise and are not significant. In fact, colliderect has the same functionality as before and it just uses the new function. The constructor was just tidied up a bit."

OLD RECT

Rect (ms) r 4a 1a 2a 1a 2s r attr r meth
instantiation 95 144.7 145.7 144.9 160.4 255.3 334.7
update 77.4 125.6 133 139.6 151.3 239.9 326.1
union 97.7 154.3 154.6 157.2 172.3 277.2 346.3
union_ip 79.3 126 133.9 140.9 154.8 263.5 354.7
clip 101 150.3 159.2 161.6 178.5 266.6 349
colliderect 34.1 63.3 91.9 91.2 115.7 199.4 287.6
fit 100.2 151.2 155.7 160.4 174.4 266.9 346.9
clamp 98.4 149.9 155.8 160.8 174.9 269.5 343.9
clamp_ip 79 125.9 134.5 140.8 150.7 240.5 328.4

NEW RECT

Rect (ms) r 4a 1a 2a 1a 2s r attr r meth
instantiation 95 144.6 150.1 151.5 163.6 259.4 337.3
update 28 64 90.6 82.9 112 197 282.1
union 49.2 75.2 108 104 131.4 223.3 299
union_ip 29.9 62.6 89 85.7 113.9 198.2 282.8
clip 51.3 77.6 112.2 105 132.9 220.1 301.3
colliderect 35.4 63.9 94.8 87.2 120.2 201.7 280.9
fit 50.8 79.4 109 106.6 130.9 217.9 300.6
clamp 51.3 77.1 111 105.3 129.1 219.1 302.6
clamp_ip 32.5 63.7 90.9 85.8 113.4 200.8 283.8

OLD FRECT

FRect (ms) r 4a 1a 2a 1a 2s r attr r meth
instantiation 98.6 139.4 145.3 149.8 167.1 268.3 334.7
update 69 108.9 120.5 129.8 152.7 252.8 333.3
union 90.7 129.9 140.2 144.7 165.9 273.1 345
union_ip 70.7 115 121.1 130.8 152.3 249.9 331.2
clip 88.9 125.9 139.8 148.5 168.6 284.7 352.6
colliderect 34.4 68.5 91.6 84.5 111.5 209.9 297.9
fit 90.3 128.9 138.2 145.3 166.2 273.2 348.4
clamp 103.2 144.9 151.5 155.6 175.5 280.7 352.4
clamp_ip 72.3 110.8 122.5 131 152.9 256.1 335.5

NEW FRECT

FRect (ms) r 4a 1a 2a 1a 2s r attr r meth
instantiation 96.8 134.6 143.2 143.7 160.2 258.9 336
update 29.7 59.1 88.7 78.4 99.5 198.3 279.2
union 49.9 81.7 106.9 98.5 116 220.4 301.5
union_ip 30.8 59.9 89.4 79.4 100.7 194.9 286.1
clip 52 77.1 104.3 96.6 118.2 222.1 302.4
colliderect 38.4 64.6 92 82.3 112.2 203 293.8
fit 50.6 77.6 103.4 94.5 116.9 218.1 298.6
clamp 52 82.3 103.9 101.6 122.3 224.1 300.8
clamp_ip 34.2 60.6 88.5 80.5 106.9 197.5 286.4

Code

import timeit
from statistics import fmean

from pygame.rect import Rect, FRect
from tabulate import tabulate


def create_table(title: str, *headers, data: list):
    table_headers = [title, *headers]
    table = []
    for row in data:
        table.append(row)
    print(tabulate(table, headers=table_headers, tablefmt="github", numalign="left",
                   stralign="left"))


NUMBER = 1_000_000

r1 = FRect(799, 799, 10, 10)
r2 = FRect(0, 0, 5, 5)


class RectAttr:
    def __init__(self, x, y, w, h):
        self.rect = FRect(x, y, w, h)


r3 = RectAttr(799, 799, 10, 10)


class RectMethod:
    def __init__(self, x, y, w, h):
        self.p = FRect(x, y, w, h)

    def rect(self):
        return self.p


r4 = RectMethod(799, 799, 10, 10)

rt = FRect(10, 10, 2, 2)

GLOB = globals()


def test(name: str, funcs: list[str], data_table) -> None:
    t = [name]
    for func in funcs:
        val = fmean(timeit.repeat(func, globals=GLOB, number=NUMBER))
        t.append(round(val*1000, 1))
    data_table.append(t)
    print(f"{name} done")


data = []

test("instantiation",
     [
         "FRect(rt)",
         "FRect(10.0, 10.0, 2.0, 2.0)",
         "FRect((10.0, 10.0, 2.0, 2.0))",
         "FRect((10.0, 10.0), (2.0, 2.0))",
         "FRect(((10.0, 10.0), (2.0, 2.0)))",
         "FRect(r3)",
         "FRect(r4)",
     ],
     data
     )
test("update",
     [
         "r1.update(rt)",
         "r1.update(10.0, 10.0, 2.0, 2.0)",
         "r1.update((10.0, 10.0, 2.0, 2.0))",
         "r1.update((10.0, 10.0), (2.0, 2.0))",
         "r1.update(((10.0, 10.0), (2.0, 2.0)))",
         "r1.update(r3)",
         "r1.update(r4)",
     ],
     data
     )
test("union",
     [
         "r1.union(rt)",
         "r1.union(10.0, 10.0, 2.0, 2.0)",
         "r1.union((10.0, 10.0, 2.0, 2.0))",
         "r1.union((10.0, 10.0), (2.0, 2.0))",
         "r1.union(((10.0, 10.0), (2.0, 2.0)))",
         "r1.union(r3)",
         "r1.union(r4)",
     ],
     data
     )
test("union_ip",
     [
         "r2.union_ip(rt)",
         "r2.union_ip(10.0, 10.0, 2.0, 2.0)",
         "r2.union_ip((10.0, 10.0, 2.0, 2.0))",
         "r2.union_ip((10.0, 10.0), (2.0, 2.0))",
         "r2.union_ip(((10.0, 10.0), (2.0, 2.0)))",
         "r2.union_ip(r3)",
         "r2.union_ip(r4)",
     ],
     data
     )
test("clip",
     [
         "r1.clip(rt)",
         "r1.clip(10.0, 10.0, 2.0, 2.0)",
         "r1.clip((10.0, 10.0, 2.0, 2.0))",
         "r1.clip((10.0, 10.0), (2.0, 2.0))",
         "r1.clip(((10.0, 10.0), (2.0, 2.0)))",
         "r1.clip(r3)",
         "r1.clip(r4)",
     ],
     data
     )
test("colliderect",
     [
         "r1.colliderect(rt)",
         "r1.colliderect(10.0, 10.0, 2.0, 2.0)",
         "r1.colliderect((10.0, 10.0, 2.0, 2.0))",
         "r1.colliderect((10.0, 10.0), (2.0, 2.0))",
         "r1.colliderect(((10.0, 10.0), (2.0, 2.0)))",
         "r1.colliderect(r3)",
         "r1.colliderect(r4)",
     ],
     data
     )
test("fit",
     [
         "r1.fit(rt)",
         "r1.fit(10.0, 10.0, 2.0, 2.0)",
         "r1.fit((10.0, 10.0, 2.0, 2.0))",
         "r1.fit((10.0, 10.0), (2.0, 2.0))",
         "r1.fit(((10.0, 10.0), (2.0, 2.0)))",
         "r1.fit(r3)",
         "r1.fit(r4)",
     ],
     data
     )
test("clamp",
     [
         "r1.clamp(rt)",
         "r1.clamp(10.0, 10.0, 2.0, 2.0)",
         "r1.clamp((10.0, 10.0, 2.0, 2.0))",
         "r1.clamp((10.0, 10.0), (2.0, 2.0))",
         "r1.clamp(((10.0, 10.0), (2.0, 2.0)))",
         "r1.clamp(r3)",
         "r1.clamp(r4)",
     ],
     data
     )
test("clamp_ip",
     [
         "r2.clamp_ip(rt)",
         "r2.clamp_ip(10.0, 10.0, 2.0, 2.0)",
         "r2.clamp_ip((10.0, 10.0, 2.0, 2.0))",
         "r2.clamp_ip((10.0, 10.0), (2.0, 2.0))",
         "r2.clamp_ip(((10.0, 10.0), (2.0, 2.0)))",
         "r2.clamp_ip(r3)",
         "r2.clamp_ip(r4)",
     ],
     data
     )

create_table(
    "FRect (ms)", "r", "4a", "1a", "2a", "1a 2s", "r attr", "r meth",
    data=data
)

@itzpr3d4t0r itzpr3d4t0r added Performance Related to the speed or resource usage of the project rect pygame.rect labels Mar 22, 2023
@itzpr3d4t0r itzpr3d4t0r requested a review from a team as a code owner March 22, 2023 14:34
@MyreMylar
Copy link
Member

Some conflicts to resolve here.

Copy link
Contributor

@dr0id dr0id left a comment

Choose a reason for hiding this comment

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

LGTM

Copy link
Member

@MyreMylar MyreMylar left a comment

Choose a reason for hiding this comment

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

LGTM 👍

I see the same speed changes on my PC.

@MyreMylar MyreMylar added this to the 2.4.0 milestone Sep 24, 2023
@MyreMylar MyreMylar merged commit 33a889a into pygame-community:main Sep 24, 2023
32 checks passed
@itzpr3d4t0r itzpr3d4t0r deleted the fastcall_rect_methods branch October 5, 2023 07:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Performance Related to the speed or resource usage of the project rect pygame.rect
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants