@@ -120,7 +120,7 @@ const DenseVecOrMat{T} = Union{DenseVector{T}, DenseMatrix{T}}
120120
121121# # Basic functions ##
122122
123- using Core: arraysize, arrayset, const_arrayref
123+ using Core: arraysize, arrayset, arrayref, const_arrayref
124124
125125"""
126126 @_safeindex
@@ -232,6 +232,7 @@ function _unsetindex!(A::Array{T}, i::Int) where {T}
232232end
233233
234234
235+ # TODO : deprecate this (aligned_sizeof and/or elsize and/or sizeof(Some{T}) are more correct)
235236"""
236237 Base.bitsunionsize(U::Union) -> Int
237238
@@ -252,7 +253,7 @@ function bitsunionsize(u::Union)
252253 return sz
253254end
254255
255- elsize (@nospecialize _ :: Type{A} ) where {T,A<: Array{T} } = aligned_sizeof (T)
256+ elsize (:: Type{A} ) where {T,A<: Array{T} } = aligned_sizeof (T)
256257function elsize (:: Type{Ptr{T}} ) where T
257258 # this only must return something valid for values which satisfy is_valid_intrinsic_elptr(T),
258259 # which includes Any and most concrete datatypes
@@ -261,15 +262,23 @@ function elsize(::Type{Ptr{T}}) where T
261262 return LLT_ALIGN (Core. sizeof (T), datatype_alignment (T))
262263end
263264elsize (:: Type{Union{}} , slurp... ) = 0
264- sizeof (a:: Array ) = Core. sizeof (a)
265+
266+ sizeof (a:: Array ) = length (a) * elsize (typeof (a)) # TODO (jwn): add bitsunion bytes?
265267
266268function isassigned (a:: Array , i:: Int... )
267269 @inline
268270 @boundscheck checkbounds (Bool, a, i... ) || return false
269- ii = (_sub2ind (size (a), i... ) % UInt) - 1
270- ccall (:jl_array_isassigned , Cint, (Any, UInt), a, ii) == 1
271+ ii = _sub2ind (size (a), i... )
272+ return @inbounds isassigned (memoryref (a. ref, ii, false ))
273+ end
274+
275+ function isassigned (a:: Vector , i:: Int ) # slight compiler simplification for the most common case
276+ @inline
277+ @boundscheck checkbounds (Bool, a, i) || return false
278+ return @inbounds isassigned (memoryref (a. ref, i, false ))
271279end
272280
281+
273282# # copy ##
274283
275284"""
291300
292301
293302function _unsafe_copyto! (dest, doffs, src, soffs, n)
303+ @_terminates_locally_meta
304+ # use pointer math to determine if they are deemed to alias
294305 destp = pointer (dest, doffs)
295306 srcp = pointer (src, soffs)
296- @inbounds if destp < srcp || destp > srcp + n
307+ endp = pointer (src, soffs + n - 1 )
308+ @inbounds if destp < srcp || destp > endp
297309 for i = 1 : n
298310 if isassigned (src, soffs + i - 1 )
299311 dest[doffs + i - 1 ] = src[soffs + i - 1 ]
@@ -324,6 +336,7 @@ that N is inbounds on either array. Incorrect usage may corrupt or segfault your
324336the same manner as C.
325337"""
326338function unsafe_copyto! (dest:: Array{T} , doffs, src:: Array{T} , soffs, n) where T
339+ @_terminates_locally_meta
327340 t1 = @_gc_preserve_begin dest
328341 t2 = @_gc_preserve_begin src
329342 destp = pointer (dest, doffs)
@@ -1063,24 +1076,188 @@ function setindex!(A::Array{T}, X::Array{T}, c::Colon) where T
10631076 return A
10641077end
10651078
1066- # efficiently grow an array
1079+ # Pick new memory size for efficiently growing an array
1080+ # TODO : This should know about the size of our GC pools
1081+ # Specifically we are wasting ~10% of memory for small arrays
1082+ # by not picking memory sizes that max out a GC pool
1083+ function overallocation (maxsize)
1084+ maxsize < 8 && return 8 ;
1085+ # compute maxsize = maxsize + 4*maxsize^(7/8) + maxsize/8
1086+ # for small n, we grow faster than O(n)
1087+ # for large n, we grow at O(n/8)
1088+ # and as we reach O(memory) for memory>>1MB,
1089+ # this means we end by adding about 10% of memory each time
1090+ exp2 = sizeof (maxsize) * 8 - Core. Intrinsics. ctlz_int (maxsize)
1091+ maxsize += (1 << div (exp2 * 7 , 8 )) * 4 + div (maxsize, 8 )
1092+ return maxsize
1093+ end
1094+
1095+ function array_new_memory (mem:: Memory{T} , newlen:: Int ) where T
1096+ if ccall (:jl_mem_owner , Any, (Memory{T},), mem) isa String
1097+ # if data is in a String, keep it that way
1098+ # TODO : use jl_gc_expand_string(oldstr, newlen)?
1099+ str = _string_n (newlen)
1100+ return ccall (:jl_string_to_genericmemory , Memory{T}, (Any,), str)
1101+ else
1102+ # TODO : when implimented, this should use a memory growing call
1103+ mem = Memory {T} (undef, newlen)
1104+ return mem
1105+ end
1106+ end
10671107
1068- _growbeg! (a:: Vector , delta:: Integer ) =
1069- ccall (:jl_array_grow_beg , Cvoid, (Any, UInt), a, delta)
1070- _growend! (a:: Vector , delta:: Integer ) =
1071- ccall (:jl_array_grow_end , Cvoid, (Any, UInt), a, delta)
1072- _growat! (a:: Vector , i:: Integer , delta:: Integer ) =
1073- ccall (:jl_array_grow_at , Cvoid, (Any, Int, UInt), a, i - 1 , delta)
1108+ function _growbeg! (a:: Vector , delta:: Integer )
1109+ delta = Int (delta)
1110+ delta == 0 && return # avoid attempting to index off the end
1111+ delta >= 0 || throw (ArgumentError (" grow requires delta >= 0" ))
1112+ ref = a. ref
1113+ mem = ref. mem
1114+ len = length (a)
1115+ offset = memoffset (ref)
1116+ newlen = len + delta
1117+ a. size = (newlen,)
1118+ # if offset is far enough advanced to fit data in existing memory without copying
1119+ if delta <= offset
1120+ a. ref = MemoryRef (ref, 1 - delta, false )
1121+ else
1122+ @noinline (function ()
1123+ memlen = length (mem)
1124+ # since we will allocate the array in the middle of the memory we need at least 2*delta extra space
1125+ # the +1 is because I didn't want to have an off by 1 error.
1126+ newmemlen = max (overallocation (memlen), len+ 2 * delta+ 1 )
1127+ newoffset = div (newmemlen - newlen, 2 )
1128+ # If there is extra data after the end of the array we can use that space so long as there is enough
1129+ # space at the end that there won't be quadratic behavior with a mix of growth from both ends.
1130+ # Specifically, we want to ensure that we will only do this operation once before
1131+ # increasing the size of the array, and that we leave enough space at both the benining and the end.
1132+ if newoffset + newlen + 1 < memlen
1133+ newoffset = div (memlen - newlen, 2 )
1134+ newmem = mem
1135+ else
1136+ newmem = array_new_memory (mem, newmemlen)
1137+ end
1138+ if len > 0
1139+ unsafe_copyto! (newmem, newoffset+ delta+ 1 , mem, offset+ 1 , len)
1140+ end
1141+ a. ref = MemoryRef (newmem, newoffset+ 1 , false )
1142+ end )()
1143+ end
1144+ return
1145+ end
10741146
1075- # efficiently delete part of an array
1147+ function _growend! (a:: Vector , delta:: Integer )
1148+ delta = Int (delta)
1149+ delta >= 0 || throw (ArgumentError (" grow requires delta >= 0" ))
1150+ ref = a. ref
1151+ mem = ref. mem
1152+ memlen = length (mem)
1153+ len = length (a)
1154+ newlen = len + delta
1155+ offset = memoffset (ref)
1156+ a. size = (newlen,)
1157+ newmemlen = offset + newlen
1158+ if memlen < newmemlen
1159+ @noinline (function ()
1160+ if offset > div (5 * newlen, 4 )
1161+ # If the offset is far enough that we can copy without resizing
1162+ # while maintaining proportional spacing on both ends of the array
1163+ # note that this branch prevents infinite growth when doing combinations
1164+ # of push! and popfirst! (i.e. when using a Vector as a queue)
1165+ newmem = mem
1166+ newoffset = div (newlen, 8 )
1167+ else
1168+ # grow either by our computed overallocation factor
1169+ # or exactly the requested size, whichever is larger
1170+ # TODO we should possibly increase the offset if the current offset is nonzero,
1171+ newmemlen2 = max (overallocation (memlen), newmemlen)
1172+ newmem = array_new_memory (mem, newmemlen2)
1173+ newoffset = offset
1174+ end
1175+ if len > 0
1176+ unsafe_copyto! (newmem, newoffset+ 1 , mem, offset+ 1 , len)
1177+ end
1178+ a. ref = MemoryRef (newmem, newoffset+ 1 , false )
1179+ end )()
1180+ end
1181+ return
1182+ end
10761183
1077- _deletebeg! (a:: Vector , delta:: Integer ) =
1078- ccall (:jl_array_del_beg , Cvoid, (Any, UInt), a, delta)
1079- _deleteend! (a:: Vector , delta:: Integer ) =
1080- ccall (:jl_array_del_end , Cvoid, (Any, UInt), a, delta)
1081- _deleteat! (a:: Vector , i:: Integer , delta:: Integer ) =
1082- ccall (:jl_array_del_at , Cvoid, (Any, Int, UInt), a, i - 1 , delta)
1184+ function _growat! (a:: Vector , i:: Integer , delta:: Integer )
1185+ delta = Int (delta)
1186+ i == 1 && return _growbeg! (a, delta)
1187+ len = length (a)
1188+ i == len + 1 && return _growend! (a, delta)
1189+ delta >= 0 || throw (ArgumentError (" grow requires delta >= 0" ))
1190+ 1 < i <= len || throw (BoundsError (a, i))
1191+ ref = a. ref
1192+ mem = ref. mem
1193+ memlen = length (mem)
1194+ newlen = len + delta
1195+ offset = memoffset (ref)
1196+ a. size = (newlen,)
1197+ newmemlen = offset + newlen
1198+
1199+ # which side would we rather grow into?
1200+ prefer_start = i <= div (len, 2 )
1201+ # if offset is far enough advanced to fit data in begining of the memory
1202+ if prefer_start && delta <= offset
1203+ a. ref = MemoryRef (mem, offset- delta+ 1 , false )
1204+ unsafe_copyto! (mem, offset- delta+ 1 , mem, offset+ 1 , i)
1205+ elseif ! prefer_start && memlen >= newmemlen
1206+ unsafe_copyto! (mem, offset+ delta+ i, mem, offset+ i, len- i+ 1 )
1207+ else
1208+ # since we will allocate the array in the middle of the memory we need at least 2*delta extra space
1209+ # the +1 is because I didn't want to have an off by 1 error.
1210+ newmemlen = max (overallocation (memlen), len+ 2 * delta+ 1 )
1211+ newoffset = (newmemlen - newlen) ÷ 2
1212+ newmem = array_new_memory (mem, newmemlen)
1213+ a. ref = MemoryRef (newmem, newoffset+ 1 , false )
1214+ unsafe_copyto! (newmem, newoffset+ 1 , mem, offset+ 1 , i)
1215+ unsafe_copyto! (newmem, newoffset+ delta+ i, mem, offset+ i, len- i+ 1 )
1216+ end
1217+ end
10831218
1219+ # efficiently delete part of an array
1220+ function _deletebeg! (a:: Vector , delta:: Integer )
1221+ delta = Int (delta)
1222+ len = length (a)
1223+ 0 <= delta <= len || throw (ArgumentError (" _deleteat! requires delta in 0:length(a)" ))
1224+ for i in 1 : delta
1225+ _unsetindex! (a, i)
1226+ end
1227+ newlen = len - delta
1228+ if newlen != 0 # if newlen==0 we could accidentally index past the memory
1229+ a. ref = MemoryRef (a. ref, delta + 1 , false )
1230+ end
1231+ a. size = (newlen,)
1232+ return
1233+ end
1234+ function _deleteend! (a:: Vector , delta:: Integer )
1235+ delta = Int (delta)
1236+ len = length (a)
1237+ 0 <= delta <= len || throw (ArgumentError (" _deleteat! requires delta in 0:length(a)" ))
1238+ newlen = len - delta
1239+ for i in newlen+ 1 : len
1240+ _unsetindex! (a, i)
1241+ end
1242+ a. size = (newlen,)
1243+ return
1244+ end
1245+ function _deleteat! (a:: Vector , i:: Integer , delta:: Integer )
1246+ i = Int (i)
1247+ len = length (a)
1248+ 0 <= delta || throw (ArgumentError (" _deleteat! requires delta >= 0" ))
1249+ 1 <= i <= len || throw (BoundsError (a, i))
1250+ i + delta <= len + 1 || throw (BoundsError (a, i + delta - 1 ))
1251+ newa = a
1252+ if 2 * i + delta <= len
1253+ unsafe_copyto! (newa, 1 + delta, a, 1 , i - 1 )
1254+ _deletebeg! (a, delta)
1255+ else
1256+ unsafe_copyto! (newa, i, a, i + delta, len + 1 - delta - i)
1257+ _deleteend! (a, delta)
1258+ end
1259+ return
1260+ end
10841261# # Dequeue functionality ##
10851262
10861263"""
@@ -1347,7 +1524,32 @@ function sizehint! end
13471524
13481525function sizehint! (a:: Vector , sz:: Integer )
13491526 ccall (:jl_array_sizehint , Cvoid, (Any, UInt), a, sz)
1350- a
1527+ return a
1528+ #= n = length(a)
1529+ len = length(a)
1530+ ref = a.ref
1531+ mem = ref.mem
1532+ memlen = length(mem)
1533+ newlen = len + delta
1534+ offset = memoffset(ref)
1535+ elsz = jl_array_elsize(a)
1536+ if (elsz == 0)
1537+ return a
1538+ end
1539+ sz = max(sz, offset+len)
1540+
1541+ if sz <= memlen
1542+ size_t dec = memlen - sz;
1543+ # if we don't save at least an eighth of maxsize then its not worth it to shrink
1544+ if dec <= div(memlen, 8)
1545+ return a
1546+ jl_array_shrink(a, dec)
1547+ else
1548+ inc = sz - len;
1549+ _growend(a, inc);
1550+ a.size = (len,)
1551+ end
1552+ a=#
13511553end
13521554
13531555"""
0 commit comments