添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
// initialize array to fit all expected items + a little wiggle room SetLength ( array , initial length + x ); // current item index curr_index := 0 ; begin // fill in the array and increase index with each line // if curr_index (+1) > Length(array) : resize array to new length to fit more items Inc ( curr_index ); end ; // finalize array SetLength ( array , curr_index ); I thought that I don't really need to SetLength, if it's already filled to the max: // finalize array, only if needed if curr_index <> Length ( array ) then SetLength ( array , curr_index ); So, I was looking into the DynArraySetLength to see what it does if you try to resize to the same length as it is. And DynArraySetLength checks for 0, <0, erros... but it doesn't check for NewLength = OldLength. This happens only a few calls later in ReallocMem -> GetMem -> AllocateLargeBlock. And it does some memory related stuff, but eventually doesn't resize or allocates new memory space. Not only with SetLength but with other situation where loops with hundreds or thousands of iterations will be performed, eg. in a case where searching for TBytes in an array, sometimes i do check length before call the compare, in other places where the length is the same but i have thousands of hash values, so i check the first byte then call the compare, thus way i reduced the overhead of the call to 1 in 256 instead of paying it each compare, sometimes i do that with strings ! procedure DynArraySetLength ( var a : Pointer ; typeInfo : Pointer ; dimCnt : NativeInt ; lengthVec : PNativeint ); //.... if SysHasWeakRef ( PTypeInfo ( ElTypeInfo )) then begin if newLength < oldLength then minLength := newLength minLength := oldLength ; GetMem ( pp , neededSize ); FillChar (( PByte ( pp ) + SizeOf ( TDynArrayRec ))^, minLength * elSize , 0 ); if p <> nil then begin // ---> here <--- MoveArray ( PByte ( pp ) + SizeOf ( TDynArrayRec ), PByte ( p ) + SizeOf ( TDynArrayRec ), ElTypeInfo , minLength ); if newLength < oldLength then FinalizeArray ( PByte ( p ) + SizeOf ( TDynArrayRec ) + newLength * elSize , ElTypeInfo , oldLength - newLength ); FreeMem ( p ); end ; 2 - The operation executes on O(n) when the array is multidimensional : // Take care of the inner dimensions, if any if dimCnt > 1 then begin Inc ( lengthVec ); Dec ( dimCnt ); i := 0 ; while i < newLength do begin DynArraySetLength ( PPointerArray ( p )[ i ], ElTypeInfo , dimCnt , lengthVec ); Inc ( i ); end ; except // Free arrays on exception for j := 0 to i do _DynArrayClear ( PPointerArray ( p )[ j ], ElTypeInfo ); _DynArrayClear ( p , typeInfo ); raise ; end ; end ; //--------------------------------- LArray : array of array of Integer ; begin SetLength ( LArray , 100 , 10 ); SetLength ( LArray , 100 , 10 ); // DynArraySetLength x100 end ; 3 - The function can ruin the cache ! because it dereferences some rtti data and it relies on MM to check for the block size. If the check was implemented inside the function than we won't dereference any unnecessary data. 4 - Adding a simple check to compare old vs new length will be much better and avoids all the issues above. GetMem ( pp , neededSize ); FillChar (( PByte ( pp ) + SizeOf ( TDynArrayRec ))^, minLength * elSize , 0 ); MoveArray ( PByte ( pp ) + SizeOf ( TDynArrayRec ), PByte ( p ) + SizeOf ( TDynArrayRec ), ElTypeInfo , minLength );