Skip to content

Commit 7357d78

Browse files
timholymbauman
andauthored
Apply suggestions from code review
Co-authored-by: Matt Bauman <mbauman@juliacomputing.com>
1 parent f0cb3a2 commit 7357d78

File tree

1 file changed

+18
-6
lines changed

1 file changed

+18
-6
lines changed

blog/2020/05/invalidations.md

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,15 @@ function applyf(container)
3939
end
4040
```
4141

42-
If you call `applyf([100, 200])`, it will compile a version of `applyf`
42+
Here I've defined two functions; `f` is a function with two very simple methods,
43+
and `arrayf` is a function with just one method that supports `Any` argument at all.
44+
When you call `applyf`, Julia will compile _specialized_ versions on demand for the
45+
particular types of `container` that you're using at that moment (even though I didn't
46+
use a single type in its definition!).
47+
48+
If you call `applyf([100, 200])`, Julia will compile and use a version of `applyf` specifically
49+
created for `Vector{Int}`. Since the element type (`Int`) is a part of the `container`'s type, it
50+
will compile this specialization
4351
knowing that only `f(::Int)` will be used: the call will be "hard-wired" into the code that Julia produces.
4452
You can see this using `@code_typed`:
4553

@@ -57,7 +65,7 @@ has elements indexable by 1 and 2. (Those `arrayref` statements enforce
5765
bounds-checking, and ensure that Julia will throw an appropriate error if you
5866
call `applyf([100])`.)
5967

60-
If you pass a `Vector{Bool}`, it will compile `applyf` again, this time specializing it for `x::Bool`:
68+
If you pass a `Vector{Bool}`, it will compile `applyf` again, this time specializing it for `Bool` elements:
6169

6270
```julia-repl
6371
julia> @code_typed applyf([true,false])
@@ -70,7 +78,7 @@ CodeInfo(
7078

7179
In this case, you can see that Julia knew those two `arrayref` statements would
7280
return a `Bool`, and since it knows the value of `f(::Bool)` it just went
73-
ahead and computed the result for you.
81+
ahead and computed the result at compile time for you.
7482

7583
At the end of these experiments, hidden away in Julia's "method cache" there will
7684
be two `MethodInstance`s of `applyf`, one specialized for `Vector{Int}` and the other specialized for `Vector{Bool}`.
@@ -143,7 +151,7 @@ f(::String) = 3
143151

144152
and Julia has prepared the way to make sure it will still give the right answer even if we add new methods to `f`.
145153

146-
However, if you try `@code_typed applyf(c)` again, you'll notice something curious:
154+
However, if you try `@code_typed applyf(Any[1, false])` again, you'll notice something curious:
147155
Julia has gone to the trouble to create a new-and-improved implementation of `applyf`,
148156
one which also union-splits for `String`.
149157
This brings us to the topic of this blog post: the old compiled method has been *invalidated*.
@@ -176,7 +184,7 @@ uses "runtime dispatch" to decide what method of `f` to call.
176184
It doesn't even try to enforce the fact that `f` returns an `Int`,
177185
in part because determining such facts takes time (adding to compiler latency)
178186
and because functions with many methods typically tend to return multiple types
179-
anyway.
187+
anyway. Adding further methods of `f` would no longer cause invalidations of this very generic implementation or any of its callers.
180188

181189
Compiling each of these new implementations takes JIT-time.
182190
If Julia knew in advance that you'd arrive at this place, it would never have bothered to produce that first, heavily-optimized version of `applyf`.
@@ -535,7 +543,7 @@ julia> trigger.children
535543
```
536544

537545
and see all the `MethodInstance`s that called this one.
538-
You'll notice three `_show_default` `MethodInstance`s here;
546+
You'll notice three `_show_default` `MethodInstance`s with the bulk of the children here;
539547
a little digging reveals that this is defined as
540548

541549
```
@@ -627,6 +635,10 @@ Let's return to our FixedPointNumbers `reduce_empty` example above.
627635
A little prodding as done above reveals that this corresponds to the definition
628636

629637
```julia-repl
638+
julia> tree = trees[end-1]
639+
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:
640+
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
641+
630642
julia> mi, node = tree[:backedges, 1]
631643
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
632644

0 commit comments

Comments
 (0)