1

我想向同事展示你可以分配超过 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分配内存呢?

4

3 回答 3

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在后台使用,它们被编译为newarrIL,它应该将一个新数组推送到堆栈上。

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

于 2013-10-07T11:17:23.013 回答
0

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

例如

// 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
于 2013-10-07T11:05:14.783 回答
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 内存的原始问题,您可以在 .NET 4.5 和 64 位操作系统下使用.NETArray.zeroCreate配置进行此类分配。下面的单行代码立即为数组分配了近8GB​​ RAM :gcAllowVeryLargeObjectsfsiAnyCPU.exeint

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

以下证明这是有效的:

> bigOne.[2146435070] <- 1
val it : unit = ()
> bigOne.[2146435070]
val it : int = 1
于 2013-10-07T17:08:04.867 回答