模組 Stdlib.Bigarray

module Bigarray: Bigarray

元素種類

Bigarray 可以包含以下種類的元素

每個元素種類在型別層級上都由以下定義的 *_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 : (float, float32_elt) kind
| Float64 : (float, float64_elt) kind
| Int8_signed : (int, int8_signed_elt) kind
| Int8_unsigned : (int, int8_unsigned_elt) kind
| Int16_signed : (int, int16_signed_elt) kind
| Int16_unsigned : (int, int16_unsigned_elt) kind
| Int32 : (int32, int32_elt) kind
| Int64 : (int64, int64_elt) kind
| Int : (int, int_elt) kind
| Nativeint : (nativeint, nativeint_elt) kind
| Complex32 : (Complex.t, complex32_elt) kind
| Complex64 : (Complex.t, complex64_elt) kind
| Char : (char, int8_unsigned_elt) kind
| Float16 : (float, float16_elt) kind

每個元素種類都關聯一個 OCaml 型別,它是可以儲存在 Bigarray 中或從 Bigarray 中讀回的 OCaml 值的型別。這個型別不一定與陣列元素本身的型別相同:例如,一個元素種類為 float32_elt 的 Bigarray 包含 32 位元單精度浮點數,但從 OCaml 讀取或寫入其中一個元素時,會使用 OCaml 型別 float,它是 64 位元雙精度浮點數。

GADT 型別 ('a, 'b) kind 捕獲了 OCaml 型別 'a 與在 Bigarray 中讀寫的值,以及代表 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_eltfloat32_eltfloat64_elt 的 Bigarray 是使用 OCaml 型別 float 存取的。複數種類 complex32_eltcomplex64_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_layoutBigarray.fortran_layout 識別。

支援的配置

GADT 型別 'a layout 代表兩種支援的記憶體配置之一:C 風格或 Fortran 風格。它的建構子在下方重新匯出為值,以確保向後相容性。

type 'a layout = 
| C_layout : c_layout layout
| Fortran_layout : fortran_layout 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

三維陣列。

通用 Bigarray 和固定維度 Bigarray 之間的轉換

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。

val array1_of_genarray : ('a, 'b, 'c) Genarray.t -> ('a, 'b, 'c) Array1.t

傳回對應於給定通用 Bigarray 的一維 Bigarray。

val array2_of_genarray : ('a, 'b, 'c) Genarray.t -> ('a, 'b, 'c) Array2.t

傳回對應於給定通用 Bigarray 的二維 Bigarray。

val array3_of_genarray : ('a, 'b, 'c) Genarray.t -> ('a, 'b, 'c) Array3.t

傳回對應於給定通用 Bigarray 的三維 Bigarray。

重新塑造 Bigarray

val reshape : ('a, 'b, 'c) Genarray.t ->
int array -> ('a, 'b, 'c) Genarray.t

reshape b [|d1;...;dN|] 將 Bigarray b 轉換為維度為 d1...dNN 維陣列。返回的陣列與原始陣列 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 永遠不會使程式崩潰,但是未同步的存取可能會產生令人驚訝的(非循序一致)結果。

原子性

每個存取多個陣列元素的 bigarray 操作都不是原子操作。這包括切片、複製和填充 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,就是 345。如果需要原子性,則使用者必須實作自己的同步機制(例如,使用 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)。