module Bigarray: Bigarray
Bigarray 可以包含以下几种元素
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 中或从 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
的 Bigarray 使用 OCaml 类型 float
进行访问。复数种类 complex32_elt
、complex64_elt
的 Bigarray 使用 OCaml 类型 Complex.t
进行访问。整数种类的 Bigarray 使用最小的 OCaml 整数类型进行访问,该类型足够大以表示数组元素:对于 8 位和 16 位整数 Bigarray 以及 OCaml 整数 Bigarray 使用 int
;对于 32 位整数 Bigarray 使用 int32
;对于 64 位整数 Bigarray 使用 int64
;对于平台原生整数 Bigarray 使用 nativeint
。最后,种类为 int8_unsigned_elt
的 Bigarray 也可以作为字符数组而不是小整数数组进行访问,方法是使用种类值 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 代码的互操作性,此库支持 Bigarray 的两种不同的内存布局,一种与 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|]
将大数组 b
转换为一个 N
维数组,其维度为 d1
...dN
。返回的数组和原始数组 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
。返回的大数组必须与原始大数组 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
的专门版本,用于重塑为三维数组。
当从多个域并发访问大数组时,必须小心:访问大数组永远不会导致程序崩溃,但未同步的访问可能会产生令人惊讶的(非顺序一致的)结果。
每个访问多个数组元素的大数组操作都不是原子的。这包括切片、blitting 和填充大数组。
例如,考虑以下程序
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
执行此代码后,大数组 a
的每个字段都为 2
、3
、4
或 5
。如果需要原子性,则用户必须实现自己的同步(例如,使用 Mutex.t
)。
如果两个域仅访问大数组的不同部分,则观察到的行为等效于这两个域的操作的某些顺序交错。
当两个域访问同一个大数组元素且没有同步,并且至少一个访问是写入操作时,就会发生数据竞争。在没有数据竞争的情况下,观察到的行为等效于来自不同域的操作的某些顺序交错。
在任何可能的情况下,都应通过使用同步来协调对大数组元素的访问,从而避免数据竞争。
实际上,在存在数据竞争的情况下,程序不会崩溃,但观察到的行为可能不等于来自不同域的操作的任何顺序交错。
大数组在存在数据竞争的情况下有一个明显的警告:并发大数组操作可能会产生令人惊讶的值,因为存在撕裂现象。更准确地说,部分写入和读取的交错可能会创建在顺序执行中不存在的值。例如,在
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
大数组可能包含既不是 Complex.i
也不是 Complex.one
的值(例如 1 + i
)。