Skip to content

Commit 84d7e67

Browse files
authored
Merge pull request #35714 from JuliaLang/teh/less_invalidation
A grab bag of latency reductions
2 parents a6e27b3 + f83a84c commit 84d7e67

File tree

9 files changed

+143
-90
lines changed

9 files changed

+143
-90
lines changed

base/abstractarray.jl

Lines changed: 70 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -803,26 +803,81 @@ end
803803
## copy between abstract arrays - generally more efficient
804804
## since a single index variable can be used.
805805

806-
copyto!(dest::AbstractArray, src::AbstractArray) =
807-
copyto!(IndexStyle(dest), dest, IndexStyle(src), src)
806+
"""
807+
copyto!(dest::AbstractArray, src) -> dest
808808
809-
function copyto!(::IndexStyle, dest::AbstractArray, ::IndexStyle, src::AbstractArray)
810-
destinds, srcinds = LinearIndices(dest), LinearIndices(src)
811-
isempty(srcinds) || (checkbounds(Bool, destinds, first(srcinds)) && checkbounds(Bool, destinds, last(srcinds))) ||
812-
throw(BoundsError(dest, srcinds))
813-
@inbounds for i in srcinds
814-
dest[i] = src[i]
815-
end
816-
return dest
809+
810+
Copy all elements from collection `src` to array `dest`, whose length must be greater than
811+
or equal to the length `n` of `src`. The first `n` elements of `dest` are overwritten,
812+
the other elements are left untouched.
813+
814+
# Examples
815+
```jldoctest
816+
julia> x = [1., 0., 3., 0., 5.];
817+
818+
julia> y = zeros(7);
819+
820+
julia> copyto!(y, x);
821+
822+
julia> y
823+
7-element Array{Float64,1}:
824+
1.0
825+
0.0
826+
3.0
827+
0.0
828+
5.0
829+
0.0
830+
0.0
831+
```
832+
"""
833+
function copyto!(dest::AbstractArray, src::AbstractArray)
834+
isempty(src) && return dest
835+
src′ = unalias(dest, src)
836+
copyto_unaliased!(IndexStyle(dest), dest, IndexStyle(src′), src′)
837+
end
838+
839+
function copyto!(deststyle::IndexStyle, dest::AbstractArray, srcstyle::IndexStyle, src::AbstractArray)
840+
isempty(src) && return dest
841+
src′ = unalias(dest, src)
842+
copyto_unaliased!(deststyle, dest, srcstyle, src′)
817843
end
818844

819-
function copyto!(::IndexStyle, dest::AbstractArray, ::IndexCartesian, src::AbstractArray)
845+
function copyto_unaliased!(deststyle::IndexStyle, dest::AbstractArray, srcstyle::IndexStyle, src::AbstractArray)
846+
isempty(src) && return dest
820847
destinds, srcinds = LinearIndices(dest), LinearIndices(src)
821-
isempty(srcinds) || (checkbounds(Bool, destinds, first(srcinds)) && checkbounds(Bool, destinds, last(srcinds))) ||
848+
idf, isf = first(destinds), first(srcinds)
849+
Δi = idf - isf
850+
(checkbounds(Bool, destinds, isf+Δi) & checkbounds(Bool, destinds, last(srcinds)+Δi)) ||
822851
throw(BoundsError(dest, srcinds))
823-
i = 0
824-
@inbounds for a in src
825-
dest[i+=1] = a
852+
if deststyle isa IndexLinear
853+
if srcstyle isa IndexLinear
854+
# Single-index implementation
855+
@inbounds for i in srcinds
856+
dest[i + Δi] = src[i]
857+
end
858+
else
859+
# Dual-index implementation
860+
i = idf - 1
861+
@inbounds for a in src
862+
dest[i+=1] = a
863+
end
864+
end
865+
else
866+
iterdest, itersrc = eachindex(dest), eachindex(src)
867+
if iterdest == itersrc
868+
# Shared-iterator implementation
869+
for I in iterdest
870+
@inbounds dest[I] = src[I]
871+
end
872+
else
873+
# Dual-iterator implementation
874+
ret = iterate(iterdest)
875+
@inbounds for a in src
876+
idx, state = ret
877+
dest[idx] = a
878+
ret = iterate(iterdest, state)
879+
end
880+
end
826881
end
827882
return dest
828883
end

