Skip to content

Commit c16bd67

Browse files
committed
Use julia-repl for REPL blocks
1 parent 5d5eecc commit c16bd67

File tree

1 file changed

+27
-25
lines changed

1 file changed

+27
-25
lines changed

blog/2020/05/invalidations.md

Lines changed: 27 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ If you call `applyf([100, 200])`, it will compile a version of `applyf`
4242
knowing that only `f(::Int)` will be used: the call will be "hard-wired" into the code that Julia produces.
4343
You can see this using `@code_typed`:
4444

45-
```
45+
```julia-repl
4646
julia> @code_typed applyf([100,200])
4747
CodeInfo(
4848
1 ─ Base.arrayref(true, container, 1)::Int64
@@ -58,7 +58,7 @@ call `applyf([100])`.)
5858

5959
If you pass a `Vector{Bool}`, it will compile `applyf` again, this time specializing it for `x::Bool`:
6060

61-
```
61+
```julia-repl
6262
julia> @code_typed applyf([true,false])
6363
CodeInfo(
6464
1 ─ Base.arrayref(true, container, 1)::Bool
@@ -77,7 +77,8 @@ You don't normally see these, but Julia manages them for you; anytime you write
7777
code that calls `applyf`, it checks to see if this previous compilation work can be reused.
7878

7979
For the purpose of this blog post, things start to get especially interesting if we try the following:
80-
```
80+
81+
```julia-repl
8182
julia> c = Any[1, false];
8283
8384
julia> applyf(c)
@@ -158,7 +159,7 @@ f(::Missing) = 5
158159

159160
then Julia produces
160161

161-
```
162+
```julia-repl
162163
julia> @code_typed applyf(c)
163164
CodeInfo(
164165
1 ─ %1 = Base.arrayref(true, container, 1)::Any
@@ -183,7 +184,7 @@ But the performance benefits of such optimizations are so large that, when appli
183184
For example, if you start a fresh Julia session and just define the `f(::Int)`
184185
and `f(::Bool)` methods, then
185186

186-
```
187+
```julia-repl
187188
julia> using BenchmarkTools
188189
189190
julia> @btime applyf($c)
@@ -245,7 +246,7 @@ The next time you want to call functionality that gets invalidated,
245246
you have to wait for recompilation.
246247
We can illustrate this using everyone's favorite example, plotting:
247248

248-
```
249+
```julia-repl
249250
julia> using Plots
250251
251252
julia> @time display(plot(rand(5)))
@@ -254,14 +255,14 @@ julia> @time display(plot(rand(5)))
254255

255256
As is well known, it's much faster the second time, because it's already compiled:
256257

257-
```
258+
```julia-repl
258259
julia> @time display(plot(rand(5)))
259260
0.311226 seconds (19.93 k allocations: 775.055 KiB)
260261
```
261262

262263
Moreover, if you decide you want some additional functionality and decide to load a new package, sometimes it's essentially as fast again:
263264

264-
```
265+
```julia-repl
265266
julia> using StaticArrays
266267
267268
julia> @time display(plot(rand(5)))
@@ -270,7 +271,7 @@ julia> @time display(plot(rand(5)))
270271

271272
But if you load a package that does a lot of invalidation:
272273

273-
```
274+
```julia-repl
274275
julia> using SIMD
275276
276277
julia> @time display(plot(rand(5)))
@@ -332,7 +333,8 @@ using SnoopCompile
332333
```
333334

334335
Then,
335-
```
336+
337+
```julia-repl
336338
julia> invalidation_trees(@snoopr f(x::String) = 3)
337339
1-element Array{SnoopCompile.MethodInvalidations,1}:
338340
insert f(x::String) in Main at REPL[7]:1 invalidated:
@@ -358,7 +360,7 @@ In some cases, though, you might really need to call `applyf` with a `Vector{Any
358360

359361
Now let's try a real-world case, where the outcomes are more complex.
360362

361-
```
363+
```julia-repl
362364
julia> trees = invalidation_trees(@snoopr using FixedPointNumbers)
363365
5-element Array{SnoopCompile.MethodInvalidations,1}:
364366
insert promote_rule(::Type{T}, ::Type{Tf}) where {T<:Normed, Tf<:AbstractFloat} in FixedPointNumbers at /home/tim/.julia/packages/FixedPointNumbers/w2pxG/src/normed.jl:310 invalidated:
@@ -396,14 +398,14 @@ One does not have to look at this list for very long to see that the majority of
396398
Consider the line `backedges: MethodInstance for (::Type{T} where T<:AbstractChar)(::Int32) triggered...`.
397399
We can see which method this is by the following:
398400

399-
```
401+
```julia-repl
400402
julia> which(Char, (Int32,))
401403
(::Type{T})(x::Number) where T<:AbstractChar in Base at char.jl:48
402404
```
403405

404406
or directly as
405407

406-
```
408+
```julia-repl
407409
julia> tree = trees[end]
408410
insert (::Type{X})(x::Real) where X<:FixedPoint in FixedPointNumbers at /home/tim/.julia/packages/FixedPointNumbers/w2pxG/src/FixedPointNumbers.jl:51 invalidated:
409411
mt_backedges: signature Tuple{Type{T} where T<:Int64,Int64} triggered MethodInstance for convert(::Type{T}, ::Int64) where T<:Int64 (1 children) ambiguous
@@ -426,7 +428,7 @@ We'll see how to work with `InstanceTree`s in a moment, for now we want to focus
426428
You may find it surprising that this method signature is ambiguous with `(::Type{X})(x::Real) where X<:FixedPoint`: after all, an `AbstractChar` is quite different from a `FixedPoint` number.
427429
We can discover why with
428430

429-
```
431+
```julia-repl
430432
julia> tree.method.sig
431433
Tuple{Type{X},Real} where X<:FixedPoint
432434
@@ -441,7 +443,7 @@ These two signatures have non-empty intersection.
441443
The second parameter, `Int32`, makes sense as the intersection of `Int32` and `Real`.
442444
The first arises from
443445

444-
```
446+
```julia-repl
445447
julia> typeintersect(Type{<:FixedPoint}, Type{<:AbstractChar})
446448
Type{Union{}}
447449
```
@@ -454,7 +456,7 @@ Consequently, we can turn our attention to other cases.
454456

455457
Let's look at the next item up the list:
456458

457-
```
459+
```julia-repl
458460
julia> tree = trees[end-1]
459461
insert reduce_empty(::typeof(Base.mul_prod), ::Type{F}) where F<:FixedPoint in FixedPointNumbers at /home/tim/.julia/packages/FixedPointNumbers/w2pxG/src/FixedPointNumbers.jl:225 invalidated:
460462
backedges: MethodInstance for reduce_empty(::Function, ::Type{T} where T) triggered MethodInstance for reduce_empty(::Base.BottomRF{typeof(max)}, ::Type{VersionNumber}) (136 children) more specific
@@ -475,7 +477,7 @@ It is not clear why such methods should be invalidating, and this may be a Julia
475477

476478
If you try
477479

478-
```
480+
```julia-repl
479481
julia> trees = invalidation_trees(@snoopr using StaticArrays)
480482
```
481483

@@ -493,7 +495,7 @@ use the default method.
493495
The vast majority of the rest appear to derive from ambiguities.
494496
However, one more interesting case we've not seen before is
495497

496-
```
498+
```julia-repl
497499
julia> tree = trees[end-7]
498500
insert unsafe_convert(::Type{Ptr{T}}, m::Base.RefValue{FA}) where {N, T, D, FA<:FieldArray{N,T,D}} in StaticArrays at /home/tim/.julia/packages/StaticArrays/mlIi1/src/FieldArray.jl:124 invalidated:
499501
mt_backedges: signature Tuple{typeof(Base.unsafe_convert),Type{Ptr{_A}} where _A,Base.RefValue{_A} where _A} triggered MethodInstance for unsafe_convert(::Type{Ptr{Nothing}}, ::Base.RefValue{_A} where _A) (159 children) more specific
@@ -504,7 +506,7 @@ has been only partially specified: it depends on a type parameter `_A`.
504506
Where does such a signature come from?
505507
You can extract this line with
506508

507-
```
509+
```julia-repl
508510
julia> trigger = tree[:mt_backedges, 1]
509511
MethodInstance for unsafe_convert(::Type{Ptr{Nothing}}, ::Base.RefValue{_A} where _A) at depth 0 with 159 children
510512
@@ -608,7 +610,7 @@ Replacing `keys(dct)` with `Base.KeySet(dct)` (which is the default consequence
608610
Let's return to our FixedPointNumbers `reduce_empty` example above.
609611
A little prodding as done above reveals that this corresponds to the definition
610612

611-
```
613+
```julia-repl
612614
julia> mi, invtree = tree[:backedges, 1]
613615
MethodInstance for reduce_empty(::Function, ::Type{T} where T) => MethodInstance for reduce_empty(::Base.BottomRF{typeof(max)}, ::Type{VersionNumber}) at depth 0 with 136 children
614616
@@ -627,7 +629,7 @@ reduce_empty(op, T) = _empty_reduce_error()
627629

628630
which indicates that it is the fallback method for reducing over an empty collection, and indeed calling this results in an error:
629631

630-
```
632+
```julia-repl
631633
julia> op = Base.BottomRF(Base.max)
632634
Base.BottomRF{typeof(max)}(max)
633635
@@ -652,7 +654,7 @@ reduce_empty(::Base.BottomRF{typeof(max)}, ::Type{VersionNumber}) = _empty_reduc
652654
so that we get the same result but don't rely on the fallback.
653655
But perhaps a better approach is to see who's calling it:
654656

655-
```
657+
```julia-repl
656658
julia> invtree.mi
657659
MethodInstance for reduce_empty(::Base.BottomRF{typeof(max)}, ::Type{VersionNumber})
658660
@@ -671,14 +673,14 @@ julia> invtree.children
671673
This illustrates how to work with an `InstanceTree`: you access the MethodInstance through `.mi` and its callers through `.children`.
672674
Let's start with the first one:
673675

674-
```
676+
```julia-repl
675677
julia> node = invtree.children[1]
676678
MethodInstance for reduce_empty_iter(::Base.BottomRF{typeof(max)}, ::Set{VersionNumber}, ::Base.HasEltype) at depth 1 with 38 children
677679
```
678680

679681
We can display the whole tree using `show(node)`:
680682

681-
```
683+
```julia-repl
682684
julia> show(node)
683685
MethodInstance for reduce_empty_iter(::Base.BottomRF{typeof(max)}, ::Set{VersionNumber}, ::Base.HasEltype)
684686
MethodInstance for reduce_empty_iter(::Base.BottomRF{typeof(max)}, ::Set{VersionNumber})
@@ -733,7 +735,7 @@ Adding this to `Pkg` would be type-piracy, since `max`, `BottomRF`, and `Version
733735
But there are other possible fixes.
734736
A little higher up the tree we see a call to `mapreduce`, and this presents another opportunity because `mapreduce` allows you to supply an `init` value:
735737

736-
```
738+
```julia-repl
737739
julia> mapreduce(identity, max, Set(VersionNumber[]); init=VersionNumber(0))
738740
v"0.0.0"
739741
```

0 commit comments

Comments
 (0)