Search code examples
.netf#gcallowverylargeobjects

Difference between Array.zeroCreate and Array.init


I wanted to show to a colleague that you can allocate more than 2GB of ram, so I made a little test application.

let mega = 1 <<< 20
let konst x y = x
let allocate1MB _ = Array.init mega (konst 0uy)
let memoryHog = Array.Parallel.init 8192 allocate1MB

printfn "I'm done..."
System.Console.ReadKey() |> ignore

this works and you actually see the process happily hogging away at the system's memory. However, it takes somewhat long - hence the Array.Parallel.init.

I noticed, that the same code does not work, if I write it with

let allocate1MB _ = Array.zeroCreate mega

More precisely, no data is allocated and it takes no time.

So thus my question; What is the difference in semantics between Array.zeroCreate and Array.init?

I understand that Array.init would run my konst 0uy function each time, which would explain the time difference. But why does Array.zeroCreate not allocate the memory?


Solution

  • From the FSharp.Core sources:

    let inline init (count:int) (f: int -> 'T) = 
        if count < 0 then invalidArg "count" InputMustBeNonNegativeString
        let arr = (zeroCreateUnchecked count : 'T array)  
        for i = 0 to count - 1 do 
            arr.[i] <- f i
        arr
    
    let zeroCreate count = 
        if count < 0 then invalidArg "count" (SR.GetString(SR.inputMustBeNonNegative))
        Microsoft.FSharp.Primitives.Basics.Array.zeroCreateUnchecked count
    
    let inline zeroCreateUnchecked (count:int) = 
        (# "newarr !0" type ('T) count : 'T array #)
    

    As you can see, the both functions use zeroCreateUnchecked under the hood, which is compiled to newarr IL that is supposed to push a new array onto the stack.

    The fact that this does not consume any memory in your case might indicate that some kind of optimization is responsible. I think either JIT or the compiler is removing this operation since the created array is never used, and that obviously does not happen in the case of Array.init.