base/array.jl

Lines changed: 30 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,30 @@ function unsafe_copyto!(dest::Ptr{T}, src::Ptr{T}, n) where T
249249
return dest
250250
end
251251

252+
253+
function _unsafe_copyto!(dest, doffs, src, soffs, n)
254+
destp = pointer(dest, doffs)
255+
srcp = pointer(src, soffs)
256+
@inbounds if destp < srcp || destp > srcp + n
257+
for i = 1:n
258+
if isassigned(src, soffs + i - 1)
259+
dest[doffs + i - 1] = src[soffs + i - 1]
260+
else
261+
_unsetindex!(dest, doffs + i - 1)
262+
end
263+
end
264+
else
265+
for i = n:-1:1
266+
if isassigned(src, soffs + i - 1)
267+
dest[doffs + i - 1] = src[soffs + i - 1]
268+
else
269+
_unsetindex!(dest, doffs + i - 1)
270+
end
271+
end
272+
end
273+
return dest
274+
end
275+
252276
"""
253277
unsafe_copyto!(dest::Array, do, src::Array, so, N)
254278
@@ -279,37 +303,23 @@ function unsafe_copyto!(dest::Array{T}, doffs, src::Array{T}, soffs, n) where T
279303
ccall(:jl_array_typetagdata, Ptr{UInt8}, (Any,), src) + soffs - 1,
280304
n)
281305
else
282-
# handle base-case: everything else above was just optimizations
283-
@inbounds if destp < srcp || destp > srcp + n
284-
for i = 1:n
285-
if isassigned(src, soffs + i - 1)
286-
dest[doffs + i - 1] = src[soffs + i - 1]
287-
else
288-
_unsetindex!(dest, doffs + i - 1)
289-
end
290-
end
291-
else
292-
for i = n:-1:1
293-
if isassigned(src, soffs + i - 1)
294-
dest[doffs + i - 1] = src[soffs + i - 1]
295-
else
296-
_unsetindex!(dest, doffs + i - 1)
297-
end
298-
end
299-
end
306+
_unsafe_copyto!(dest, doffs, src, soffs, n)
300307
end
301308
@_gc_preserve_end t2
302309
@_gc_preserve_end t1
303310
return dest
304311
end
305312

313+
unsafe_copyto!(dest::Array, doffs, src::Array, soffs, n) =
314+
_unsafe_copyto!(dest, doffs, src, soffs, n)
315+
306316
"""
307317
copyto!(dest, do, src, so, N)
308318
309319
Copy `N` elements from collection `src` starting at offset `so`, to array `dest` starting at
310320
offset `do`. Return `dest`.
311321
"""
312-
function copyto!(dest::Array{T}, doffs::Integer, src::Array{T}, soffs::Integer, n::Integer) where T
322+
function copyto!(dest::Array, doffs::Integer, src::Array, soffs::Integer, n::Integer)
313323
n == 0 && return dest
314324
n > 0 || _throw_argerror()
315325
if soffs < 1 || doffs < 1 || soffs+n-1 > length(src) || doffs+n-1 > length(dest)
@@ -327,7 +337,7 @@ function _throw_argerror()
327337
throw(ArgumentError("Number of elements to copy must be nonnegative."))
328338
end
329339

330-
copyto!(dest::Array{T}, src::Array{T}) where {T} = copyto!(dest, 1, src, 1, length(src))
340+
copyto!(dest::Array, src::Array) = copyto!(dest, 1, src, 1, length(src))
331341

332342
# N.B: The generic definition in multidimensional.jl covers, this, this is just here
333343
# for bootstrapping purposes.

base/dict.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,10 @@ end
378378

379379
function setindex!(h::Dict{K,V}, v0, key::K) where V where K
380380
v = convert(V, v0)
381+
setindex!(h, v, key)
382+
end
383+
384+
function setindex!(h::Dict{K,V}, v::V, key::K) where V where K
381385
index = ht_keyindex2!(h, key)
382386

383387
if index > 0

base/loading.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -715,6 +715,7 @@ function _tryrequire_from_serialized(modkey::PkgId, build_id::UInt64, modpath::U
715715
invokelatest(callback, modkey)
716716
end
717717
for M in mod::Vector{Any}
718+
M = M::Module
718719
if PkgId(M) == modkey && module_build_id(M) === build_id
719720
return M
720721
end

base/logging.jl

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,15 @@ logger type.
7171
catch_exceptions(logger) = true
7272

7373

74+
# Prevent invalidation when packages define custom loggers
75+
# Using invoke in combination with @nospecialize eliminates backedges to these methods
76+
function _invoked_shouldlog(logger, level, _module, group, id)
77+
@nospecialize
78+
invoke(shouldlog, Tuple{typeof(logger), typeof(level), typeof(_module), typeof(group), typeof(id)}, logger, level, _module, group, id)
79+
end
80+
81+
_invoked_min_enabled_level(@nospecialize(logger)) = invoke(min_enabled_level, Tuple{typeof(logger)}, logger)
82+
7483

7584
"""
7685
NullLogger()
@@ -315,7 +324,7 @@ function logmsg_code(_module, file, line, level, message, exs...)
315324
id = $id
316325
# Second chance at an early bail-out (before computing the message),
317326
# based on arbitrary logger-specific logic.
318-
if shouldlog(logger, level, _module, group, id)
327+
if _invoked_shouldlog(logger, level, _module, group, id)
319328
file = $file
320329
line = $line
321330
try
@@ -374,7 +383,7 @@ struct LogState
374383
logger::AbstractLogger
375384
end
376385

