You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: blog/2020/05/invalidations.md
+18-6Lines changed: 18 additions & 6 deletions
Original file line number
Diff line number
Diff line change
@@ -39,7 +39,15 @@ function applyf(container)
39
39
end
40
40
```
41
41
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
43
51
knowing that only `f(::Int)` will be used: the call will be "hard-wired" into the code that Julia produces.
44
52
You can see this using `@code_typed`:
45
53
@@ -57,7 +65,7 @@ has elements indexable by 1 and 2. (Those `arrayref` statements enforce
57
65
bounds-checking, and ensure that Julia will throw an appropriate error if you
58
66
call `applyf([100])`.)
59
67
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:
61
69
62
70
```julia-repl
63
71
julia> @code_typed applyf([true,false])
@@ -70,7 +78,7 @@ CodeInfo(
70
78
71
79
In this case, you can see that Julia knew those two `arrayref` statements would
72
80
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.
74
82
75
83
At the end of these experiments, hidden away in Julia's "method cache" there will
76
84
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
143
151
144
152
and Julia has prepared the way to make sure it will still give the right answer even if we add new methods to `f`.
145
153
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:
147
155
Julia has gone to the trouble to create a new-and-improved implementation of `applyf`,
148
156
one which also union-splits for `String`.
149
157
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.
176
184
It doesn't even try to enforce the fact that `f` returns an `Int`,
177
185
in part because determining such facts takes time (adding to compiler latency)
178
186
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.
180
188
181
189
Compiling each of these new implementations takes JIT-time.
182
190
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
535
543
```
536
544
537
545
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;
539
547
a little digging reveals that this is defined as
540
548
541
549
```
@@ -627,6 +635,10 @@ Let's return to our FixedPointNumbers `reduce_empty` example above.
627
635
A little prodding as done above reveals that this corresponds to the definition
628
636
629
637
```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
+
630
642
julia> mi, node = tree[:backedges, 1]
631
643
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
0 commit comments