Skip to content

Precompile displaying N0f8, which also accelerates the other types - #352

Open
PatrickHaecker wants to merge 3 commits into
JuliaMath:masterfrom
PatrickHaecker:precompile-display
Open

PatrickHaecker wants to merge 3 commits into
JuliaMath:masterfrom
PatrickHaecker:precompile-display

Conversation

@PatrickHaecker

@PatrickHaecker PatrickHaecker commented Oct 10, 2026 •

Copy link
Copy Markdown
Collaborator

Showing a value or a vector for the first time was slow. Precompiling show and print for the terminal and for buffers cuts the first display in the terminal from 302 to 82 ms on Julia 1.10 and from 913 to 88 ms on Julia 1.13, and the first repr from 438 to 113 ms and from 553 to 107 ms. Loading becomes 8 ms (1.10) and 2 ms (1.13) slower, and the package image grows by about 1 MiB. Invalidations do not change, neither from loading the package nor from loading ColorTypes and Colors afterwards.

@codecov

codecov Bot commented Oct 10, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 96.78%. Comparing base (d62abf3) to head (d7c9e8b).

Additional details and impacted files
@@            Coverage Diff             @@
##           master     #352      +/-   ##
==========================================
+ Coverage   96.75%   96.78%   +0.03%     
==========================================
  Files           7        7              
  Lines         800      808       +8     
==========================================
+ Hits          774      782       +8     
  Misses         26       26              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@kimikage

Copy link
Copy Markdown
Collaborator

Personally, I believe this is worthwhile.
However, opinions vary regarding the trade-offs involved in TTFX.
If there are no objections after some time has passed, I'll merge this PR.

@kimikage

Copy link
Copy Markdown
Collaborator

BTW, I am considering registering the TTFX benchmark snippet for Colors.jl.
JuliaGraphics/Colors.jl#590

Displaying RGB{N0f8} should be in the snippet.

@PatrickHaecker

Copy link
Copy Markdown
Collaborator Author

BTW, I am considering registering the TTFX benchmark snippet for Colors.jl. JuliaGraphics/Colors.jl#590

Displaying RGB{N0f8} should be in the snippet.

I am not sure whether I understood what you have asked for here. FixedPointNumbers.jl can't precompile RGB{N0f8}, because Colors.jl depends on FixedPointNumbers.jl, not the other way round. So Colors.jl needs to precompile RGB{N0f8}.

However, I let the agent check whether we can do something to accelerate the use case and indeed we can. Turns out that precompiling Base.PipeEndpoint can also be useful for types like this.


🤖:
I measured your draft from JuliaGraphics/Colors.jl#590, with a REPL-style display of an RGB{N0f8} and a Vector{RGB{N0f8}} added (ColorTypes 0.12.4, Colors 0.13.3, fastest of 5 fresh processes, in ms):

first display of RGB{N0f8} stdout master 7f91b71 (TTY + buffers) 9b952ff (+ pipes)
REPL-style show, Julia 1.13 pipe 2488 1906 428
REPL-style show, Julia 1.10 pipe 1376 1342 414
println(collect(p_max)), Julia 1.13 pipe 852 784 362

julia-ttfx-check runs the workload with stdout going to a pipe, so the first commit alone would not have shown up in such a benchmark. The second commit adds IOContext{Base.PipeEndpoint}, which also covers scripts whose output is piped, as in CI. The package image grows by another 0.6 MiB, and the load time changes by less than the noise. Writing to a file needs no directive, since it is already fast on master.

RGB{N0f8} benefits because the directives for N0f8 and Vector{N0f8} compile the array printing code for these IO types, and RGB{N0f8} display uses the same code.


👨:
I added precompiling Base.PipeEndpoint to the PR.

@kimikage

Copy link
Copy Markdown
Collaborator

I feel like N0f16 is used far less often than N0f8.
At least when it comes to display, execution performance isn't necessary, so I think N0f8 alone is sufficient.

While I'm opposed to targeting pipes solely for CI purposes, the reality is that many people use a variety of tools for coding. So, it's worth considering that end-users may (indirectly) use pipes.
On the other hand, there is the question of whether it is even worth specializing IO to such an extent in the first place.
This is an issue with the downstream packages rather than with this package itself.

