picogame: enable on RP2040 - #11358
lynt-smitka wants to merge 4 commits into
Conversation
Neither fits at -O3: pico has 48 KB free of its 1020 KB partition and pico_w 2 KB of its 1536 KB, while the engine needs 50 KB. At -O2 with the loop passes both keep around 140 KB free, within 1% of -O3 on this M0+.
They were set per board. Every RP2040 build has the same reason to use them, so they belong next to the other RP2040 defaults, with a note on what each pass does and why RP2350 keeps -O3.
dhalbert
left a comment
There was a problem hiding this comment.
This is good; I would just like some comment tweaks.
|
I'll rework it a bit, including adding better comments. I ran the tests for each flag separately and I have to interpret the results (the vectorization need few more tests). I hope it will be done in few hours. |
|
I meant to have this enabled for all of RP2040 -- sorry I was talking about optimization flags only. |
|
The new recommendation after heavy benchmarking individual flags:
AI summarized benchmark results: 1. Each flag on its own
Two flags do the work, and they do not overlap.
The interpreter does not move under any flag. 2. Combinations
Two flags reach Dropping 3. Outside picogame
The gains are small but they are not noise and they are not picogame: 2.3 % on alpha blending, 4. What the two flags do
It did two different things to the two calls that got faster, and only one of them is unswitching in In mode7 it is textbook. The In the blit it is not. The specialization on the flip and transpose flags already exists at This also explains a column that never moves. The unflipped blit stays at 59 to 62 microseconds in
5. What the cost model costsIt is a trade, not a free win. The one call that gets slower is
So The reason is the unrolled The same trade shows on big integers: 6. The eight
|
| added on top of the five | flash | fill_rect | fill_circle | hot functions |
|---|---|---|---|---|
-fipa-cp-clone |
+11280 | 1168 | 325 | byte-identical |
-fpeel-loops |
+24052 | 1168 | 325 | byte-identical |
-floop-unroll-and-jam |
+0 | 1177 | 332 | byte-identical |
-fsplit-loops |
+3296 | 1162 | 322 | byte-identical |
-ftree-loop-distribution |
-40 | 1176 | 332 | byte-identical |
-funroll-completely-grow-size |
does not link | |||
-fversion-loops-for-strides |
+5904 | 1162 | 322 | rewrites blit and mode7 |
-floop-interchange |
+0 | 1163 | 332 | byte-identical |
Six of the seven that link leave the drawing code untouched. -fversion-loops-for-strides is the
exception: it rewrites 985 instructions in the blit and 214 in mode7 and is still no faster.
Together with the inline parameters these are the 130 KB between the set and -O3.
The five-pass set was 16-37 % behind -O3 on rectangle fills, memory copies and aesio, because what recovers those is the vectorizer cost model, not the loop passes. Adding -fvect-cost-model=dynamic closes that gap. Four of the five passes then turn out to emit the same code as plain -O2 for the loops that matter here, so drop them. The result matches -O3 on every benchmark for 130 KB less flash, and is 2.5 KB smaller than the five-pass set it replaces.
The port now sets the same flags for every RP2040, and this board's copy assigned with = rather than ?=, so it would pin the old five-pass set and be the one board that misses the port default.
| # -O2 plus the two flags that carry -O3 here, which is 130 KB less flash than -O3 for the same | ||
| # speed on everything measured. That headroom is what lets a board fit. | ||
| # | ||
| # -funswitch-loops Hoists a test that cannot change inside a loop out of it and keeps | ||
| # one copy of the body per value. Costs 16 KB, because it duplicates | ||
| # loop bodies in 94 functions. Buys 24 % on a flipped sprite blit, | ||
| # 9 % on a textured floor, and 1-2 % on bitmaptools.alphablend and | ||
| # the ulab reductions. | ||
| # | ||
| # -fvect-cost-model=dynamic -O2 sets very-cheap, which turns nearly every loop down. dynamic | ||
| # accepts them, which here means memset, memcpy and a 16-bit row | ||
| # fill get unrolled with an alignment prologue. Costs 4 KB. Buys | ||
| # 37 % on rectangle fills and bytearray copies and 16 % on aesio. | ||
| # Runs shorter than the prologue lose: a four-pixel span fill is | ||
| # 7 % slower. | ||
| # | ||
| # The four other -O3 loop passes we measured (-fpredictive-commoning, -fgcse-after-reload, | ||
| # -ftree-partial-pre, -fsplit-paths) emit the same code as -O2 for the loops that matter here, | ||
| # so they are left out. So is the rest of -O3: its inline parameters alone are 92 KB, and they | ||
| # also take the 60-factorial loop 56 % slower. | ||
| # | ||
| # Neither flag reaches the bytecode dispatch loop: py/py.mk builds gc.o and vm.o at -O3 whatever | ||
| # this is set to, so pure interpreter work measures the same under all of them. | ||
| # | ||
| # RP2350 keeps plain -O3. There an -O2 set runs 24-41 % slower on fill loops. |
There was a problem hiding this comment.
This analysis should be in the PR or commit message.
Enables picogame on raspberry_pi_pico and raspberry_pi_pico_w.
Neither fits at the port default -O3: pico has 48 KB free of its 1020 KB partition and pico_w 2 KB of its 1536 KB, while the engine needs 50 KB. Both build at -O2, which leaves around 140 KB free and measures within 1% of -O3 across the render kernels on this M0+.
pico_w sits at 99.9% today, so it builds all 17 translations on every pull request; with the flags it skips them like the other boards.