Array.zeroCreate和Array.init之间的区别

时间:2013-10-07 10:40:24

标签: .net f# gcallowverylargeobjects

我想向同事证明你可以分配超过2GB的内存,所以我做了一个小测试应用程序。

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

这有效,你实际上看到这个过程正在愉快地占据系统的记忆。但是,它需要一些时间 - 因此Array.Parallel.init

我注意到,如果我用

写它,相同的代码不起作用
let allocate1MB _ = Array.zeroCreate mega

更准确地说,没有分配数据,也没有时间。

因此,我的问题; Array.zeroCreate和Array.init之间的语义有什么不同?

我知道Array.init每次都会运行konst 0uy函数,这可以解释时差。但为什么Array.zeroCreate没有分配内存?

3 个答案:

答案 0 :(得分:2)

来自FSharp.Core来源:

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 #)

正如您所看到的,这两个函数在引擎盖下使用zeroCreateUnchecked,它被编译为newarr IL,它应该将新数组推送到堆栈中。

在您的情况下,这不消耗任何内存的事实可能表明某种优化是负责任的。我认为JIT或编译器正在删除此操作,因为从未使用过创建的数组,并且在Array.init的情况下显然不会发生。

答案 1 :(得分:0)

Array.zeroCreate确实会分配内存,它会创建数组,每个项目都初始化为默认值,而Array.init则可以设置每个项目的值。

e.g。

// create large array of bytes set to 0
let array : byte[] = Array.zeroCreate (1 <<< 20)

// create large array of object references set to null.
let array : obj[] = Array.zeroCreate (1 <<< 20)

更改示例中的这两行将在运行时抛出Out of Memory异常,因此正在分配内存。

let allocate1MB _ = Array.zeroCreate mega
let memoryHog : byte[][] = Array.Parallel.init 8192 allocate1MB

答案 2 :(得分:0)

回答你的直接问题:Array.zeroCreate生成类型为默认值的元素数组,而Array.init使用提供的生成器函数创建每个元素数组。您始终可以使用Array.zeroCreate的对应生成器函数来实现Array.init的语义:

- 对于值类型,例如byte

> let az: byte [] = Array.zeroCreate 1;;
val az : byte [] = [|0uy|]
> let ai = Array.init 1 (fun _ -> 0uy);;
val ai : byte [] = [|0uy|]

- 用于参考类型,例如string

> let az: string [] = Array.zeroCreate 1;;
val az : string [] = [|null|]
> let ai = Array.init 1 (fun _ -> Unchecked.defaultof<string>);;
val ai : string [] = [|null|]

现在,将此观察应用于分配超过2GB RAM的原始问题,您可以在enabling gcAllowVeryLargeObjects in configuration {{{3}}之后使用.NET 4.5和64位操作系统下的Array.zeroCreate进行此类分配。 1}}。下面的单行代码可以立即为fsiAnyCPU.exe 的数组分配 8GB 的RAM

int

以下证明这有效:

> let bigOne: int [] = Array.zeroCreate 2146435071