Patrick Häcker added 3 commits October 18, 2026 04:22
Showing a value or a vector for the first time was the slowest first use
of the package. Precompiling `show` and `print` for the terminal and for
buffers cuts the first display in the terminal from 302 to 82 ms on
Julia 1.10 and from 913 to 88 ms on Julia 1.13, and the first `repr`
from 438 to 113 ms and from 553 to 107 ms. Loading becomes 8 ms (1.10)
and 2 ms (1.13) slower, and the package image grows by about 1 MiB.
Invalidations do not change, neither from loading the package nor from
loading ColorTypes and Colors afterwards.
Scripts whose output is piped, such as CI jobs and TTFX benchmarks, print
through `IOContext{Base.PipeEndpoint}`. Without these directives, the first
display of an `RGB{N0f8}` vector takes about 2 s on Julia 1.13 and 1.4 s on
1.10, with them about 0.4 s.
It turns out that compiling only `N0f8` covers enough generic code so that the
latency of the other methods for the other types is acceptable on typical REPL
usage. A one-time delay of half a second is okay on the REPL. A delay of 2.5
seconds feels sluggish.

Within package usage, the packages can and should precompile for their
specific use cases.

----
:robot:
| | master | previous PR | current PR |
|---|---:|---:|---:|
| image (KiB) | 2653 | 4016 | 3277 |
| display `N0f8` | 2511 | 274 | 261 |
| display `N0f16` | 2144 | 196 | 444 |
| display `Q0f7` | 2510 | 473 | 485 |
| display `Q0f15` | 2500 | 607 | 537 |
| core `N0f16` | 155 | 106 | 207 |
| core `Q0f7` | 363 | 271 | 257 |
| core `Q0f15` | 327 | 220 | 275 |

Julia 1.13.1, time in ms of the first use in a fresh process, fastest of 5, pinned to one core. "display" is the first `show` of a value and of a 2-element vector plus `print`, into a terminal (pipes give the same numbers within noise). "core" is the first arithmetic, rounding, conversion and `rand`.
@PatrickHaecker

Copy link
Copy Markdown
Collaborator Author

I feel like N0f16 is used far less often than N0f8.
At least when it comes to display, execution performance isn't necessary, so I think N0f8 alone is sufficient.

It turns out that compiling only N0f8 covers enough generic code so that the
latency of the methods for the other types is acceptable on typical REPL
usage. A one-time delay of half a second is okay on the REPL. A delay of 2.5
seconds feels sluggish.

Within package usage, the packages can and should precompile for their
specific use cases.

So removing N0f16 turned out to be indeed the best compromise found so far. I changed it accordingly.


🤖:

master previous PR current PR
image (KiB) 2653 4016 3277
display N0f8 2511 274 261
display N0f16 2144 196 444
display Q0f7 2510 473 485
display Q0f15 2500 607 537
core N0f16 155 106 207
core Q0f7 363 271 257
core Q0f15 327 220 275

Julia 1.13.1, time in ms of the first use in a fresh process, fastest of 5, pinned to one core. "display" is the first show of a value and of a 2-element vector plus print, into a terminal (pipes give the same numbers within noise). "core" is the first arithmetic, rounding, conversion and rand.

@kimikage

Copy link
Copy Markdown
Collaborator

Although the policy hasn't been finalized yet, we'll at least need to review the PR title before merging.

@PatrickHaecker PatrickHaecker changed the title Precompile displaying N0f8 and N0f16 Precompile displaying N0f8 Oct 18, 2026
@PatrickHaecker PatrickHaecker changed the title Precompile displaying N0f8 Precompile displaying N0f8, which also accelerates the other types Oct 18, 2026
@PatrickHaecker

Copy link
Copy Markdown
Collaborator Author

Good point. I changed the PR title.

Ready to merge now, or what kind of policy needs to be finalized? Your last suggestion, which is implemented in this PR, seems to be the best compromise we have found so far. If we find a better one in the future, we can do another PR.

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.

2 participants