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
+35-16Lines changed: 35 additions & 16 deletions
Original file line number
Diff line number
Diff line change
@@ -415,7 +415,7 @@ insert (::Type{X})(x::Real) where X<:FixedPoint in FixedPointNumbers at /home/ti
415
415
6 mt_cache
416
416
417
417
418
-
julia> mi, invtree = tree[:backedges,1]
418
+
julia> mi, node = tree[:backedges,1]
419
419
MethodInstance for (::Type{T} where T<:AbstractChar)(::Int32) => MethodInstance for +(::AbstractChar, ::UInt8) at depth 0 with 157 children
420
420
421
421
julia> mi.def
@@ -465,7 +465,21 @@ insert reduce_empty(::typeof(Base.mul_prod), ::Type{F}) where F<:FixedPoint in F
465
465
466
466
`reduce_empty(::typeof(Base.mul_prod), ::Type{F}) where F<:FixedPoint` is strictly more specific than `reduce_empty(::Function, ::Type{T} where T)`.
467
467
This might look like one of those "necessary" invalidations.
468
-
Below we'll analyze this in far greater detail and discover some possible fixes.
468
+
However, even though it's marked "more specific," the new method `reduce_empty(::typeof(Base.add_sum), ::Type{F}) where F<:FixedPoint` can't be reached from a call `reduce_empty(::Base.BottomRF{typeof(max)}, ::Type{VersionNumber})`:
469
+
470
+
```julia-repl
471
+
julia> mi, node = tree[:backedges, 1]
472
+
MethodInstance for reduce_empty(::Function, ::Type{T} where T) => MethodInstance for reduce_empty(::Base.BottomRF{typeof(max)}, ::Type{VersionNumber}) at depth 0 with 143 children
473
+
474
+
julia> node.mi # this is the MethodInstance that called `mi`
475
+
MethodInstance for reduce_empty(::Base.BottomRF{typeof(max)}, ::Type{VersionNumber})
This is is a consequence of the fact that Julia's compiler has chosen not to specialize the argument types, and `reduce_empty(::Function, ::Type{T} where T)` is broader than what could be determined from the caller.
482
+
Thus, while it looks like a case of greater specificity, in fact this is more analogous to the "partial specialization" described below.
469
483
470
484
Moving backward another step, we get to `sizeof(::Type{X}) where X<:FixedPoint`.
471
485
Simply put, this looks like a method that we don't need; perhaps it dates from some confusion, or an era where perhaps it was necessary.
@@ -613,7 +627,7 @@ Let's return to our FixedPointNumbers `reduce_empty` example above.
613
627
A little prodding as done above reveals that this corresponds to the definition
614
628
615
629
```julia-repl
616
-
julia> mi, invtree = tree[:backedges, 1]
630
+
julia> mi, node = tree[:backedges, 1]
617
631
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
618
632
619
633
julia> mi
@@ -635,35 +649,40 @@ which indicates that it is the fallback method for reducing over an empty collec
635
649
julia> op = Base.BottomRF(Base.max)
636
650
Base.BottomRF{typeof(max)}(max)
637
651
638
-
julia> Base.reduce_empty(op, VERSION)
652
+
julia> Base.reduce_empty(op, VersionNumber)
639
653
ERROR: ArgumentError: reducing over an empty collection is not allowed
640
654
Stacktrace:
641
655
[1] _empty_reduce_error() at ./reduce.jl:299
642
-
[2] reduce_empty(::Function, ::VersionNumber) at ./reduce.jl:309
643
-
[3] reduce_empty(::Base.BottomRF{typeof(max)}, ::VersionNumber) at ./reduce.jl:324
644
-
[4] top-level scope at REPL[36]:1
656
+
[2] reduce_empty(::Function, ::Type{T} where T) at ./reduce.jl:309
657
+
[3] reduce_empty(::Base.BottomRF{typeof(max)}, ::Type{T} where T) at ./reduce.jl:324
658
+
[4] top-level scope at REPL[2]:1
645
659
```
646
660
647
661
This essentially means that no "neutral element" has been defined for this operation and type.
648
662
649
-
Can we avoid this fallback?
663
+
For the purposes of illustration, let's ignore the fact that this might be a case where the fix might in principle be made in the compiler.
664
+
Using ordinary Julia code, can we avoid this fallback?
650
665
One approach is to define the method directly: modify Julia to add
so that we get the same result but don't rely on the fallback.
657
-
But perhaps a better approach is to see who's calling it:
658
672
659
-
```julia-repl
660
-
julia> invtree.mi
661
-
MethodInstance for reduce_empty(::Base.BottomRF{typeof(max)}, ::Type{VersionNumber})
673
+
Given our observation above that this *apparent* invalidating method is not actually reachable by this call chain,
674
+
another approach is to force the compiler to specialize the method by adding type parameters:
662
675
663
-
julia> invtree.mi.def
664
-
reduce_empty(op::Base.BottomRF, T) in Base at reduce.jl:324
676
+
```
677
+
reduce_empty(op::F, ::Type{T}) where {F,T} = _empty_reduce_error()
678
+
```
665
679
666
-
julia> invtree.children
680
+
While there's little actual reason to force specialization on a method that just issues an error, in this case it does have the effect of allowing the compiler to realize that our new method is not reachable from this call path.
681
+
682
+
For addressing this purely at the level of Julia code, perhaps the best approach is to see who's calling it:
683
+
684
+
```julia-repl
685
+
julia> node.children
667
686
5-element Array{SnoopCompile.InstanceTree,1}:
668
687
MethodInstance for reduce_empty_iter(::Base.BottomRF{typeof(max)}, ::Set{VersionNumber}, ::Base.HasEltype) at depth 1 with 38 children
669
688
MethodInstance for reduce_empty(::Base.BottomRF{typeof(max)}, ::Type{Int64}) at depth 1 with 39 children
@@ -676,7 +695,7 @@ This illustrates how to work with an `InstanceTree`: you access the MethodInstan
676
695
Let's start with the first one:
677
696
678
697
```julia-repl
679
-
julia> node = invtree.children[1]
698
+
julia> node = node.children[1]
680
699
MethodInstance for reduce_empty_iter(::Base.BottomRF{typeof(max)}, ::Set{VersionNumber}, ::Base.HasEltype) at depth 1 with 38 children
0 commit comments