bau icon indicating copy to clipboard operation
bau copied to clipboard

Incorporate extbigarray

Open rleonid opened this issue 7 years ago • 2 comments

This set of work aims to incorporate all of Extbigarray's functionality but using monomorphic (consequently faster) functions via bigarray_fold_ppx.

rleonid avatar Feb 05 '18 20:02 rleonid

@hcarty I'd like to get your opinion on this approach. I've used a ppx to write all of the operations where specifying the kind would generate faster code using for loops that do exactly that. Some of the rough performance tests show a 3x-10x speed up on some of these operations, so I think this is a better approach. Then I use cppo to automate the invocation of this ppx with the various combinations. I've only implemented mono and dimorphic (wrt. the Bigarray kind) functions so far and only for Array1:

module Array1 :
  sig
    type ('a, 'b, 'c) t = ('a, 'b, 'c) Bigarray.Array1.t
    val create :
      ('a, 'b) Bigarray.kind -> 'c Bigarray.layout -> int -> ('a, 'b, 'c) t
   (* .... same as original, the new stuff is: *)
    val init :
      ('a, 'b) Bigarray.kind ->
      'c Bau__BigarrayExt.layout -> int -> (int -> 'a) -> ('a, 'b, 'c) t
    val copy : ('a, 'b, 'c) t -> ('a, 'b, 'c) t
    val to_array : f:('a -> 'b) -> ('a, 'c, 'd) t -> 'b array
   (* If you know one kind then use one of these nested modules: *)
    module Float32 : sig ... end
    module Float64 : sig ... end
    module Int8_signed : sig ... end
    module Int8_unsigned : sig ... end
    module Int16_signed : sig ... end
    module Int16_unsigned : sig ... end
    module Int : sig ... end
    module Int32 : sig ... end
    module Int64 : sig ... end
    module Nativeint : sig ... end
    module Complex32 : sig ... end
    module Complex64 : sig ... end
    module Char : sig ... end
  end

and they contain

module Array1.Float32 :
  sig
    val fold_left :
      f:('a -> float -> 'a) ->
      init:'a ->
      (float, float32_elt, 'b) Array1.t -> 'a
    val fold_right :
      f:(float -> 'a -> 'a) ->
      init:'a ->
      (float, float32_elt, 'b) Array1.t -> 'a
    val foldi :
      f:('a -> int -> float -> 'a) ->
      init:'a ->
      (float, float32_elt, 'b) Array1.t -> 'a
    val reduce_left :
      f:(float -> float -> float) ->
      (float, float32_elt, 'a) Array1.t ->
      float
    val reduce_right :
      f:(float -> float -> float) ->
      (float, float32_elt, 'a) Array1.t ->
      float
    val reducei :
      f:(float -> int -> float -> float) ->
      (float, float32_elt, 'a) Array1.t ->
      float
    val iter :
      f:(float -> unit) ->
      (float, float32_elt, 'a) Array1.t ->
      unit
    val iteri :
      f:(int -> float -> unit) ->
      (float, float32_elt, 'a) Array1.t ->
      unit
    val modify :
      f:(float -> float) ->
      (float, float32_elt, 'a) Array1.t ->
      unit
    val modifyi :
      f:(int -> float -> float) ->
      (float, float32_elt, 'a) Array1.t ->
      unit
    val init :
      f:(int -> float) ->
      'a Bigarray.layout ->
      int ->
      (float, float32_elt, 'a) Array1.t
    val to_array :
      (float, float32_elt, 'a) Array1.t ->
      float array
    val of_array :
      float array ->
      'a layout ->
      (float, float32_elt, 'a) Array1.t
   (* When you know the 2nd kind, even more nested modules *)
    module Float32 : sig ... end
    module Float64 : sig ... end
    module Int8_signed : sig ... end
    module Int8_unsigned : sig ... end
    module Int16_signed : sig ... end
    module Int16_unsigned : sig ... end
    module Int : sig ... end
    module Int32 : sig ... end
    module Int64 : sig ... end
    module Nativeint : sig ... end
    module Complex32 : sig ... end
    module Complex64 : sig ... end
    module Char : sig ... end
  end

and those 2x nested modules look like:

module Array1.Float32.Float64 :
  sig
    val fold_left2 :
      f:('a -> float -> float -> 'a) ->
      init:'a ->
      (float, float32_elt, 'b) Array1.t ->
      (float, float64_elt, 'c) Array1.t -> 'a
    val fold_right2 :
      f:(float -> float -> 'a -> 'a) ->
      init:'a ->
      (float, float32_elt, 'b) Array1.t ->
      (float, float64_elt, 'c) Array1.t -> 'a
    val foldi2 :
      f:('a -> int -> float -> float -> 'a) ->
      init:'a ->
      (float, float32_elt, 'b) Array1.t ->
      (float, float64_elt, 'c) Array1.t -> 'a
    val iter2 :
      f:(float -> float -> unit) ->
      (float, float32_elt, 'a) Array1.t ->
      (float, float64_elt, 'b) Array1.t ->
      unit
    val iteri2 :
      f:(int -> float -> float -> unit) ->
      (float, float32_elt, 'a) Array1.t ->
      (float, float64_elt, 'b) Array1.t ->
      unit
    val map :
      f:(float -> float) ->
      (float, float32_elt, 'a) Array1.t ->
      (float, float64_elt, 'a) Array1.t
    val mapi :
      f:(int -> float -> float) ->
      (float, float32_elt, 'a) Array1.t ->
      (float, float64_elt, 'a) Array1.t
  end

All in all writing something like

let norms v =  Array1.Complex64.Float64.map ~f:Complex.norm v

doesn't seem to be that bad. Thoughts?

rleonid avatar Feb 13 '18 18:02 rleonid

That looks good to me! The ppx removes/centralizes most of the potential for typos in the codebase, so it's nice to have that much flexibility in nesting.

I like it.

hcarty avatar Feb 13 '18 20:02 hcarty