* "Ref" and copy of functions
@ 2007-12-13 17:27 Pierre-Evariste Dagand
2007-12-14 10:49 ` Zheng Li
2007-12-16 5:17 ` [Caml-list] " Jacques Garrigue
0 siblings, 2 replies; 12+ messages in thread
From: Pierre-Evariste Dagand @ 2007-12-13 17:27 UTC (permalink / raw)
To: caml-list
Hi Caml-list,
I'm looking for advices about a "clean way" of doing something which,
by design, isn't. So, let states the problem.
I'm doing a combinator library based on Arrows for a Yampa-like system :
type ('a,'b) arrow = Arrow of ( 'a -> 'b )
Thus, I can instantiate an arrow with :
let arr f = Arrow ( f )
val arr : ('a -> 'b) -> ('a, 'b) arrow
Then, I have, for example, the composition of arrows :
let (>>>) (Arrow f) (Arrow g) =Arrow ( fun c -> g ( f c ) )
val ( >>> ) : ('a, 'b) arrow -> ('b, 'c) arrow -> ('a, 'c) arrow
Right here, everything plays well (excepted some weak type variables
issues but that's not a big deal).
But (and that's here that the trouble is) I need a "loop" combinator,
that I wrote this way :
let loop init (Arrow f) =
let state = ref init in
Arrow (
fun c ->
let new_state , output = f ( !state , c ) in
state := new_state;
output
)
val loop : 'a -> ('a * 'b, 'a * 'c) arrow -> ('b, 'c) arrow
Finally, I can write a small arrow :
let arr_counter = loop 0 ( arr ( fun ( counter , x ) -> counter+1 ,
counter+x ) )
let arr_double = arr ( fun x -> 2*x )
let arr_my_small_arrow = arr_counter >>> arr_double
And I execute it with :
let run (Arrow f) input =
f input
val run : ('a, 'b) arrow -> 'a -> 'b
And it works. Right.
But now, I would like to be able to "copy" a built arrow and to be
able to execute the copy without side-effecting on the first one.
Obviously, I cannot do that in this implementation.
My current solution is really *ugly* :
let arrow_string = Marshal.to_string arrow [ Marshal.No_sharing ;
Marshal.Closures ]
Then I "Marshal.from_string arrow_string". I'm not even sure if it
will not blow out of my hands with "strange" things in the Ref cell.
I'm aware of implementations in CPS but I use to think that it has a
performance cost that I'm not ready to pay (I'm fighting against a C++
code so...).
So if someone knows an elegant solution to my problem (if someone has
read this mail until here), please let me know :-)
Best regards,
P.S.: For those who are just curious, these combinators are used in a
functional-reactive library for Peer-to-Peer overlays programming.
--
Pierre-Evariste DAGAND
http://perso.eleves.bretagne.ens-cachan.fr/~dagand/
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: "Ref" and copy of functions
2007-12-13 17:27 "Ref" and copy of functions Pierre-Evariste Dagand
@ 2007-12-14 10:49 ` Zheng Li
2007-12-14 14:51 ` [Caml-list] " David Teller
2007-12-14 14:54 ` [Caml-list] " Pierre-Evariste Dagand
2007-12-16 5:17 ` [Caml-list] " Jacques Garrigue
1 sibling, 2 replies; 12+ messages in thread
From: Zheng Li @ 2007-12-14 10:49 UTC (permalink / raw)
To: caml-list
Hi, "Pierre-Evariste Dagand" <pedagand@gmail.com> writes: > I'm
looking for advices about a "clean way" of doing something which,
> by design, isn't. So, let states the problem. > I'm doing a
combinator library based on Arrows for a Yampa-like system : I
don't understand what "arrow" is and the rational behind it, so
don't take my suggestion seriously. > type ('a,'b) arrow = Arrow
of ( 'a -> 'b ) > let arr f = Arrow ( f ) > val arr : ('a -> 'b)
-> ('a, 'b) arrow > let (>>>) (Arrow f) (Arrow g) =Arrow ( fun c
-> g ( f c ) ) > let loop init (Arrow f) = > let state = ref
init in > Arrow ( > fun c -> > let new_state ,
output = f ( !state , c ) in > state := new_state; >
output > ) > let arr_counter = loop 0 ( arr ( fun ( counter ,
x ) -> counter+1 , > counter+x ) ) > let arr_double = arr ( fun x
-> 2*x ) > let arr_my_small_arrow = arr_counter >>> arr_double >
let run (Arrow f) input = > f input At least on this
particular example, you can use the following encoding (I
deliberately use rectypes here, but you can always use extra
variant instead)
---definition---
(* Only in mind: type ('a,'b) arrow = 'a -> 'b * ('a, 'b) arrow;;
*)
let rec arr f x = f x, arr f;;
let rec (>>>) f g x =
let rf,nf = f x in let rg,ng = g rf in
rg, nf >>> ng;;
let rec loop init f x =
let ((init', r), n) = f (init,x) in
r, loop init' n;;
let rec run f = function
| [] -> []
| h::t -> let r,n = f h in r :: run n t;;
---test---
let arr_counter = loop 0 (arr (fun (c,x) -> c+1,c+x));;
let arr_double = arr (( * ) 2);;
let arr_my_small_arrow = arr_counter >>> arr_double;;
run arr_my_small_arrow [6;5;4;3];;
- : int list = [12; 12; 12; 12]
Since it doesn't use mutable variable, it's free to make copy. >
P.S.: For those who are just curious, these combinators are used
in a > functional-reactive library for Peer-to-Peer overlays
programming. It looks like "arrow" is for some kind of flow-like
programming, then you may also take a look at SDFlow [1]. Your
example can be encoded in a point-free style as
---flow---
let arr_counter =
comb |- map (fun (x,c) -> x+c,c+1) |- split |> curry |-
feedr[<'0>];;
let arr_double = map (( * ) 2);;
let arr_my_small_arrow = arr_counter |- arr_double;;
arr_my_small_arrow [<'6;'5;'4;'3>] |> to_list;;
- : int list = [12; 12; 12; 12]
Note however, SDFlow is just a proof of concept. The syntax on
recursion can be made much more straightforward with some camlp4
script to allow recursive value with preconditions. I just don't
have time to do that.
HTH.
--
Zheng Li
http://www.pps.jussieu.fr/~li
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [Caml-list] Re: "Ref" and copy of functions
2007-12-14 10:49 ` Zheng Li
@ 2007-12-14 14:51 ` David Teller
2007-12-14 16:19 ` Zheng Li
2007-12-14 14:54 ` [Caml-list] " Pierre-Evariste Dagand
1 sibling, 1 reply; 12+ messages in thread
From: David Teller @ 2007-12-14 14:51 UTC (permalink / raw)
To: Caml
Speaking of SDFlow, I've slightly extended it with three functions
to_array, to_stream and of_array. I may extend it a bit further, but I
don't think so. Do you accept submissions ?
I've also made use of SDFlow in a Camlp4 extension for stream
comprehension, by the way. I'll try and publish this within a few days.
Cheers,
David
On Fri, 2007-12-14 at 11:49 +0100, Zheng Li wrote:
> Note however, SDFlow is just a proof of concept. The syntax on
> recursion can be made much more straightforward with some camlp4
> script to allow recursive value with preconditions. I just don't
> have time to do that.
>
> HTH.
--
David Teller
Security of Distributed Systems
http://www.univ-orleans.fr/lifo/Members/David.Teller
Angry researcher: French Universities need reforms, but the LRU act brings liquidations.
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [Caml-list] Re: "Ref" and copy of functions
2007-12-14 10:49 ` Zheng Li
2007-12-14 14:51 ` [Caml-list] " David Teller
@ 2007-12-14 14:54 ` Pierre-Evariste Dagand
2007-12-14 16:12 ` Zheng Li
2007-12-14 16:30 ` Loup Vaillant
1 sibling, 2 replies; 12+ messages in thread
From: Pierre-Evariste Dagand @ 2007-12-14 14:54 UTC (permalink / raw)
To: Zheng Li, caml-list
[-- Attachment #1: Type: text/plain, Size: 5925 bytes --]
Hi,
> At least on this
> particular example, you can use the following encoding (I
> deliberately use rectypes here, but you can always use extra
> variant instead)
>
> (...)
>
> Since it doesn't use mutable variable, it's free to make copy.
Yes, that's a CPS-way of doing things. I am aware of it and my first
implementation was in this style.
The reasons why I decided to switch to Ref were that :
1/ The implementation is much more "intuitive" (I don't have a CPS
pre-processor in my brain)
2/ I thought I could get rid of my hack (Marshal/UnMarshal) easily
3/ I used to think that I will be faster
Point 1 is not a problem, Santa Claus might bring me this CPS
pre-processor in a few days.
Point 2 seems to be wrong : being fast is nice but if it's for hitting
the wall, well... one should slow down. And for the moment, I haven't
found a clean solution and I'm not sure whether my hack is safe or
not.
So, remains point 3. That's why I have carried out an experience on my
initial Ref implementation, your Rectypes implementation and a CPS
implementation without rectypes (because rectypes frighten me).
%%%%%%%%%%%%%%%%%%%
----------------------------
Ref implementation :
----------------------------
type ('a,'b) arrow = Arrow of ( 'a -> 'b )
let arr f = Arrow ( f )
let (>>>) (Arrow f) (Arrow g) =Arrow ( fun c -> g ( f c ) )
let loop init (Arrow f) =
let state = ref init in
Arrow ( fun c ->
let new_state , output = f ( !state , c ) in
state := new_state;
output
)
let run (Arrow f) input =
f input
-----------------------------------
Rectypes implementation :
------------------------------------
let rec arr f x = f x, arr f
let rec (>>>) f g x =
let rf,nf = f x in let rg,ng = g rf in
rg, nf >>> ng
let rec run f x ?(i=0) max_inc =
if i = max_inc then
x
else
let _,n = f x in
run n x ~i:(i+1) max_inc
--------------------------------------
Variant CPS implementation :
--------------------------------------
type ('a,'b) arrow = Arrow of ( 'a -> 'b * ('a,'b) arrow )
let rec arr f = Arrow ( fun x -> f x , arr f );;
let rec (>>>) (Arrow f) (Arrow g) =
Arrow (
fun x ->
let rf,nf = f x in
let rg,ng = g rf in
rg , nf >>> ng
)
let loop init f =
let rec loop_aux c (Arrow f) =
Arrow (
fun x->
let ((c', r), n) = f (c,x) in
r , loop_aux c' n
)
in
loop_aux init f
let rec run (Arrow f) x ?(i=0) max_inc =
if i = max_inc then
x
else
let _,n = f x in
run n x ~i:(i+1) max_inc
%%%%%%%%%%%%%%%%%%%%%
Then I wrote a small test :
%%%%%%%%%%%%%%%%%%%%%
let pair_to_simple (x,y) = x
let arr_pair_to_simple () = arr pair_to_simple
let simple_to_pair x = (x,x)
let arr_simple_to_pair () = arr simple_to_pair
let arr_incr = arr ( fun ( c , x ) -> c + 1 , c + x )
let arr_counter1 = loop 1 arr_incr
let arr_incr1 = (arr_pair_to_simple ()) >>> arr_counter1 >>>
(arr_simple_to_pair ())
let arr_counter2 = loop 2 arr_incr1
let arr_incr2 = (arr_pair_to_simple ()) >>> arr_counter2 >>>
(arr_simple_to_pair ())
let arr_counter3 = loop 3 arr_incr2
let arr_incr3 = (arr_pair_to_simple ()) >>> arr_counter3 >>>
(arr_simple_to_pair ())
let arr_counter4 = loop 4 arr_incr3
let arr_incr4 = (arr_pair_to_simple ()) >>> arr_counter4 >>>
(arr_simple_to_pair ())
let arr_counter5 = loop 5 arr_incr4
let arr_incr5 = (arr_pair_to_simple ()) >>> arr_counter5 >>>
(arr_simple_to_pair ())
let arr_counter6 = loop 6 arr_incr5
let arr_incr6 = (arr_pair_to_simple ()) >>> arr_counter6 >>>
(arr_simple_to_pair ())
let arr_counter7 = loop 7 arr_incr6
let arr_incr7 = (arr_pair_to_simple ()) >>> arr_counter7 >>>
(arr_simple_to_pair ())
let arr_counter8 = loop 8 arr_incr7
let arr_incr8 = (arr_pair_to_simple ()) >>> arr_counter8 >>>
(arr_simple_to_pair ())
let arr_counter9 = loop 9 arr_incr8
let arr_incr9 = (arr_pair_to_simple ()) >>> arr_counter9 >>>
(arr_simple_to_pair ())
let arr_counter10 = loop 10 arr_incr9
let arr_incr10 = (arr_pair_to_simple ()) >>> arr_counter10 >>>
(arr_simple_to_pair ())
let arr_counter11 = loop 11 arr_incr10
let arr_incr11 = (arr_pair_to_simple ()) >>> arr_counter11 >>>
(arr_simple_to_pair ())
let arr_counter12 = loop 12 arr_incr11
let arr_incr12 = (arr_pair_to_simple ()) >>> arr_counter12 >>>
(arr_simple_to_pair ())
let arr_my_arrow () = ( arr_simple_to_pair () ) >>> arr_incr12
%%%%%%%%%%%%%%%%%%%%%%%%
Finally, I measured the processing time with :
%%%%%%%%%%%%%%%%%%%%%%%%
-------------
For Ref
-------------
let _ =
let ntest = 100000 in
let start_time = Unix.gettimeofday () in
let arr_my_arrow = arr_my_arrow () in
for i=0 to ntest-1 do
let _ = run arr_my_arrow 2 in
()
done;
let end_time = Unix.gettimeofday () in
let time = ( end_time -. start_time ) /. (float_of_int ntest) *.
1000000.0 in
Printf.printf "Mean processing time : %f microseconds\n" time;
--------------------
For CPS (both)
--------------------
let _ =
let ntest = 100000 in
let arr_my_arrow = arr_my_arrow () in
let start_time = Unix.gettimeofday () in
let _ = run arr_my_arrow 2 ntest in
();
let end_time = Unix.gettimeofday () in
let time = ( end_time -. start_time ) /. (float_of_int ntest) *.
1000000.0 in
Printf.printf "Mean processing time : %f microseconds\n" time;
%%%%%%%%%%%%%%%%%%%%%%%%%%%%
And the results are ...
1/ Ref : 0.75 microseconds
2/ Variant CPS : 2.69 microseconds
3/ Rectypes : 2.80 microseconds
[ compiled with ocamlopt.opt, with -rectypes for 3/ ]
It's about 4 times longer and, against a C++ code, I fear that I can't
afford it.
> It looks like "arrow" is for some kind of flow-like
> programming, then you may also take a look at SDFlow [1].
Yes, it does. For more info : http://www.haskell.org/arrows/
I will take a look at SDFlow, thanks for the hint.
> HTH.
Thanks,
--
Pierre-Evariste DAGAND
[-- Attachment #2: measure_ref.ml --]
[-- Type: application/octet-stream, Size: 2495 bytes --]
type ('a,'b) arrow = Arrow of ( 'a -> 'b )
let arr f = Arrow ( f )
let (>>>) (Arrow f) (Arrow g) =
Arrow (
fun c ->
g ( f c )
)
let loop init (Arrow f) =
let state = ref init in
Arrow (
fun c ->
let new_state , output = f ( !state , c ) in
state := new_state;
output
)
let run (Arrow f) input =
f input
let pair_to_simple (x,y) = x
let arr_pair_to_simple () = arr pair_to_simple
let simple_to_pair x = (x,x)
let arr_simple_to_pair () = arr simple_to_pair
let arr_incr = arr ( fun ( c , x ) -> c + 1 , c + x )
let arr_counter1 = loop 1 arr_incr
let arr_incr1 = (arr_pair_to_simple ()) >>> arr_counter1 >>> (arr_simple_to_pair ())
let arr_counter2 = loop 2 arr_incr1
let arr_incr2 = (arr_pair_to_simple ()) >>> arr_counter2 >>> (arr_simple_to_pair ())
let arr_counter3 = loop 3 arr_incr2
let arr_incr3 = (arr_pair_to_simple ()) >>> arr_counter3 >>> (arr_simple_to_pair ())
let arr_counter4 = loop 4 arr_incr3
let arr_incr4 = (arr_pair_to_simple ()) >>> arr_counter4 >>> (arr_simple_to_pair ())
let arr_counter5 = loop 5 arr_incr4
let arr_incr5 = (arr_pair_to_simple ()) >>> arr_counter5 >>> (arr_simple_to_pair ())
let arr_counter6 = loop 6 arr_incr5
let arr_incr6 = (arr_pair_to_simple ()) >>> arr_counter6 >>> (arr_simple_to_pair ())
let arr_counter7 = loop 7 arr_incr6
let arr_incr7 = (arr_pair_to_simple ()) >>> arr_counter7 >>> (arr_simple_to_pair ())
let arr_counter8 = loop 8 arr_incr7
let arr_incr8 = (arr_pair_to_simple ()) >>> arr_counter8 >>> (arr_simple_to_pair ())
let arr_counter9 = loop 9 arr_incr8
let arr_incr9 = (arr_pair_to_simple ()) >>> arr_counter9 >>> (arr_simple_to_pair ())
let arr_counter10 = loop 10 arr_incr9
let arr_incr10 = (arr_pair_to_simple ()) >>> arr_counter10 >>> (arr_simple_to_pair ())
let arr_counter11 = loop 11 arr_incr10
let arr_incr11 = (arr_pair_to_simple ()) >>> arr_counter11 >>> (arr_simple_to_pair ())
let arr_counter12 = loop 12 arr_incr11
let arr_incr12 = (arr_pair_to_simple ()) >>> arr_counter12 >>> (arr_simple_to_pair ())
let arr_my_arrow () = ( arr_simple_to_pair () ) >>> arr_incr12;;
let _ =
let ntest = 100000 in
let start_time = Unix.gettimeofday () in
let arr_my_arrow = arr_my_arrow () in
for i=0 to ntest-1 do
let _ = run arr_my_arrow 2 in
()
done;
let end_time = Unix.gettimeofday () in
let time = ( end_time -. start_time ) /. (float_of_int ntest) *. 1000000.0 in
Printf.printf "Mean processing time : %f microseconds\n" time;
[-- Attachment #3: measure_rectypes.ml --]
[-- Type: application/octet-stream, Size: 2434 bytes --]
let rec arr f x = f x, arr f;;
let rec (>>>) f g x =
let rf,nf = f x in let rg,ng = g rf in
rg, nf >>> ng;;
let rec loop init f x =
let ((init', r), n) = f (init,x) in
r, loop init' n;;
let rec run f x ?(i=0) max_inc =
if i = max_inc then
x
else
let _,n = f x in
run n x ~i:(i+1) max_inc
let pair_to_simple (x,y) = x
let arr_pair_to_simple () = arr pair_to_simple
let simple_to_pair x = (x,x)
let arr_simple_to_pair () = arr simple_to_pair
let arr_incr = arr ( fun ( c , x ) -> c + 1 , c + x )
let arr_counter1 = loop 1 arr_incr
let arr_incr1 = (arr_pair_to_simple ()) >>> arr_counter1 >>> (arr_simple_to_pair ())
let arr_counter2 = loop 2 arr_incr1
let arr_incr2 = (arr_pair_to_simple ()) >>> arr_counter2 >>> (arr_simple_to_pair ())
let arr_counter3 = loop 3 arr_incr2
let arr_incr3 = (arr_pair_to_simple ()) >>> arr_counter3 >>> (arr_simple_to_pair ())
let arr_counter4 = loop 4 arr_incr3
let arr_incr4 = (arr_pair_to_simple ()) >>> arr_counter4 >>> (arr_simple_to_pair ())
let arr_counter5 = loop 5 arr_incr4
let arr_incr5 = (arr_pair_to_simple ()) >>> arr_counter5 >>> (arr_simple_to_pair ())
let arr_counter6 = loop 6 arr_incr5
let arr_incr6 = (arr_pair_to_simple ()) >>> arr_counter6 >>> (arr_simple_to_pair ())
let arr_counter7 = loop 7 arr_incr6
let arr_incr7 = (arr_pair_to_simple ()) >>> arr_counter7 >>> (arr_simple_to_pair ())
let arr_counter8 = loop 8 arr_incr7
let arr_incr8 = (arr_pair_to_simple ()) >>> arr_counter8 >>> (arr_simple_to_pair ())
let arr_counter9 = loop 9 arr_incr8
let arr_incr9 = (arr_pair_to_simple ()) >>> arr_counter9 >>> (arr_simple_to_pair ())
let arr_counter10 = loop 10 arr_incr9
let arr_incr10 = (arr_pair_to_simple ()) >>> arr_counter10 >>> (arr_simple_to_pair ())
let arr_counter11 = loop 11 arr_incr10
let arr_incr11 = (arr_pair_to_simple ()) >>> arr_counter11 >>> (arr_simple_to_pair ())
let arr_counter12 = loop 12 arr_incr11
let arr_incr12 = (arr_pair_to_simple ()) >>> arr_counter12 >>> (arr_simple_to_pair ())
let arr_my_arrow () = ( arr_simple_to_pair () ) >>> arr_incr12;;
let _ =
let ntest = 100000 in
let arr_my_arrow = arr_my_arrow () in
let start_time = Unix.gettimeofday () in
let _ = run arr_my_arrow 2 ntest in
();
let end_time = Unix.gettimeofday () in
let time = ( end_time -. start_time ) /. (float_of_int ntest) *. 1000000.0 in
Printf.printf "Mean processing time : %f microseconds\n" time;
[-- Attachment #4: measure_pure_cps.ml --]
[-- Type: application/octet-stream, Size: 2651 bytes --]
type ('a,'b) arrow = Arrow of ( 'a -> 'b * ('a,'b) arrow )
let rec arr f = Arrow ( fun x -> f x , arr f );;
let rec (>>>) (Arrow f) (Arrow g) =
Arrow (
fun x ->
let rf,nf = f x in
let rg,ng = g rf in
rg , nf >>> ng
)
;;
let loop init f =
let rec loop_aux c (Arrow f) =
Arrow (
fun x->
let ((c', r), n) = f (c,x) in
r , loop_aux c' n
)
in
loop_aux init f
;;
let rec run (Arrow f) x ?(i=0) max_inc =
if i = max_inc then
x
else
let _,n = f x in
run n x ~i:(i+1) max_inc
let pair_to_simple (x,y) = x
let arr_pair_to_simple () = arr pair_to_simple
let simple_to_pair x = (x,x)
let arr_simple_to_pair () = arr simple_to_pair
let arr_incr = arr ( fun ( c , x ) -> c + 1 , c + x )
let arr_counter1 = loop 1 arr_incr
let arr_incr1 = (arr_pair_to_simple ()) >>> arr_counter1 >>> (arr_simple_to_pair ())
let arr_counter2 = loop 2 arr_incr1
let arr_incr2 = (arr_pair_to_simple ()) >>> arr_counter2 >>> (arr_simple_to_pair ())
let arr_counter3 = loop 3 arr_incr2
let arr_incr3 = (arr_pair_to_simple ()) >>> arr_counter3 >>> (arr_simple_to_pair ())
let arr_counter4 = loop 4 arr_incr3
let arr_incr4 = (arr_pair_to_simple ()) >>> arr_counter4 >>> (arr_simple_to_pair ())
let arr_counter5 = loop 5 arr_incr4
let arr_incr5 = (arr_pair_to_simple ()) >>> arr_counter5 >>> (arr_simple_to_pair ())
let arr_counter6 = loop 6 arr_incr5
let arr_incr6 = (arr_pair_to_simple ()) >>> arr_counter6 >>> (arr_simple_to_pair ())
let arr_counter7 = loop 7 arr_incr6
let arr_incr7 = (arr_pair_to_simple ()) >>> arr_counter7 >>> (arr_simple_to_pair ())
let arr_counter8 = loop 8 arr_incr7
let arr_incr8 = (arr_pair_to_simple ()) >>> arr_counter8 >>> (arr_simple_to_pair ())
let arr_counter9 = loop 9 arr_incr8
let arr_incr9 = (arr_pair_to_simple ()) >>> arr_counter9 >>> (arr_simple_to_pair ())
let arr_counter10 = loop 10 arr_incr9
let arr_incr10 = (arr_pair_to_simple ()) >>> arr_counter10 >>> (arr_simple_to_pair ())
let arr_counter11 = loop 11 arr_incr10
let arr_incr11 = (arr_pair_to_simple ()) >>> arr_counter11 >>> (arr_simple_to_pair ())
let arr_counter12 = loop 12 arr_incr11
let arr_incr12 = (arr_pair_to_simple ()) >>> arr_counter12 >>> (arr_simple_to_pair ())
let arr_my_arrow () = ( arr_simple_to_pair () ) >>> arr_incr12;;
let _ =
let ntest = 100000 in
let arr_my_arrow = arr_my_arrow () in
let start_time = Unix.gettimeofday () in
let _ = run arr_my_arrow 2 ntest in
();
let end_time = Unix.gettimeofday () in
let time = ( end_time -. start_time ) /. (float_of_int ntest) *. 1000000.0 in
Printf.printf "Mean processing time : %f microseconds\n" time;
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: "Ref" and copy of functions
2007-12-14 14:54 ` [Caml-list] " Pierre-Evariste Dagand
@ 2007-12-14 16:12 ` Zheng Li
2007-12-14 16:55 ` [Caml-list] " Pierre-Evariste Dagand
2007-12-14 16:30 ` Loup Vaillant
1 sibling, 1 reply; 12+ messages in thread
From: Zheng Li @ 2007-12-14 16:12 UTC (permalink / raw)
To: caml-list
Hi,
"Pierre-Evariste Dagand" <pedagand@gmail.com> writes:
> Yes, that's a CPS-way of doing things. I am aware of it and my first
> implementation was in this style.
>
> The reasons why I decided to switch to Ref were that :
> 1/ The implementation is much more "intuitive" (I don't have a CPS
> pre-processor in my brain)
> 2/ I thought I could get rid of my hack (Marshal/UnMarshal) easily
> 3/ I used to think that I will be faster
>
> Point 1 is not a problem, Santa Claus might bring me this CPS
> pre-processor in a few days.
>
> Point 2 seems to be wrong : being fast is nice but if it's for hitting
> the wall, well... one should slow down. And for the moment, I haven't
> found a clean solution and I'm not sure whether my hack is safe or
> not.
I guess you won't be able to find such a solution. In the Ref-based
implementation, the result arrow type is stateful. You won't be able to
_implicitly_ copy a state _inside_ the language, otherwise first-class
continuation would already appear.
A related issue appears on the "arr" function, if the argument "f" is
itself stateful, even with a functional encoding like CPS won't help to
copy the state inside. You can only solve this with a out-of-language
solution such as Marshal or independent process evaluation . However,
they also charge you cost. AFAIR, marshal (native code) has some
cross-module safety problem.
> So, remains point 3. That's why I have carried out an experience on my
> initial Ref implementation, your Rectypes implementation and a CPS
> implementation without rectypes (because rectypes frighten me).
Since the corresponding mechanics in Ref and CPS are mutation and
closure (de)construction, no wonder the former is faster. One ad-hoc
solution, if you only have to provide high-level functional API to
outside, is to make states explicit, like
type ('a,'b) arrow = {mutable state: 'c; eval: 'c -> 'a -> 'b}
This is not valid OCaml, since the 'c type is out of scope. You can
either encode it with existential type (which also involve closure
(de)construction, but I'm not sure about the cost). Or simply using
Obj.t instead of 'c as far as you won't expose it, at least it won't be
unsafer than Marshal and faster.
--
Zheng Li
http://www.pps.jussieu.fr/~li
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: "Ref" and copy of functions
2007-12-14 14:51 ` [Caml-list] " David Teller
@ 2007-12-14 16:19 ` Zheng Li
0 siblings, 0 replies; 12+ messages in thread
From: Zheng Li @ 2007-12-14 16:19 UTC (permalink / raw)
To: caml-list
Hello,
David Teller <David.Teller@ens-lyon.org> writes:
> Speaking of SDFlow, I've slightly extended it with three functions
> to_array, to_stream and of_array. I may extend it a bit further, but I
> don't think so. Do you accept submissions ?
Sure, submissions are always welcome, or you may derive new version
based on it as you like.
> I've also made use of SDFlow in a Camlp4 extension for stream
> comprehension, by the way. I'll try and publish this within a few days.
Glad to know.
Best wishes.
--
Zheng Li
http://www.pps.jussieu.fr/~li
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [Caml-list] Re: "Ref" and copy of functions
2007-12-14 14:54 ` [Caml-list] " Pierre-Evariste Dagand
2007-12-14 16:12 ` Zheng Li
@ 2007-12-14 16:30 ` Loup Vaillant
[not found] ` <6cb897b30712140848j52e5628avbf0e3dadcb771f71@mail.gmail.com>
1 sibling, 1 reply; 12+ messages in thread
From: Loup Vaillant @ 2007-12-14 16:30 UTC (permalink / raw)
To: Pierre-Evariste Dagand; +Cc: Zheng Li, caml-list
2007/12/14, Pierre-Evariste Dagand <pedagand@gmail.com>:
> And the results are ...
> 1/ Ref : 0.75 microseconds
> 2/ Variant CPS : 2.69 microseconds
> 3/ Rectypes : 2.80 microseconds
> [ compiled with ocamlopt.opt, with -rectypes for 3/ ]
>
> It's about 4 times longer and, against a C++ code, I fear that I can't
> afford it.
Err, Is this likely to be a bottoleneck? Or do you want it to be a
library, so it has to be fast anyway?
Loup
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [Caml-list] Re: "Ref" and copy of functions
2007-12-14 16:12 ` Zheng Li
@ 2007-12-14 16:55 ` Pierre-Evariste Dagand
0 siblings, 0 replies; 12+ messages in thread
From: Pierre-Evariste Dagand @ 2007-12-14 16:55 UTC (permalink / raw)
To: Zheng Li, caml-list
> I guess you won't be able to find such a solution. In the Ref-based
> implementation, the result arrow type is stateful. You won't be able to
> _implicitly_ copy a state _inside_ the language, otherwise first-class
> continuation would already appear.
That's why I asked this to the Caml-list : to find a black magic recipe :-)
> Since the corresponding mechanics in Ref and CPS are mutation and
> closure (de)construction, no wonder the former is faster. One ad-hoc
> solution, if you only have to provide high-level functional API to
> outside, is to make states explicit, like
>
> type ('a,'b) arrow = {mutable state: 'c; eval: 'c -> 'a -> 'b}
>
> This is not valid OCaml, since the 'c type is out of scope. You can
> either encode it with existential type (which also involve closure
> (de)construction, but I'm not sure about the cost). Or simply using
> Obj.t instead of 'c as far as you won't expose it, at least it won't be
> unsafer than Marshal and faster.
This solution looks promising, I will look at it and see if it suits my needs.
Thanks,
--
Pierre-Evariste DAGAND
^ permalink raw reply [flat|nested] 12+ messages in thread
* [Caml-list] Re: "Ref" and copy of functions
[not found] ` <6cb897b30712140848j52e5628avbf0e3dadcb771f71@mail.gmail.com>
@ 2007-12-14 16:57 ` Pierre-Evariste Dagand
0 siblings, 0 replies; 12+ messages in thread
From: Pierre-Evariste Dagand @ 2007-12-14 16:57 UTC (permalink / raw)
To: caml-list
[I forgot to cc the list, sorry]
> Err, Is this likely to be a bottoleneck? Or do you want it to be a
> library, so it has to be fast anyway?
Indeed, it will be a library and I would like to get it the faster I can.
These arrows can be connected to :
1/ A simulator that simulates a whole network (target size : 10.000 nodes)
2/ A network interface that connects the arrow with the World
3/ If I have enough time, a debugger and a model checker
In the 2/ case, I'm looking for documentation about the granularity of
the Unix/Windows threads switching (as I rely on threads to interact
with the world).
For the other cases, the faster, the better.
--
Pierre-Evariste DAGAND
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [Caml-list] "Ref" and copy of functions
2007-12-13 17:27 "Ref" and copy of functions Pierre-Evariste Dagand
2007-12-14 10:49 ` Zheng Li
@ 2007-12-16 5:17 ` Jacques Garrigue
2007-12-16 16:39 ` Pierre-Evariste Dagand
1 sibling, 1 reply; 12+ messages in thread
From: Jacques Garrigue @ 2007-12-16 5:17 UTC (permalink / raw)
To: pedagand; +Cc: caml-list
From: "Pierre-Evariste Dagand" <pedagand@gmail.com>
> I'm looking for advices about a "clean way" of doing something which,
> by design, isn't. So, let states the problem.
>
> I'm doing a combinator library based on Arrows for a Yampa-like system :
>
> type ('a,'b) arrow = Arrow of ( 'a -> 'b )
[...]
> But now, I would like to be able to "copy" a built arrow and to be
> able to execute the copy without side-effecting on the first one.
> Obviously, I cannot do that in this implementation.
Here is yet another solution, using objects, which actually combines
Zheng's and Oleg's ideas. That is, it separates state and function,
but provides only access to state through a cloning method, so that it
is completely type safe. This is just what objects are good at!
class ['a,'b] arrow (f : 'a -> 'b) =
object (self) method call = f method copy = self end
let (>>>) rf rg : ('a,'b) arrow =
object
val rf : ('a,'c) arrow = rf
val rg : ('c,'b) arrow = rg
method call x = rg#call (rf#call x)
method copy = {< rf = rf#copy; rg = rg#copy >}
end
let loop init rf : ('b,'c) arrow =
object
val mutable state = init
val rf : ('a*'b,'a*'c) arrow = rf
method call x =
let state', y = rf#call (state, x) in
state <- state'; y
method copy = {< rf = rf#copy >}
end
let arr = new arrow
let arr_counter = loop 0 (arr (fun (counter,x) -> counter+1, counter+x))
let arr_double = arr (fun x -> 2*x)
let arr_my_small_arrow = arr_counter >>> arr_double
The key here is the {< ... >} construct, which creates a shallow copy
of an object, eventually with some fields changed. As a result, in
loop there is no need to handle the state explicitly: the state field
in the copied object will be distinct from the state field in the
original object. On the other hand, you must explicitly update fields
holding arrows, since the copy is shallow. Note that the explicit
"val" fields are needed to allow updating their contents when copying.
One difference with Oleg's approach is that we take a copy of the
original object, rather than creating a completely new record. In this
case, this doesn't mean much, since there is no extra computation
involved. Still, the state after copying is not the original but the
current one. And this may matter more if the construction is more
complicated.
Jacques Garrigue
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [Caml-list] "Ref" and copy of functions
2007-12-16 5:17 ` [Caml-list] " Jacques Garrigue
@ 2007-12-16 16:39 ` Pierre-Evariste Dagand
2007-12-16 18:27 ` Pierre-Evariste Dagand
0 siblings, 1 reply; 12+ messages in thread
From: Pierre-Evariste Dagand @ 2007-12-16 16:39 UTC (permalink / raw)
To: caml-list
> Here is yet another solution, using objects, which actually combines
> Zheng's and Oleg's ideas. That is, it separates state and function,
> but provides only access to state through a cloning method, so that it
> is completely type safe. This is just what objects are good at!
>
> class ['a,'b] arrow (f : 'a -> 'b) =
> object (self) method call = f method copy = self end
Yet another nice solution I would never have imagined :-)
And the performance is quite good :
1.428032 microseconds on my small benchmark
(the unsafe Ref implantation takes 0.75 microseconds while the CPS
implantation takes 2.7 microseconds).
Nevertheless, I have carried out my benchmark on Oleg's implementation
and find an impressive performance :
0.74 microseconds !
Now, I will re-write my combinators and see which style better suits my needs.
Thanks,
--
Pierre-Evariste DAGAND
http://perso.eleves.bretagne.ens-cachan.fr/~dagand/
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [Caml-list] "Ref" and copy of functions
2007-12-16 16:39 ` Pierre-Evariste Dagand
@ 2007-12-16 18:27 ` Pierre-Evariste Dagand
0 siblings, 0 replies; 12+ messages in thread
From: Pierre-Evariste Dagand @ 2007-12-16 18:27 UTC (permalink / raw)
To: caml-list
> Now, I will re-write my combinators and see which style better suits my needs.
Finally, impressed by the speed of the record solution, I have
re-written the whole library with records.
I have measured the performance of this implementation against the
Ref-based one on "real" code. Thus, I have simulated my Chord [1]
implementation with a simulator instrumented such that it drops the
processing time for each arrow processing.
The results can be found at [2].
In a nutshell : the record solution has a negligible overhead compared
to the Ref solution.
Thanks all,
[1]: http://pdos.csail.mit.edu/chord/
[2]: http://perso.eleves.bretagne.ens-cachan.fr/~dagand/projets/opis/processing_speed_records.pdf
--
Pierre-Evariste DAGAND
^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2007-12-16 18:27 UTC | newest]
Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2007-12-13 17:27 "Ref" and copy of functions Pierre-Evariste Dagand
2007-12-14 10:49 ` Zheng Li
2007-12-14 14:51 ` [Caml-list] " David Teller
2007-12-14 16:19 ` Zheng Li
2007-12-14 14:54 ` [Caml-list] " Pierre-Evariste Dagand
2007-12-14 16:12 ` Zheng Li
2007-12-14 16:55 ` [Caml-list] " Pierre-Evariste Dagand
2007-12-14 16:30 ` Loup Vaillant
[not found] ` <6cb897b30712140848j52e5628avbf0e3dadcb771f71@mail.gmail.com>
2007-12-14 16:57 ` Pierre-Evariste Dagand
2007-12-16 5:17 ` [Caml-list] " Jacques Garrigue
2007-12-16 16:39 ` Pierre-Evariste Dagand
2007-12-16 18:27 ` Pierre-Evariste Dagand
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox