@@ -518,44 +518,170 @@ namespace Microsoft.FSharp.Collections
518518 else
519519 empty
520520
521+ // The filter module is a space and performance for Array.filter based optimization that uses
522+ // a bitarray to store the results of the filtering of every element of the array. This means
523+ // that the only additional temporary garbage that needs to be allocated is {array.Length/8} bytes.
524+ //
525+ // Other optimizations include:
526+ // - arrays < 32 elements don't allocate any garbage at all
527+ // - when the predicate yields consecutive runs of true data that is >= 32 elements (and fall
528+ // into maskArray buckets) are copied in chunks using System.Array.Copy
529+ module Filter =
530+ let private populateMask < 'a > ( f : 'a -> bool ) ( src : array < 'a >) ( maskArray : array < uint32 >) =
531+ let mutable count = 0
532+ for maskIdx = 0 to maskArray.Length-1 do
533+ let srcIdx = maskIdx * 32
534+ let mutable mask = 0 u
535+ if f src.[ srcIdx+ 0x00 ] then mask <- mask ||| ( 1 u <<< 0x00 ); count <- count + 1
536+ if f src.[ srcIdx+ 0x01 ] then mask <- mask ||| ( 1 u <<< 0x01 ); count <- count + 1
537+ if f src.[ srcIdx+ 0x02 ] then mask <- mask ||| ( 1 u <<< 0x02 ); count <- count + 1
538+ if f src.[ srcIdx+ 0x03 ] then mask <- mask ||| ( 1 u <<< 0x03 ); count <- count + 1
539+ if f src.[ srcIdx+ 0x04 ] then mask <- mask ||| ( 1 u <<< 0x04 ); count <- count + 1
540+ if f src.[ srcIdx+ 0x05 ] then mask <- mask ||| ( 1 u <<< 0x05 ); count <- count + 1
541+ if f src.[ srcIdx+ 0x06 ] then mask <- mask ||| ( 1 u <<< 0x06 ); count <- count + 1
542+ if f src.[ srcIdx+ 0x07 ] then mask <- mask ||| ( 1 u <<< 0x07 ); count <- count + 1
543+ if f src.[ srcIdx+ 0x08 ] then mask <- mask ||| ( 1 u <<< 0x08 ); count <- count + 1
544+ if f src.[ srcIdx+ 0x09 ] then mask <- mask ||| ( 1 u <<< 0x09 ); count <- count + 1
545+ if f src.[ srcIdx+ 0x0A ] then mask <- mask ||| ( 1 u <<< 0x0A ); count <- count + 1
546+ if f src.[ srcIdx+ 0x0B ] then mask <- mask ||| ( 1 u <<< 0x0B ); count <- count + 1
547+ if f src.[ srcIdx+ 0x0C ] then mask <- mask ||| ( 1 u <<< 0x0C ); count <- count + 1
548+ if f src.[ srcIdx+ 0x0D ] then mask <- mask ||| ( 1 u <<< 0x0D ); count <- count + 1
549+ if f src.[ srcIdx+ 0x0E ] then mask <- mask ||| ( 1 u <<< 0x0E ); count <- count + 1
550+ if f src.[ srcIdx+ 0x0F ] then mask <- mask ||| ( 1 u <<< 0x0F ); count <- count + 1
551+ if f src.[ srcIdx+ 0x10 ] then mask <- mask ||| ( 1 u <<< 0x10 ); count <- count + 1
552+ if f src.[ srcIdx+ 0x11 ] then mask <- mask ||| ( 1 u <<< 0x11 ); count <- count + 1
553+ if f src.[ srcIdx+ 0x12 ] then mask <- mask ||| ( 1 u <<< 0x12 ); count <- count + 1
554+ if f src.[ srcIdx+ 0x13 ] then mask <- mask ||| ( 1 u <<< 0x13 ); count <- count + 1
555+ if f src.[ srcIdx+ 0x14 ] then mask <- mask ||| ( 1 u <<< 0x14 ); count <- count + 1
556+ if f src.[ srcIdx+ 0x15 ] then mask <- mask ||| ( 1 u <<< 0x15 ); count <- count + 1
557+ if f src.[ srcIdx+ 0x16 ] then mask <- mask ||| ( 1 u <<< 0x16 ); count <- count + 1
558+ if f src.[ srcIdx+ 0x17 ] then mask <- mask ||| ( 1 u <<< 0x17 ); count <- count + 1
559+ if f src.[ srcIdx+ 0x18 ] then mask <- mask ||| ( 1 u <<< 0x18 ); count <- count + 1
560+ if f src.[ srcIdx+ 0x19 ] then mask <- mask ||| ( 1 u <<< 0x19 ); count <- count + 1
561+ if f src.[ srcIdx+ 0x1A ] then mask <- mask ||| ( 1 u <<< 0x1A ); count <- count + 1
562+ if f src.[ srcIdx+ 0x1B ] then mask <- mask ||| ( 1 u <<< 0x1B ); count <- count + 1
563+ if f src.[ srcIdx+ 0x1C ] then mask <- mask ||| ( 1 u <<< 0x1C ); count <- count + 1
564+ if f src.[ srcIdx+ 0x1D ] then mask <- mask ||| ( 1 u <<< 0x1D ); count <- count + 1
565+ if f src.[ srcIdx+ 0x1E ] then mask <- mask ||| ( 1 u <<< 0x1E ); count <- count + 1
566+ if f src.[ srcIdx+ 0x1F ] then mask <- mask ||| ( 1 u <<< 0x1F ); count <- count + 1
567+ maskArray.[ maskIdx] <- mask
568+ count
569+
570+ let private createMask < 'a > ( f : 'a -> bool ) ( src : array < 'a >) ( maskArrayOut : byref < array < uint32 >>) ( leftoverMaskOut : byref < uint32 >) =
571+ let maskArrayLength = src.Length / 0x20
572+
573+ // null when there are less than 32 items in src array.
574+ let maskArray =
575+ if maskArrayLength = 0 then Unchecked.defaultof<_>
576+ else Array.zeroCreateUnchecked< uint32> maskArrayLength
577+
578+ let mutable count =
579+ match maskArray with
580+ | null -> 0
581+ | maskArray -> populateMask f src maskArray
582+
583+ let leftoverMask =
584+ match src.Length % 0x20 with
585+ | 0 -> 0 u
586+ | _ ->
587+ let mutable mask = 0 u
588+ let mutable elementMask = 1 u
589+ for arrayIdx = maskArrayLength* 0x20 to src.Length-1 do
590+ if f src.[ arrayIdx] then mask <- mask ||| elementMask; count <- count + 1
591+ elementMask <- elementMask <<< 1
592+ mask
593+
594+ maskArrayOut <- maskArray
595+ leftoverMaskOut <- leftoverMask
596+ count
597+
598+ let private populateDstViaMask < 'a > ( src : array < 'a >) ( maskArray : array < uint32 >) ( dst : array < 'a >) =
599+ let mutable dstIdx = 0
600+ let mutable batchCount = 0
601+ for maskIdx = 0 to maskArray.Length-1 do
602+ let mask = maskArray.[ maskIdx]
603+ if mask = 0xFFFFFFFF u then
604+ batchCount <- batchCount + 1
605+ else
606+ let srcIdx = maskIdx * 0x20
607+
608+ if batchCount <> 0 then
609+ let batchSize = batchCount * 0x20
610+ System.Array.Copy ( src, srcIdx- batchSize, dst, dstIdx, batchSize)
611+ dstIdx <- dstIdx + batchSize
612+ batchCount <- 0
613+
614+ if mask <> 0 u then
615+ if mask &&& ( 1 u <<< 0x00 ) <> 0 u then dst.[ dstIdx] <- src.[ srcIdx+ 0x00 ]; dstIdx <- dstIdx + 1
616+ if mask &&& ( 1 u <<< 0x01 ) <> 0 u then dst.[ dstIdx] <- src.[ srcIdx+ 0x01 ]; dstIdx <- dstIdx + 1
617+ if mask &&& ( 1 u <<< 0x02 ) <> 0 u then dst.[ dstIdx] <- src.[ srcIdx+ 0x02 ]; dstIdx <- dstIdx + 1
618+ if mask &&& ( 1 u <<< 0x03 ) <> 0 u then dst.[ dstIdx] <- src.[ srcIdx+ 0x03 ]; dstIdx <- dstIdx + 1
619+ if mask &&& ( 1 u <<< 0x04 ) <> 0 u then dst.[ dstIdx] <- src.[ srcIdx+ 0x04 ]; dstIdx <- dstIdx + 1
620+ if mask &&& ( 1 u <<< 0x05 ) <> 0 u then dst.[ dstIdx] <- src.[ srcIdx+ 0x05 ]; dstIdx <- dstIdx + 1
621+ if mask &&& ( 1 u <<< 0x06 ) <> 0 u then dst.[ dstIdx] <- src.[ srcIdx+ 0x06 ]; dstIdx <- dstIdx + 1
622+ if mask &&& ( 1 u <<< 0x07 ) <> 0 u then dst.[ dstIdx] <- src.[ srcIdx+ 0x07 ]; dstIdx <- dstIdx + 1
623+ if mask &&& ( 1 u <<< 0x08 ) <> 0 u then dst.[ dstIdx] <- src.[ srcIdx+ 0x08 ]; dstIdx <- dstIdx + 1
624+ if mask &&& ( 1 u <<< 0x09 ) <> 0 u then dst.[ dstIdx] <- src.[ srcIdx+ 0x09 ]; dstIdx <- dstIdx + 1
625+ if mask &&& ( 1 u <<< 0x0A ) <> 0 u then dst.[ dstIdx] <- src.[ srcIdx+ 0x0A ]; dstIdx <- dstIdx + 1
626+ if mask &&& ( 1 u <<< 0x0B ) <> 0 u then dst.[ dstIdx] <- src.[ srcIdx+ 0x0B ]; dstIdx <- dstIdx + 1
627+ if mask &&& ( 1 u <<< 0x0C ) <> 0 u then dst.[ dstIdx] <- src.[ srcIdx+ 0x0C ]; dstIdx <- dstIdx + 1
628+ if mask &&& ( 1 u <<< 0x0D ) <> 0 u then dst.[ dstIdx] <- src.[ srcIdx+ 0x0D ]; dstIdx <- dstIdx + 1
629+ if mask &&& ( 1 u <<< 0x0E ) <> 0 u then dst.[ dstIdx] <- src.[ srcIdx+ 0x0E ]; dstIdx <- dstIdx + 1
630+ if mask &&& ( 1 u <<< 0x0F ) <> 0 u then dst.[ dstIdx] <- src.[ srcIdx+ 0x0F ]; dstIdx <- dstIdx + 1
631+ if mask &&& ( 1 u <<< 0x10 ) <> 0 u then dst.[ dstIdx] <- src.[ srcIdx+ 0x10 ]; dstIdx <- dstIdx + 1
632+ if mask &&& ( 1 u <<< 0x11 ) <> 0 u then dst.[ dstIdx] <- src.[ srcIdx+ 0x11 ]; dstIdx <- dstIdx + 1
633+ if mask &&& ( 1 u <<< 0x12 ) <> 0 u then dst.[ dstIdx] <- src.[ srcIdx+ 0x12 ]; dstIdx <- dstIdx + 1
634+ if mask &&& ( 1 u <<< 0x13 ) <> 0 u then dst.[ dstIdx] <- src.[ srcIdx+ 0x13 ]; dstIdx <- dstIdx + 1
635+ if mask &&& ( 1 u <<< 0x14 ) <> 0 u then dst.[ dstIdx] <- src.[ srcIdx+ 0x14 ]; dstIdx <- dstIdx + 1
636+ if mask &&& ( 1 u <<< 0x15 ) <> 0 u then dst.[ dstIdx] <- src.[ srcIdx+ 0x15 ]; dstIdx <- dstIdx + 1
637+ if mask &&& ( 1 u <<< 0x16 ) <> 0 u then dst.[ dstIdx] <- src.[ srcIdx+ 0x16 ]; dstIdx <- dstIdx + 1
638+ if mask &&& ( 1 u <<< 0x17 ) <> 0 u then dst.[ dstIdx] <- src.[ srcIdx+ 0x17 ]; dstIdx <- dstIdx + 1
639+ if mask &&& ( 1 u <<< 0x18 ) <> 0 u then dst.[ dstIdx] <- src.[ srcIdx+ 0x18 ]; dstIdx <- dstIdx + 1
640+ if mask &&& ( 1 u <<< 0x19 ) <> 0 u then dst.[ dstIdx] <- src.[ srcIdx+ 0x19 ]; dstIdx <- dstIdx + 1
641+ if mask &&& ( 1 u <<< 0x1A ) <> 0 u then dst.[ dstIdx] <- src.[ srcIdx+ 0x1A ]; dstIdx <- dstIdx + 1
642+ if mask &&& ( 1 u <<< 0x1B ) <> 0 u then dst.[ dstIdx] <- src.[ srcIdx+ 0x1B ]; dstIdx <- dstIdx + 1
643+ if mask &&& ( 1 u <<< 0x1C ) <> 0 u then dst.[ dstIdx] <- src.[ srcIdx+ 0x1C ]; dstIdx <- dstIdx + 1
644+ if mask &&& ( 1 u <<< 0x1D ) <> 0 u then dst.[ dstIdx] <- src.[ srcIdx+ 0x1D ]; dstIdx <- dstIdx + 1
645+ if mask &&& ( 1 u <<< 0x1E ) <> 0 u then dst.[ dstIdx] <- src.[ srcIdx+ 0x1E ]; dstIdx <- dstIdx + 1
646+ if mask &&& ( 1 u <<< 0x1F ) <> 0 u then dst.[ dstIdx] <- src.[ srcIdx+ 0x1F ]; dstIdx <- dstIdx + 1
647+
648+ if batchCount <> 0 then
649+ let srcIdx = maskArray.Length * 0x20
650+ let batchSize = batchCount * 0x20
651+ System.Array.Copy ( src, srcIdx- batchSize, dst, dstIdx, batchSize)
652+ dstIdx <- dstIdx + batchSize
653+
654+ dstIdx
655+
656+ let private filterViaMask ( maskArray : array < uint32 >) ( leftoverMask : uint32 ) ( count : int ) ( src : array < _ >) =
657+ let dst = Array.zeroCreateUnchecked count
658+
659+ let mutable dstIdx = 0
660+ let srcIdx =
661+ match maskArray with
662+ | null -> 0
663+ | _ ->
664+ dstIdx <- populateDstViaMask src maskArray dst
665+ maskArray.Length* 0x20
666+
667+ let mutable elementMask = 1 u
668+ for srcIdx = srcIdx to src.Length-1 do
669+ if leftoverMask &&& elementMask <> 0 u then dst.[ dstIdx] <- src.[ srcIdx]; dstIdx <- dstIdx + 1
670+ elementMask <- elementMask <<< 1
671+
672+ dst
673+
674+ let filter f ( src : array < _ >) =
675+ let mutable maskArray = Unchecked.defaultof<_>
676+ let mutable leftOverMask = Unchecked.defaultof<_>
677+ match createMask f src & maskArray & leftOverMask with
678+ | 0 -> empty
679+ | count -> filterViaMask maskArray leftOverMask count src
680+
521681 [<CompiledName( " Filter" ) >]
522682 let filter f ( array : _ []) =
523- checkNonNull " array" array
524- let mutable i = 0
525- while i < array.Length && not ( f array.[ i]) do
526- i <- i + 1
527-
528- if i <> array.Length then
529- let mutable element = array.[ i]
530- let chunk1 : 'T [] = Array.zeroCreateUnchecked ((( array.Length- i) >>> 2 ) + 1 )
531- let mutable count = 1
532- chunk1.[ 0 ] <- element
533- i <- i + 1
534- while count < chunk1.Length && i < array.Length do
535- element <- array.[ i]
536- if f element then
537- chunk1.[ count] <- element
538- count <- count + 1
539- i <- i + 1
540-
541- if i < array.Length then
542- let chunk2 = Array.zeroCreateUnchecked ( array.Length- i)
543- count <- 0
544- while i < array.Length do
545- element <- array.[ i]
546- if f element then
547- chunk2.[ count] <- element
548- count <- count + 1
549- i <- i + 1
550-
551- let res : 'T [] = Array.zeroCreateUnchecked ( chunk1.Length + count)
552- Array.Copy( chunk1, res, chunk1.Length)
553- Array.Copy( chunk2, 0 , res, chunk1.Length, count)
554- res
555- else
556- Array.subUnchecked 0 count chunk1
557- else empty
558-
683+ checkNonNull " array" array
684+ Filter.filter f array
559685
560686 [<CompiledName( " Where" ) >]
561687 let where f ( array : _ []) = filter f array
0 commit comments