@@ -12,6 +12,9 @@ namespace Microsoft.FSharp.Core
12
12
[<CompilationRepresentation( CompilationRepresentationFlags.ModuleSuffix) >]
13
13
[<RequireQualifiedAccess>]
14
14
module String =
15
+ [<CompiledName( " Length" ) >]
16
+ let length ( str : string ) = if isNull str then 0 else str.Length
17
+
15
18
[<CompiledName( " Concat" ) >]
16
19
let concat sep ( strings : seq < string >) =
17
20
String.Join( sep, strings)
@@ -40,13 +43,24 @@ namespace Microsoft.FSharp.Core
40
43
41
44
[<CompiledName( " MapIndexed" ) >]
42
45
let mapi ( mapping : int -> char -> char ) ( str : string ) =
43
- if String.IsNullOrEmpty str then
46
+ let len = length str
47
+ if len = 0 then
44
48
String.Empty
45
49
else
46
- let res = StringBuilder str.Length
47
- let f = OptimizedClosures.FSharpFunc<_,_,_>. Adapt( mapping)
48
- str |> iteri ( fun i c -> res.Append( f.Invoke( i, c)) |> ignore)
49
- res.ToString()
50
+ let result = str.ToCharArray()
51
+ let f = OptimizedClosures.FSharpFunc<_,_,_>. Adapt mapping
52
+
53
+ // x2 unrolled loop gives 10-20% boost, overall 2.5x SB perf
54
+ let mutable i = 0
55
+ while i < len - len % 2 do
56
+ result.[ i] <- f.Invoke( i, result.[ i])
57
+ result.[ i + 1 ] <- f.Invoke( i, result.[ i + 1 ])
58
+ i <- i + 2
59
+
60
+ if i % 2 = 1 then
61
+ result.[ i] <- f.Invoke( i, result.[ i])
62
+
63
+ new String( result)
50
64
51
65
[<CompiledName( " Filter" ) >]
52
66
let filter ( predicate : char -> bool ) ( str : string ) =
@@ -101,6 +115,3 @@ namespace Microsoft.FSharp.Core
101
115
else
102
116
let rec check i = ( i < str.Length) && ( predicate str.[ i] || check ( i+ 1 ))
103
117
check 0
104
-
105
- [<CompiledName( " Length" ) >]
106
- let length ( str : string ) = if isNull str then 0 else str.Length
0 commit comments