1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490
|
(*
* Copyright (c) 2013 Jeremy Yallop.
*
* This file is distributed under the terms of the MIT License.
* See the file LICENSE for details.
*)
module Std_array = Array
type 'a std_array = 'a array
open OUnit
open Ctypes
module BA = Bigarray
let testlib = Dl.(dlopen ~filename:"clib/test_functions.so" ~flags:[RTLD_NOW])
let array_of_list2 typ list2 =
let dim2 = List.length (List.hd list2) in
let atyp = array dim2 typ in
Array.of_list atyp (List.map (Array.of_list typ) list2)
let array_of_list3 typ list3 =
let dim2 = List.length (List.hd list3)
and dim3 = List.length (List.hd (List.hd list3)) in
let atyp = array dim2 (array dim3 typ) in
Array.of_list atyp (List.map (array_of_list2 typ) list3)
let list2_of_array array =
List.map Array.to_list (Array.to_list array)
let matrix l = bigarray_of_array array2 BA.float64 (array_of_list2 double l)
let unmatrix m = list2_of_array (array_of_bigarray array2 m)
let castp typ p = from_voidp typ (to_voidp p)
(*
View ctypes-managed memory through a bigarray lens.
*)
let test_bigarray_of_ctypes_array () =
(* One-dimensional Genarrays *)
let a1 = Array.of_list int8_t [10; 20; 30; 40] in
let b1 = bigarray_of_array genarray BA.int8_signed a1 in
let () = begin
assert_equal (Array.length a1) (BA.Genarray.nth_dim b1 0);
for i = 0 to Array.length a1 - 1 do
assert_equal a1.(i) (BA.Genarray.get b1 [|i|])
done
end in
(* Array1 *)
let eps32 = 1e-6 in
let complex32_eq =
let open Complex in
fun { re = lre; im = lim } { re = rre; im = rim } ->
abs_float (lre -. rre) < eps32 && abs_float (lim -. rim) < eps32 in
let a2 = Array.of_list complex32
Complex.([{re = 0.1; im = 1.0};
{re = 0.2; im = 2.0};
{re = 0.3; im = 3.0};
{re = 0.4; im = 4.0}]) in
let b2 = bigarray_of_array array1 BA.complex32 a2 in
let () = begin
assert_equal (Array.length a2) (BA.Array1.dim b2);
for i = 0 to Array.length a2 - 1 do
assert_equal a2.(i) b2.{i} ~cmp:complex32_eq
done
end in
(* Two-dimensional Genarrays *)
let uint16 = view uint16_t
~read:Unsigned.UInt16.to_int ~write:Unsigned.UInt16.of_int in
let a3 = array_of_list2 uint16
[[5; 10; 15];
[3; 6; 9];
[2; 4; 6];
[1; 2; 3]] in
let b3 = BA.reshape (bigarray_of_array genarray BA.int16_unsigned
(Array.from_ptr
(castp uint16 (Array.start a3)) 12))
[| 4; 3 |] in
let () = begin
assert_equal (Array.length a3) (BA.Genarray.nth_dim b3 0);
assert_equal (Array.length a3.(0)) (BA.Genarray.nth_dim b3 1);
for i = 0 to Array.length a3 - 1 do
for j = 0 to Array.length a3.(0) - 1 do
assert_equal a3.(i).(j) (BA.Genarray.get b3 [|i; j|])
done
done
end in
(* Array2 *)
let a4 = array_of_list2 nativeint [[5n; 10n]; [3n; 6n]; [1n; 2n]] in
let b4 = bigarray_of_array array2 BA.nativeint a4 in
let () = begin
assert_equal (Array.length a4) (BA.Array2.dim1 b4);
assert_equal (Array.length a4.(0)) (BA.Array2.dim2 b4);
for i = 0 to Array.length a4 - 1 do
for j = 0 to Array.length a4.(0) - 1 do
assert_equal a4.(i).(j) b4.{i, j}
done
done
end in
(* Three-dimensional Genarrays *)
let a5 = array_of_list3 int64_t
[[[1L; 2L; 3L; 4L; 5L];
[2L; 4L; 6L; 8L; 10L]];
[[10L; 20L; 30L; 40L; 50L];
[20L; 40L; 60L; 80L; 100L]];
[[100L; 200L; 300L; 400L; 500L];
[200L; 400L; 600L; 800L; 1000L]]] in
let b5 = BA.reshape
(bigarray_of_array genarray BA.int64
(Array.from_ptr (castp int64_t (Array.start a5)) 30))
[| 3; 2; 5 |] in
let () = begin
assert_equal (Array.length a5) (BA.Genarray.nth_dim b5 0);
assert_equal (Array.length a5.(0)) (BA.Genarray.nth_dim b5 1);
assert_equal (Array.length a5.(0).(0)) (BA.Genarray.nth_dim b5 2);
for i = 0 to Array.length a5 - 1 do
for j = 0 to Array.length a5.(0) - 1 do
for k = 0 to Array.length a5.(0).(0) - 1 do
assert_equal a5.(i).(j).(k) (BA.Genarray.get b5 [|i; j; k|])
done
done
done
end in
(* Array3 *)
let a6 = array_of_list3 double
[[[1.; 2.; 3.; 4.];
[2.; 4.; 6.; 8.]];
[[10.; 20.; 30.; 40.];
[20.; 40.; 60.; 80.]];
[[100.; 200.; 300.; 400.];
[200.; 400.; 600.; 800.]]] in
let b6 = bigarray_of_array array3 BA.float64 a6 in
let () = begin
assert_equal (Array.length a6) (BA.Array3.dim1 b6);
assert_equal (Array.length a6.(0)) (BA.Array3.dim2 b6);
assert_equal (Array.length a6.(0).(0)) (BA.Array3.dim3 b6);
for i = 0 to Array.length a6 - 1 do
for j = 0 to Array.length a6.(0) - 1 do
for k = 0 to Array.length a6.(0).(0) - 1 do
assert_equal a6.(i).(j).(k) b6.{i, j, k}
done
done
done
end in
()
(*
View bigarray-managed memory through a ctypes lens
*)
let test_ctypes_array_of_bigarray () =
(* One-dimensional Genarrays *)
let b1_dim = 6 in
let b1 = BA.(Genarray.create float32 c_layout) [| b1_dim |] in
let a1 = array_of_bigarray genarray b1 in
begin
assert_equal (BA.Genarray.nth_dim b1 0) (Array.length a1);
List.iteri (fun i -> BA.Genarray.set b1 [| i |])
[ 6.; 5.; 4.; 3.; 2.; 1. ];
for i = 0 to b1_dim - 1 do
assert_equal (BA.Genarray.get b1 [| i |]) a1.(i)
done
end;
(* Array1 *)
let b2_dim = 7 in
let b2 = BA.(Array1.create int8_unsigned c_layout) b2_dim in
let a2 = array_of_bigarray array1 b2 in
begin
assert_equal (BA.Array1.dim b2) (Array.length a2);
List.iteri (fun i ->
fun v -> b2.{i} <- v)
[ 2; 4; 6; 8; 10; 12; 14 ];
for i = 0 to b2_dim - 1 do
assert_equal b2.{i} a2.(i)
done
end;
(* Two-dimensional Genarrays *)
let b3_dim1 = 4 and b3_dim2 = 2 in
let b3 = BA.(Genarray.create int16_signed c_layout) [| b3_dim1; b3_dim2 |] in
let a3 = Array.from_ptr
(castp (array b3_dim2 int16_t) (bigarray_start genarray b3))
b3_dim1 in
begin
assert_equal (BA.Genarray.nth_dim b3 0) (Array.length a3);
assert_equal (BA.Genarray.nth_dim b3 1) (Array.length a3.(0));
List.iteri (fun i ->
List.iteri (fun j ->
BA.Genarray.set b3 [| i; j |]))
[[-1; -2];
[-3; -4];
[-5; -6];
[-7; -8]];
for i = 0 to b3_dim1 - 1 do
for j = 0 to b3_dim2 - 1 do
assert_equal (BA.Genarray.get b3 [| i; j |]) a3.(i).(j)
done
done
end;
(* Array2 *)
let b4_dim1 = 3 and b4_dim2 = 4 in
let b4 = BA.(Array2.create int32 c_layout) b4_dim1 b4_dim2 in
let a4 = array_of_bigarray array2 b4 in
begin
assert_equal (BA.Array2.dim1 b4) (Array.length a4);
assert_equal (BA.Array2.dim2 b4) (Array.length a4.(0));
List.iteri (fun i ->
List.iteri (fun j ->
fun v -> b4.{i, j} <- v))
[[17l; 15l; 13l; 11l];
[9l; 7l; 5l; 3l];
[1l; -1l; -3l; -5l]];
for i = 0 to b4_dim1 - 1 do
for j = 0 to b4_dim2 - 1 do
assert_equal b4.{i, j} a4.(i).(j)
done
done
end;
(* Three-dimensional Genarrays *)
let b5_dim1 = 4 and b5_dim2 = 2 and b5_dim3 = 5 in
let b5 = BA.(Genarray.create int c_layout) [| b5_dim1; b5_dim2; b5_dim3 |] in
let a5 = Array.from_ptr
(castp (array b5_dim2 (array b5_dim3 camlint)) (bigarray_start genarray b5))
b5_dim1 in
begin
assert_equal
(BA.Genarray.nth_dim b5 0) (Array.length a5);
assert_equal
(BA.Genarray.nth_dim b5 1) (Array.length a5.(0));
assert_equal
(BA.Genarray.nth_dim b5 2) (Array.length a5.(0).(0));
List.iteri (fun i ->
List.iteri (fun j ->
List.iteri (fun k ->
BA.Genarray.set b5 [| i; j; k |])))
[[[1; 2; 3; 4; 5];
[6; 7; 8; 9; 10]];
[[11; 12; 13; 14; 15];
[16; 17; 18; 19; 20]];
[[21; 22; 23; 24; 25];
[26; 27; 28; 29; 30]];
[[31; 32; 33; 34; 35];
[36; 37; 38; 39; 40]]];
for i = 0 to b5_dim1 - 1 do
for j = 0 to b5_dim2 - 1 do
for k = 0 to b5_dim3 - 1 do
assert_equal
(BA.Genarray.get b5 [| i; j; k |]) a5.(i).(j).(k)
done
done
done
end;
(* Array3 *)
let eps64 = 1e-12 in
let complex64_eq =
let open Complex in
fun { re = lre; im = lim } { re = rre; im = rim } ->
abs_float (lre -. rre) < eps64 && abs_float (lim -. rim) < eps64 in
let b6_dim1 = 3 and b6_dim2 = 4 and b6_dim3 = 2 in
let b6 = BA.(Array3.create complex64 c_layout) b6_dim1 b6_dim2 b6_dim3 in
let a6 = array_of_bigarray array3 b6 in
begin
assert_equal (BA.Array3.dim1 b6) (Array.length a6);
assert_equal (BA.Array3.dim2 b6) (Array.length a6.(0));
assert_equal (BA.Array3.dim3 b6) (Array.length a6.(0).(0));
let open Complex in
List.iteri (fun i ->
List.iteri (fun j ->
List.iteri (fun k ->
fun v -> b6.{i, j, k} <- v)))
[[[{re = 1.; im = 10.}; {re = 1e2; im = 0.0}];
[{re = 2.; im = 20.}; {re = 2e2; im = 0.0}];
[{re = 3.; im = 30.}; {re = 3e2; im = 0.0}];
[{re = 4.; im = 40.}; {re = 4e2; im = 0.0}]];
[[{re = 5.; im = 50.}; {re = 5e2; im = 0.1}];
[{re = 6.; im = 60.}; {re = 6e2; im = 0.1}];
[{re = 7.; im = 70.}; {re = 7e2; im = 0.1}];
[{re = 8.; im = 80.}; {re = 8e2; im = 0.1}]];
[[{re = 9.; im = 90.}; {re = 9e2; im = 0.2}];
[{re = 10.; im = 100.}; {re = 1e3; im = 0.2}];
[{re = 11.; im = 110.}; {re = 1.1e3; im = 0.2}];
[{re = 12.; im = 120.}; {re = 1.2e3; im = 0.2}]]];
for i = 0 to b6_dim1 - 1 do
for j = 0 to b6_dim2 - 1 do
for k = 0 to b6_dim3 - 1 do
assert_equal b6.{i, j, k} a6.(i).(j).(k)
~cmp:complex64_eq
done
done
done
end
(*
Test passing bigarrays to c functions.
*)
let test_passing_bigarrays () =
let matrix_mul = Foreign.foreign "matrix_mul" ~from:testlib
(int @-> int @-> int @->
ptr double @-> ptr double @-> ptr double @->
returning void) in
let mul l r =
let m = BA.Array2.dim1 l and n = BA.Array2.dim2 l in
let o = BA.Array2.dim1 r and p = BA.Array2.dim2 r in
assert (n = o);
let product = BA.(Array2.(create (kind l)) c_layout) m p in
let addr = bigarray_start array2 in
matrix_mul m n p (addr l) (addr r) (addr product);
product in
assert_equal
[[-6.; 11.];
[-3.; -3.]]
(unmatrix
(mul
(matrix [[1.; 6.];
[9.; 3.]])
(matrix [[ 0.; -1.];
[-1.; 2.]])));
assert_equal
[[460.; 520.; 580.; 640.; 700.];
[1000.; 1150.; 1300.; 1450.; 1600.]]
(unmatrix (mul
(matrix [[10.; 20.; 30.];
[40.; 50.; 60.]])
(matrix [[ 1.; 2.; 3.; 4.; 5.];
[ 6.; 7.; 8.; 9.; 10.];
[11.; 12.; 13.; 14.; 15.]])))
(*
Test returning bigarrays from c functions.
*)
let test_returning_bigarrays () =
let matrix_transpose = Foreign.foreign "matrix_transpose" ~from:testlib
(int @-> int @-> ptr double @-> returning (ptr double)) in
let transpose m =
(* For the purposes of the test we'll just leak the allocated memory. *)
let rows = BA.Array2.dim1 m and cols = BA.Array2.dim2 m in
bigarray_of_ptr array2 (cols, rows) BA.float64
(matrix_transpose rows cols (bigarray_start array2 m)) in
assert_equal
[[25.; 1.];
[15.; 2.];
[10.; 3.];
[ 5.; 4.];
[ 0.; 5.]]
(unmatrix
(transpose
(matrix [[25.; 15.; 10.; 5.; 0.];
[ 1.; 2.; 3.; 4.; 5.]])))
(*
Test that bigarrays are not collected while there's a ctypes pointer pointing
into them.
*)
let test_bigarray_lifetime_with_ctypes_reference () =
let state = ref `Not_safe_to_collect in
let finalise ba =
begin
assert_equal `Safe_to_collect !state;
assert_equal 1 ba.{0, 0};
state := `Collected;
end
in
let () =
let pointer =
(* Allocate a bigarray and attach a ctypes pointer *)
let ba = Bigarray.(Array2.create int c_layout) 1024 1024 in
begin
ba.{0,0} <- 1;
Gc.finalise finalise ba;
bigarray_start array2 ba
end
in
(* The bigarray is out of scope, but the ctypes object is still live, so
the memory shouldn't be reclaimed. *)
begin
Gc.major ();
Gc.major ();
assert_equal !state `Not_safe_to_collect;
assert_equal 1 !@pointer;
end
in
(* Both the bigarray and the ctypes object are unreachable, so the finaliser
should (or, at least, could) run. *)
begin
state := `Safe_to_collect;
Gc.major ();
Gc.major ();
assert_equal !state `Collected
end
(*
Test that ctypes-allocated memory is not collected while there's a bigarray
associated with it.
*)
let test_ctypes_memory_lifetime_with_bigarray_reference () =
let state = ref `Not_safe_to_collect in
let finalise a =
begin
assert_equal `Safe_to_collect !state;
assert_equal [1L; 2L; 3L; 4L; 5L] (Array.to_list a);
state := `Collected
end
in
let () =
(* Allocate a chunk of ctypes-managed memory, and view it as a bigarray *)
let ba =
let a = Array.make ~finalise int64_t 5 in
begin
for i = 0 to 4 do a.(i) <- Int64.(add (of_int i) one) done;
bigarray_of_array array1 BA.int64 a
end
in
(* The ctypes object is out of scope, but the bigarray is still live, so
the memory shouldn't be reclaimed. *)
begin
Gc.major ();
Gc.major ();
assert_equal !state `Not_safe_to_collect;
assert_equal ba.{0} 1L;
assert_equal ba.{3} 4L;
end
in
(* Both the ctypes object and the bigarray are unreachable, so the finaliser
should (or, at least, could) run. *)
begin
state := `Safe_to_collect;
Gc.major ();
Gc.major ();
assert_equal !state `Collected
end
let suite = "Bigarray tests" >:::
[
"View ctypes-managed memory using bigarrays"
>:: test_bigarray_of_ctypes_array;
"View bigarray-managed memory using ctypes"
>:: test_ctypes_array_of_bigarray;
"Bigarrays live at least as long as ctypes references to them"
>:: test_bigarray_lifetime_with_ctypes_reference;
"Ctypes-allocated memory lives while there's a bigarray reference to it"
>:: test_ctypes_memory_lifetime_with_bigarray_reference;
"Passing bigarrays to C"
>:: test_passing_bigarrays;
"Returning bigarrays from C"
>:: test_returning_bigarrays;
]
let _ =
run_test_tt_main suite
|