module Bigarray:sig
..end
大型、多维数值数组。
此模块实现了整数和浮点数的多维数组,以下称为“Bigarrays”,以将其与Array
中描述的标准 OCaml 数组区分开来。
该实现允许在 OCaml 代码和 C 或 Fortran 数值库之间高效共享大型数值数组。
“Bigarrays”和标准 OCaml 数组之间的主要区别如下
鼓励此模块的用户在其源代码中执行open Bigarray
,然后通过简短的点表示法引用数组类型和操作,例如Array1.t
或Array2.sub
。
Bigarrays 支持所有 OCaml 特设多态操作
=
、<>
、<=
等,以及compare
);Hash
);Marshal
模块的函数,以及output_value
和input_value
)。Bigarrays 可以包含以下类型的元素
Bigarray.float16_elt
),Bigarray.float32_elt
),Bigarray.float64_elt
),Bigarray.complex32_elt
),Bigarray.complex64_elt
),Bigarray.int8_signed_elt
或 Bigarray.int8_unsigned_elt
),Bigarray.int16_signed_elt
或 Bigarray.int16_unsigned_elt
),Bigarray.int_elt
),Bigarray.int32_elt
),Bigarray.int64_elt
),Bigarray.nativeint_elt
)。每种元素种类在类型级别上由下面定义的 *_elt
类型之一表示(出于技术上的单射性原因,使用单个构造函数而不是抽象类型来定义)。
type
float16_elt =
| |
Float16_elt |
type
float32_elt =
| |
Float32_elt |
type
float64_elt =
| |
Float64_elt |
type
int8_signed_elt =
| |
Int8_signed_elt |
type
int8_unsigned_elt =
| |
Int8_unsigned_elt |
type
int16_signed_elt =
| |
Int16_signed_elt |
type
int16_unsigned_elt =
| |
Int16_unsigned_elt |
type
int32_elt =
| |
Int32_elt |
type
int64_elt =
| |
Int64_elt |
type
int_elt =
| |
Int_elt |
type
nativeint_elt =
| |
Nativeint_elt |
type
complex32_elt =
| |
Complex32_elt |
type
complex64_elt =
| |
Complex64_elt |
type ('a, 'b)
kind =
| |
Float32 : |
| |
Float64 : |
| |
Int8_signed : |
| |
Int8_unsigned : |
| |
Int16_signed : |
| |
Int16_unsigned : |
| |
Int32 : |
| |
Int64 : |
| |
Int : |
| |
Nativeint : |
| |
Complex32 : |
| |
Complex64 : |
| |
Char : |
| |
Float16 : |
与每个元素种类关联一个 OCaml 类型,它是可以存储在 Bigarray 中或从中读取回 OCaml 值的类型。此类型不一定与数组元素本身的类型相同:例如,元素种类为 float32_elt
的 Bigarray 包含 32 位单精度浮点数,但从 OCaml 读取或写入其一个元素使用 OCaml 类型 float
,它是 64 位双精度浮点数。
GADT 类型 ('a, 'b) kind
捕获了用于在 Bigarray 中读取或写入值的 OCaml 类型 'a
和表示 Bigarray 实际内容的元素种类 'b
之间的这种关联。它的构造函数列出了 OCaml 类型与元素种类之间所有可能的关联,并且出于向后兼容性的原因,在下面重新导出了这些构造函数。
在此处使用广义代数数据类型 (GADT) 允许编写类型良好的多态函数,其返回类型取决于参数类型,例如
let zero : type a b. (a, b) kind -> a = function
| Float32 -> 0.0 | Complex32 -> Complex.zero
| Float64 -> 0.0 | Complex64 -> Complex.zero
| Float16 -> 0.0
| Int8_signed -> 0 | Int8_unsigned -> 0
| Int16_signed -> 0 | Int16_unsigned -> 0
| Int32 -> 0l | Int64 -> 0L
| Int -> 0 | Nativeint -> 0n
| Char -> '\000'
val float16 : (float, float16_elt) kind
参见 Bigarray.char
。
val float32 : (float, float32_elt) kind
参见 Bigarray.char
。
val float64 : (float, float64_elt) kind
参见 Bigarray.char
。
val complex32 : (Complex.t, complex32_elt) kind
参见 Bigarray.char
。
val complex64 : (Complex.t, complex64_elt) kind
参见 Bigarray.char
。
val int8_signed : (int, int8_signed_elt) kind
参见 Bigarray.char
。
val int8_unsigned : (int, int8_unsigned_elt) kind
参见 Bigarray.char
。
val int16_signed : (int, int16_signed_elt) kind
参见 Bigarray.char
。
val int16_unsigned : (int, int16_unsigned_elt) kind
参见 Bigarray.char
。
val int : (int, int_elt) kind
参见 Bigarray.char
。
val int32 : (int32, int32_elt) kind
参见 Bigarray.char
。
val int64 : (int64, int64_elt) kind
参见 Bigarray.char
。
val nativeint : (nativeint, nativeint_elt) kind
参见 Bigarray.char
。
val char : (char, int8_unsigned_elt) kind
如上值类型所示,种类为 float16_elt
、float32_elt
和 float64_elt
的 Bigarrays 使用 OCaml 类型 float
进行访问。复数种类 complex32_elt
、complex64_elt
的 Bigarrays 使用 OCaml 类型 Complex.t
进行访问。整数种类的 Bigarrays 使用足够大的最小 OCaml 整数类型来表示数组元素:对于 8 位和 16 位整数 Bigarrays 以及 OCaml 整数 Bigarrays 使用 int
;对于 32 位整数 Bigarrays 使用 int32
;对于 64 位整数 Bigarrays 使用 int64
;对于平台原生整数 Bigarrays 使用 nativeint
。最后,种类为 int8_unsigned_elt
的 Bigarrays 也可以作为字符数组而不是小整数数组进行访问,方法是使用种类值 char
而不是 int8_unsigned
。
val kind_size_in_bytes : ('a, 'b) kind -> int
kind_size_in_bytes k
是用于存储类型为 k
的元素的字节数。
type
c_layout =
| |
C_layout_typ |
type
fortran_layout =
| |
Fortran_layout_typ |
为了便于与现有的 C 和 Fortran 代码进行互操作,此库支持 Bigarrays 的两种不同的内存布局,一种与 C 约定兼容,另一种与 Fortran 约定兼容。
在 C 样式布局中,数组索引从 0 开始,多维数组以行主序格式排列。也就是说,对于二维数组,第 0 行的所有元素在内存中是连续的,之后是第 1 行的所有元素,依此类推。换句话说,位于 (x,y)
和 (x, y+1)
的数组元素在内存中是相邻的。
在 Fortran 样式布局中,数组索引从 1 开始,多维数组以列主序格式排列。也就是说,对于二维数组,第 0 列的所有元素在内存中是连续的,之后是第 1 列的所有元素,依此类推。换句话说,位于 (x,y)
和 (x+1, y)
的数组元素在内存中是相邻的。
每种布局样式都在类型级别由幻像类型 Bigarray.c_layout
和 Bigarray.fortran_layout
分别标识。
GADT 类型 'a layout
表示两种支持的内存布局之一:C 样式或 Fortran 样式。出于向后兼容性的原因,其构造函数作为值重新导出。
type 'a
layout =
| |
C_layout : |
| |
Fortran_layout : |
val c_layout : c_layout layout
val fortran_layout : fortran_layout layout
module Genarray:sig
..end
module Array0:sig
..end
零维数组。
module Array1:sig
..end
一维数组。
module Array2:sig
..end
二维数组。
module Array3:sig
..end
三维数组。
val genarray_of_array0 : ('a, 'b, 'c) Array0.t -> ('a, 'b, 'c) Genarray.t
返回对应于给定零维 Bigarray 的通用 Bigarray。
val genarray_of_array1 : ('a, 'b, 'c) Array1.t -> ('a, 'b, 'c) Genarray.t
返回对应于给定一维 Bigarray 的通用 Bigarray。
val genarray_of_array2 : ('a, 'b, 'c) Array2.t -> ('a, 'b, 'c) Genarray.t
返回对应于给定二维 Bigarray 的通用 Bigarray。
val genarray_of_array3 : ('a, 'b, 'c) Array3.t -> ('a, 'b, 'c) Genarray.t
返回对应于给定三维 Bigarray 的通用 Bigarray。
val array0_of_genarray : ('a, 'b, 'c) Genarray.t -> ('a, 'b, 'c) Array0.t
返回对应于给定通用 Bigarray 的零维 Bigarray。
Invalid_argument
如果通用 Bigarray 的维度不正好为零。val array1_of_genarray : ('a, 'b, 'c) Genarray.t -> ('a, 'b, 'c) Array1.t
返回对应于给定通用 Bigarray 的一维 Bigarray。
Invalid_argument
如果通用 Bigarray 的维度不正好为一。val array2_of_genarray : ('a, 'b, 'c) Genarray.t -> ('a, 'b, 'c) Array2.t
返回对应于给定通用 Bigarray 的二维 Bigarray。
Invalid_argument
如果通用 Bigarray 的维度不正好为二。val array3_of_genarray : ('a, 'b, 'c) Genarray.t -> ('a, 'b, 'c) Array3.t
返回对应于给定通用 Bigarray 的三维 Bigarray。
Invalid_argument
如果通用 Bigarray 的维度不正好为三。val reshape : ('a, 'b, 'c) Genarray.t ->
int array -> ('a, 'b, 'c) Genarray.t
reshape b [|d1;...;dN|]
将 Bigarray b
转换为维度为 d1
...dN
的 N
维数组。返回的数组和原始数组 b
共享数据并具有相同的布局。例如,假设 b
是一个维度为 12 的一维数组,reshape b [|3;4|]
返回一个维度为 3 和 4 的二维数组 b'
。如果 b
具有 C 布局,则 b'
的元素 (x,y)
对应于 b
的元素 x * 3 + y
。如果 b
具有 Fortran 布局,则 b'
的元素 (x,y)
对应于 b
的元素 x + (y - 1) * 4
。返回的 Bigarray 必须与原始 Bigarray b
具有完全相同的元素数量。也就是说,b
的维度的乘积必须等于 i1 * ... * iN
。否则,将引发 Invalid_argument
。
val reshape_0 : ('a, 'b, 'c) Genarray.t -> ('a, 'b, 'c) Array0.t
Bigarray.reshape
的专门版本,用于重塑为零维数组。
val reshape_1 : ('a, 'b, 'c) Genarray.t -> int -> ('a, 'b, 'c) Array1.t
Bigarray.reshape
的专门版本,用于重塑为一维数组。
val reshape_2 : ('a, 'b, 'c) Genarray.t ->
int -> int -> ('a, 'b, 'c) Array2.t
Bigarray.reshape
的专门版本,用于重塑为二维数组。
val reshape_3 : ('a, 'b, 'c) Genarray.t ->
int -> int -> int -> ('a, 'b, 'c) Array3.t
Bigarray.reshape
的专门版本,用于重塑为三维数组。
当从多个域并发访问 bigarray 时,必须小心:访问 bigarray 永远不会导致程序崩溃,但不同步的访问可能会产生令人惊讶的(非顺序一致的)结果。
每个访问多个数组元素的 bigarray 操作都不是原子的。这包括切片、bliting 和填充 bigarray。
例如,考虑以下程序
open Bigarray
let size = 100_000_000
let a = Array1.init Int C_layout size (fun _ -> 1)
let update f a () =
for i = 0 to size - 1 do a.{i} <- f a.{i} done
let d1 = Domain.spawn (update (fun x -> x + 1) a)
let d2 = Domain.spawn (update (fun x -> 2 * x + 1) a)
let () = Domain.join d1; Domain.join d2
执行此代码后,bigarray a
的每个字段都是 2
、3
、4
或 5
。如果需要原子性,则用户必须实现自己的同步(例如,使用 Mutex.t
)。
如果两个域仅访问 bigarray 的不相交部分,则观察到的行为等效于来自两个域的操作的某些顺序交错。
当两个域访问同一个 bigarray 元素而没有同步,并且至少一个访问是写操作时,就会发生数据竞争。在没有数据竞争的情况下,观察到的行为等效于来自不同域的操作的某些顺序交错。
只要可能,应通过使用同步来协调对 bigarray 元素的访问,从而避免数据竞争。
实际上,在存在数据竞争的情况下,程序不会崩溃,但观察到的行为可能不等效于来自不同域的操作的任何顺序交错。
Bigarray 在存在数据竞争的情况下有一个明显的警告:并发 bigarray 操作可能会由于撕裂而产生意外的值。更准确地说,部分写和读的交错可能会创建在顺序执行中不存在的值。例如,在
let res = Array1.init Complex64 c_layout size (fun _ -> Complex.zero)
let d1 = Domain.spawn (fun () -> Array1.fill res Complex.one)
let d2 = Domain.spawn (fun () -> Array1.fill res Complex.i)
let () = Domain.join d1; Domain.join d2
res
bigarray 可能会包含既不是 Complex.i
也不是 Complex.one
的值(例如 1 + i
)。