377-
LogState(logger) = LogState(LogLevel(min_enabled_level(logger)), logger)
386+
LogState(logger) = LogState(LogLevel(_invoked_min_enabled_level(logger)), logger)
378387

379388
function current_logstate()
380389
logstate = current_task().logstate
@@ -391,6 +400,7 @@ end
391400
end
392401

393402
function with_logstate(f::Function, logstate)
403+
@nospecialize
394404
t = current_task()
395405
old = t.logstate
396406
try
@@ -401,7 +411,6 @@ function with_logstate(f::Function, logstate)
401411
end
402412
end
403413

404-
405414
#-------------------------------------------------------------------------------
406415
# Control of the current logger and early log filtering
407416

@@ -502,7 +511,7 @@ with_logger(logger) do
502511
end
503512
```
504513
"""
505-
with_logger(f::Function, logger::AbstractLogger) = with_logstate(f, LogState(logger))
514+
with_logger(@nospecialize(f::Function), logger::AbstractLogger) = with_logstate(f, LogState(logger))
506515

507516
"""
508517
current_logger()

base/multidimensional.jl

Lines changed: 5 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,11 @@ module IteratorsMD
310310
convert(::Type{CartesianIndices{N,R}}, inds::CartesianIndices{N}) where {N,R} =
311311
CartesianIndices(convert(R, inds.indices))
312312

313+
# equality
314+
Base.:(==)(a::CartesianIndices{N}, b::CartesianIndices{N}) where N =
315+
all(map(==, a.indices, b.indices))
316+
Base.:(==)(a::CartesianIndices, b::CartesianIndices) = false
317+
313318
# AbstractArray implementation
314319
Base.axes(iter::CartesianIndices{N,R}) where {N,R} = map(Base.axes1, iter.indices)
315320
Base.IndexStyle(::Type{CartesianIndices{N,R}}) where {N,R} = IndexCartesian()
@@ -937,56 +942,6 @@ function fill!(A::AbstractArray{T}, x) where T
937942
A
938943
end
939944

940-
"""
941-
copyto!(dest::AbstractArray, src) -> dest
942-
943-
944-
Copy all elements from collection `src` to array `dest`, whose length must be greater than
945-
or equal to the length `n` of `src`. The first `n` elements of `dest` are overwritten,
946-
the other elements are left untouched.
947-
948-
# Examples
949-
```jldoctest
950-
julia> x = [1., 0., 3., 0., 5.];
951-
952-
julia> y = zeros(7);
953-
954-
julia> copyto!(y, x);
955-
956-
julia> y
957-
7-element Array{Float64,1}:
958-
1.0
959-
0.0
960-
3.0
961-
0.0
962-
5.0
963-
0.0
964-
0.0
965-
```
966-
"""
967-
copyto!(dest, src)
968-
969-
function copyto!(dest::AbstractArray{T1,N}, src::AbstractArray{T2,N}) where {T1,T2,N}
970-
isempty(src) && return dest
971-
src′ = unalias(dest, src)
972-
# fastpath for equal axes (#34025)
973-
if axes(dest) == axes(src)
974-
for I in eachindex(IndexStyle(src′,dest), src′)
975-
@inbounds dest[I] = src′[I]
976-
end
977-
# otherwise enforce linear indexing
978-
else
979-
isrc = eachindex(IndexLinear(), src)
980-
idest = eachindex(IndexLinear(), dest)
981-
ΔI = first(idest) - first(isrc)
982-
checkbounds(dest, last(isrc) + ΔI)
983-
for I in isrc
984-
@inbounds dest[I + ΔI] = src′[I]
985-
end
986-
end
987-
return dest
988-
end
989-
990945
function copyto!(dest::AbstractArray{T1,N}, Rdest::CartesianIndices{N},
991946
src::AbstractArray{T2,N}, Rsrc::CartesianIndices{N}) where {T1,T2,N}
992947
isempty(Rdest) && return dest

base/set.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ delete!(s::Set, x) = (delete!(s.dict, x); s)
6767

6868
copy(s::Set) = copymutable(s)
6969

70+
copymutable(s::Set{T}) where {T} = Set{T}(s)
7071
# Set is the default mutable fall-back
7172
copymutable(s::AbstractSet{T}) where {T} = Set{T}(s)
7273

contrib/generate_precompile.jl

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,14 @@ CTRL_C = '\x03'
1313
UP_ARROW = "\e[A"
1414
DOWN_ARROW = "\e[B"
1515

16+
hardcoded_precompile_statements = """
17+
precompile(Tuple{typeof(Base.stale_cachefile), String, String})
18+
precompile(Tuple{typeof(push!), Set{Module}, Module})
19+
precompile(Tuple{typeof(push!), Set{Method}, Method})
20+
precompile(Tuple{typeof(push!), Set{Base.PkgId}, Base.PkgId})
21+
precompile(Tuple{typeof(setindex!), Dict{String,Base.PkgId}, Base.PkgId, String})
22+
"""
23+
1624
precompile_script = """
1725
2+2
1826
print("")
@@ -152,6 +160,10 @@ function generate_precompile_statements()
152160
push!(statements, statement)
153161
end
154162

163+
for statement in split(hardcoded_precompile_statements, '\n')
164+
push!(statements, statement)
165+
end
166+
155167
# Create a staging area where all the loaded packages are available
156168
PrecompileStagingArea = Module()
157169
for (_pkgid, _mod) in Base.loaded_modules

test/arrayops.jl

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1229,6 +1229,12 @@ end
12291229
R = reshape(A, 2, 2)
12301230
A[R] .= reshape((1:4) .+ 2^30, 2, 2)
12311231
@test A == [2,1,4,3] .+ 2^30
1232+
1233+
# unequal dimensionality (see comment in #35714)
1234+
a = [1 3; 2 4]
1235+
b = [3, 1, 4, 2]
1236+
copyto!(view(a, b), a)
1237+
@test a == [2 1; 4 3]
12321238
end
12331239

12341240
@testset "Base.mightalias unit tests" begin
@@ -2799,4 +2805,4 @@ end
27992805
showerror(b, err)
28002806
@test String(take!(b)) ==
28012807
"BoundsError: attempt to access 2×2 Array{Float64,2} at index [10, \"bad index\"]"
2802-
end
2808+
end

0 commit comments

Comments
 (0)