caml-list - the Caml user's mailing list
 help / color / mirror / Atom feed
* Question about float refs.
@ 2010-08-19 11:52 Ethan Burns
  2010-08-19 13:14 ` [Caml-list] " Lukasz Stafiniak
                   ` (2 more replies)
  0 siblings, 3 replies; 14+ messages in thread
From: Ethan Burns @ 2010-08-19 11:52 UTC (permalink / raw)
  To: caml-list; +Cc: Wheeler Ruml

Hello,

I noticed a strange behavior when using references to floating point
values.  My suspicion, initially, was that the references would not be
boxed because they are the same as a record with a single mutable
field that is of type float.  It seems that OCaml still boxes float
refs.  Consider the following two pieces of code:

let r = ref 0.0 ;;
for i = 0 to 1000000000 do r := float i done;
Printf.printf "%f\n" !r;
Printf.printf "words: %f\n" (Gc.stat ()).Gc.minor_words
---------------
$ time ./a.out
1000000000.000000
words: 2000000373.000000
10.073 secs

This seems to perform a lot of allocations and it certainly takes a
long time to run.  But, now, if we use a record instead:

type float_ref = { mutable data : float }
let r = { data = 0.0 } ;;
for i = 0 to 1000000000 do r.data <- float i done;
Printf.printf "%f\n" r.data;
Printf.printf "words: %f\n" (Gc.stat ()).Gc.minor_words
---------------
$ time ./a.out
1000000000.000000
words: 373.000000
0.959 secs

It runs *much* quicker and hardly allocates anything.  My question is:
is there something better than creating a special float_ref type every
time I would like a reference to a floating point number?  Typically I
want a reference to something because it is going to change a lot in a
loop and then be accessed after the loop is complete so it is
undesirable for an allocation to occur each time the reference
changes.

Thanks in advance for any help,

Ethan


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [Caml-list] Question about float refs.
  2010-08-19 11:52 Question about float refs Ethan Burns
@ 2010-08-19 13:14 ` Lukasz Stafiniak
  2010-08-19 13:34 ` David House
  2010-08-23 12:06 ` Christophe TROESTLER
  2 siblings, 0 replies; 14+ messages in thread
From: Lukasz Stafiniak @ 2010-08-19 13:14 UTC (permalink / raw)
  To: Ethan Burns; +Cc: caml-list, Wheeler Ruml

On Thu, Aug 19, 2010 at 1:52 PM, Ethan Burns <burns.ethan@gmail.com> wrote:
> Hello,
>
> I noticed a strange behavior when using references to floating point
> values.  My suspicion, initially, was that the references would not be
> boxed because they are the same as a record with a single mutable
> field that is of type float.  It seems that OCaml still boxes float
> refs.

Note that refs are just a standard polymorphic type defined in Pervasives:

  type 'a ref = {
    mutable contents : 'a ;
  }

Being polymorphic, the "float ref" are not unboxed: imagine what would
happen when you passed them to a function

foo : 'a ref -> 'a list = fun x -> [ !x ]

or similar (with a negative occurrence of polymorphic ref).


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [Caml-list] Question about float refs.
  2010-08-19 11:52 Question about float refs Ethan Burns
  2010-08-19 13:14 ` [Caml-list] " Lukasz Stafiniak
@ 2010-08-19 13:34 ` David House
  2010-08-19 13:49   ` Till Varoquaux
  2010-08-23 12:06 ` Christophe TROESTLER
  2 siblings, 1 reply; 14+ messages in thread
From: David House @ 2010-08-19 13:34 UTC (permalink / raw)
  To: Ethan Burns; +Cc: caml-list, Wheeler Ruml

Note that you can directly observe boxedness using the Obj functions:

# type t1 = { a : float; b : int };;
type t1 = { a : float; b : int; }
# type t2 = { c : float; d : float };;
type t2 = { c : float; d : float; }
# let x1 = { a = 1.; b = 2 };;
val x1 : t1 = {a = 1.; b = 2}
# let x2 = { c = 3.; d = 4. };;
val x2 : t2 = {c = 3.; d = 4.}
# Obj.tag (Obj.repr x1);;
- : int = 0
# Obj.tag (Obj.repr x2);;
- : int = 254

For the use case you describe, I believe your best bet is to define a
float_ref type as you describe.


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [Caml-list] Question about float refs.
  2010-08-19 13:34 ` David House
@ 2010-08-19 13:49   ` Till Varoquaux
  0 siblings, 0 replies; 14+ messages in thread
From: Till Varoquaux @ 2010-08-19 13:49 UTC (permalink / raw)
  To: David House; +Cc: Ethan Burns, caml-list, Wheeler Ruml

What you want is the system to check at runtime whether it should box
the values or not. If you really care about this behaviour you can use
arrays instead of records:

type 'a ref = 'a array
let ref v = Array.create 1 v

let (!) v = v.(0)

let ( := ) v x = v.(0) <- x

I don't expect this to come without a runtime cost.
Till

On Thu, Aug 19, 2010 at 9:34 AM, David House <dmhouse@gmail.com> wrote:
> Note that you can directly observe boxedness using the Obj functions:
>
> # type t1 = { a : float; b : int };;
> type t1 = { a : float; b : int; }
> # type t2 = { c : float; d : float };;
> type t2 = { c : float; d : float; }
> # let x1 = { a = 1.; b = 2 };;
> val x1 : t1 = {a = 1.; b = 2}
> # let x2 = { c = 3.; d = 4. };;
> val x2 : t2 = {c = 3.; d = 4.}
> # Obj.tag (Obj.repr x1);;
> - : int = 0
> # Obj.tag (Obj.repr x2);;
> - : int = 254
>
> For the use case you describe, I believe your best bet is to define a
> float_ref type as you describe.
>
> _______________________________________________
> Caml-list mailing list. Subscription management:
> http://yquem.inria.fr/cgi-bin/mailman/listinfo/caml-list
> Archives: http://caml.inria.fr
> Beginner's list: http://groups.yahoo.com/group/ocaml_beginners
> Bug reports: http://caml.inria.fr/bin/caml-bugs
>


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [Caml-list] Question about float refs.
  2010-08-19 11:52 Question about float refs Ethan Burns
  2010-08-19 13:14 ` [Caml-list] " Lukasz Stafiniak
  2010-08-19 13:34 ` David House
@ 2010-08-23 12:06 ` Christophe TROESTLER
  2010-08-23 12:14   ` Ethan Burns
  2 siblings, 1 reply; 14+ messages in thread
From: Christophe TROESTLER @ 2010-08-23 12:06 UTC (permalink / raw)
  To: burns.ethan; +Cc: caml-list, ruml

On Thu, 19 Aug 2010 07:52:33 -0400, Ethan Burns wrote:
> 
> let r = ref 0.0 ;;
> for i = 0 to 1000000000 do r := float i done;
> Printf.printf "%f\n" !r;
> Printf.printf "words: %f\n" (Gc.stat ()).Gc.minor_words

To add a precision to others' answers : float refs are unboxed
_locally_.  If you rewrite your code as 

let r = ref 0.0 in
for i = 0 to 1000_000_000 do r := float i done;
Printf.printf "%f\n" !r;
Printf.printf "words: %f\n" (Gc.stat ()).Gc.minor_words

then it runs at about the same speed as you other version.

My 0.02€,
C.


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [Caml-list] Question about float refs.
  2010-08-23 12:06 ` Christophe TROESTLER
@ 2010-08-23 12:14   ` Ethan Burns
  2010-08-23 12:42     ` Sylvain Le Gall
  0 siblings, 1 reply; 14+ messages in thread
From: Ethan Burns @ 2010-08-23 12:14 UTC (permalink / raw)
  To: Christophe TROESTLER; +Cc: caml-list, ruml

On Mon, Aug 23, 2010 at 8:06 AM, Christophe TROESTLER
<Christophe.Troestler+ocaml@umh.ac.be> wrote:
> On Thu, 19 Aug 2010 07:52:33 -0400, Ethan Burns wrote:
>>
>> let r = ref 0.0 ;;
>> for i = 0 to 1000000000 do r := float i done;
>> Printf.printf "%f\n" !r;
>> Printf.printf "words: %f\n" (Gc.stat ()).Gc.minor_words
>
> To add a precision to others' answers : float refs are unboxed
> _locally_.  If you rewrite your code as
>
> let r = ref 0.0 in
> for i = 0 to 1000_000_000 do r := float i done;
> Printf.printf "%f\n" !r;
> Printf.printf "words: %f\n" (Gc.stat ()).Gc.minor_words
>
> then it runs at about the same speed as you other version.


$ time ./a.out
1000000000.000000
words: 2000000367.000000

real	0m2.655s

It does seem to run a lot faster than my first version, but it also
seems to allocate a whole lot.  If it is still allocating just as much
why is this version so much faster?

Ethan


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: Question about float refs.
  2010-08-23 12:14   ` Ethan Burns
@ 2010-08-23 12:42     ` Sylvain Le Gall
  2010-08-23 13:00       ` [Caml-list] " Dmitry Bely
  0 siblings, 1 reply; 14+ messages in thread
From: Sylvain Le Gall @ 2010-08-23 12:42 UTC (permalink / raw)
  To: caml-list

On 23-08-2010, Ethan Burns <burns.ethan@gmail.com> wrote:
> On Mon, Aug 23, 2010 at 8:06 AM, Christophe TROESTLER
><Christophe.Troestler+ocaml@umh.ac.be> wrote:
>> On Thu, 19 Aug 2010 07:52:33 -0400, Ethan Burns wrote:
>>>
>>> let r = ref 0.0 ;;
>>> for i = 0 to 1000000000 do r := float i done;
>>> Printf.printf "%f\n" !r;
>>> Printf.printf "words: %f\n" (Gc.stat ()).Gc.minor_words
>>
>> To add a precision to others' answers : float refs are unboxed
>> _locally_.  If you rewrite your code as
>>
>> let r = ref 0.0 in
>> for i = 0 to 1000_000_000 do r := float i done;
>> Printf.printf "%f\n" !r;
>> Printf.printf "words: %f\n" (Gc.stat ()).Gc.minor_words
>>
>> then it runs at about the same speed as you other version.
>
>
> $ time ./a.out
> 1000000000.000000
> words: 2000000367.000000
>
> real	0m2.655s
>
> It does seem to run a lot faster than my first version, but it also
> seems to allocate a whole lot.  If it is still allocating just as much
> why is this version so much faster?
>

Allocation on the minor heap is very cheap compared to assignement into
the major heap. It is better to allocate a lot on the minor heap than to
do operations on the major heap. 

I think the main reason for the difference is that the first example
(float ref not local) implies a call to "caml_modify"
(byterun/memory.c|h) which has a cost. This cost is bigger on amd64
architecture because one test is quite expensive (Is_in_heap I think)
due to address space randomization.

Regards,
Sylvain Le Gall


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [Caml-list] Re: Question about float refs.
  2010-08-23 12:42     ` Sylvain Le Gall
@ 2010-08-23 13:00       ` Dmitry Bely
  2010-08-31 19:41         ` Jon Harrop
  0 siblings, 1 reply; 14+ messages in thread
From: Dmitry Bely @ 2010-08-23 13:00 UTC (permalink / raw)
  To: caml-list

On Mon, Aug 23, 2010 at 4:42 PM, Sylvain Le Gall <sylvain@le-gall.net> wrote:
> On 23-08-2010, Ethan Burns <burns.ethan@gmail.com> wrote:
>> On Mon, Aug 23, 2010 at 8:06 AM, Christophe TROESTLER
>><Christophe.Troestler+ocaml@umh.ac.be> wrote:
>>> On Thu, 19 Aug 2010 07:52:33 -0400, Ethan Burns wrote:
>>>>
>>>> let r = ref 0.0 ;;
>>>> for i = 0 to 1000000000 do r := float i done;
>>>> Printf.printf "%f\n" !r;
>>>> Printf.printf "words: %f\n" (Gc.stat ()).Gc.minor_words
>>>
>>> To add a precision to others' answers : float refs are unboxed
>>> _locally_.  If you rewrite your code as
>>>
>>> let r = ref 0.0 in
>>> for i = 0 to 1000_000_000 do r := float i done;
>>> Printf.printf "%f\n" !r;
>>> Printf.printf "words: %f\n" (Gc.stat ()).Gc.minor_words
>>>
>>> then it runs at about the same speed as you other version.
>>
>>
>> $ time ./a.out
>> 1000000000.000000
>> words: 2000000367.000000
>>
>> real  0m2.655s
>>
>> It does seem to run a lot faster than my first version, but it also
>> seems to allocate a whole lot.  If it is still allocating just as much
>> why is this version so much faster?
>>
>
> Allocation on the minor heap is very cheap compared to assignement into
> the major heap. It is better to allocate a lot on the minor heap than to
> do operations on the major heap.

Some black magic is needed:

let r = ref 0.0 in
for i = 0 to 1000_000_000 do r := float i done;
Printf.printf "%f\n" (!r +. 0.);
Printf.printf "words: %f\n" (Gc.stat ()).Gc.minor_words

and the reference will be placed in a register (Caml heap will not be
used inside the loop)

- Dmitry Bely


^ permalink raw reply	[flat|nested] 14+ messages in thread

* RE: [Caml-list] Re: Question about float refs.
  2010-08-23 13:00       ` [Caml-list] " Dmitry Bely
@ 2010-08-31 19:41         ` Jon Harrop
  2010-09-01  7:18           ` Dmitry Bely
  0 siblings, 1 reply; 14+ messages in thread
From: Jon Harrop @ 2010-08-31 19:41 UTC (permalink / raw)
  To: 'Dmitry Bely', caml-list

You might prefer to multiply by 1.0 because adding 0.0 changes -0.0.

Cheers,
Jon.

> Some black magic is needed:
> 
> let r = ref 0.0 in
> for i = 0 to 1000_000_000 do r := float i done;
> Printf.printf "%f\n" (!r +. 0.);
> Printf.printf "words: %f\n" (Gc.stat ()).Gc.minor_words
> 
> and the reference will be placed in a register (Caml heap will not be
> used inside the loop)
> 
> - Dmitry Bely



^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [Caml-list] Re: Question about float refs.
  2010-08-31 19:41         ` Jon Harrop
@ 2010-09-01  7:18           ` Dmitry Bely
  2010-09-01  7:46             ` Christophe TROESTLER
  2010-09-01  8:31             ` Fabrice Le Fessant
  0 siblings, 2 replies; 14+ messages in thread
From: Dmitry Bely @ 2010-09-01  7:18 UTC (permalink / raw)
  To: caml-list

On Tue, Aug 31, 2010 at 11:41 PM, Jon Harrop
<jonathandeanharrop@googlemail.com> wrote:

> You might prefer to multiply by 1.0 because adding 0.0 changes -0.0.
>
> Cheers,
> Jon.
>
>> Some black magic is needed:
>>
>> let r = ref 0.0 in
>> for i = 0 to 1000_000_000 do r := float i done;
>> Printf.printf "%f\n" (!r +. 0.);
>> Printf.printf "words: %f\n" (Gc.stat ()).Gc.minor_words
>>
>> and the reference will be placed in a register (Caml heap will not be
>> used inside the loop)

Do you have any idea why the optimizer needs that?

- Dmitry Bely


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [Caml-list] Re: Question about float refs.
  2010-09-01  7:18           ` Dmitry Bely
@ 2010-09-01  7:46             ` Christophe TROESTLER
  2010-09-01  8:31             ` Fabrice Le Fessant
  1 sibling, 0 replies; 14+ messages in thread
From: Christophe TROESTLER @ 2010-09-01  7:46 UTC (permalink / raw)
  To: dmitry.bely; +Cc: caml-list

On Wed, 1 Sep 2010 11:18:05 +0400, Dmitry Bely wrote:
> 
> >> Some black magic is needed:
> >>
> >> let r = ref 0.0 in
> >> for i = 0 to 1000_000_000 do r := float i done;
> >> Printf.printf "%f\n" (!r +. 0.);
> >> Printf.printf "words: %f\n" (Gc.stat ()).Gc.minor_words
> 
> Do you have any idea why the optimizer needs that?

It helps the compiler to figure out that “r” must be unboxed.
See also http://caml.inria.fr/mantis/view.php?id=5133

C.


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [Caml-list] Re: Question about float refs.
  2010-09-01  7:18           ` Dmitry Bely
  2010-09-01  7:46             ` Christophe TROESTLER
@ 2010-09-01  8:31             ` Fabrice Le Fessant
  2010-09-01  9:54               ` Ethan Burns
  1 sibling, 1 reply; 14+ messages in thread
From: Fabrice Le Fessant @ 2010-09-01  8:31 UTC (permalink / raw)
  To: caml-list

If you replace (!r +. 0.) by (!r), you are passing the content of the
reference as an argument to the function. Function arguments are not
unboxed in ocamlopt, so the compiler sees a boxed occurrence of the
float, and so must allocate it. The compiler could decide to have the
float both as boxed and unboxed values, but since the reference is
modified in the loop, there would be a risk of inconsistency, and so the
compiler decides to have only a boxed value, and so the loop is much
slower since it has to allocate the float at each iteration (the
reference itself is probably not allocated).

With (!r +. 0.), the float is not used as argument of a function, but in
a floating-point computation, where unboxed values are allowed. As a
consequence, the compiler discovers that all occurrences of the float
can be unboxed, and so decides to unbox the value completely.

--Fabrice

Dmitry Bely wrote, On 09/01/2010 09:18 AM:
> On Tue, Aug 31, 2010 at 11:41 PM, Jon Harrop
> <jonathandeanharrop@googlemail.com> wrote:
> 
>> You might prefer to multiply by 1.0 because adding 0.0 changes -0.0.
>>
>> Cheers,
>> Jon.
>>
>>> Some black magic is needed:
>>>
>>> let r = ref 0.0 in
>>> for i = 0 to 1000_000_000 do r := float i done;
>>> Printf.printf "%f\n" (!r +. 0.);
>>> Printf.printf "words: %f\n" (Gc.stat ()).Gc.minor_words
>>>
>>> and the reference will be placed in a register (Caml heap will not be
>>> used inside the loop)
> 
> Do you have any idea why the optimizer needs that?
> 
> - Dmitry Bely
> 
> _______________________________________________
> Caml-list mailing list. Subscription management:
> http://yquem.inria.fr/cgi-bin/mailman/listinfo/caml-list
> Archives: http://caml.inria.fr
> Beginner's list: http://groups.yahoo.com/group/ocaml_beginners
> Bug reports: http://caml.inria.fr/bin/caml-bugs
> 

-- 
Fabrice LE FESSANT
Chercheur, Equipe ASAP
(As Scalable As Possible)
http://www.lefessant.net/

INRIA-Futurs, Bat P - 112
Parc Orsay Université
2-4, rue Jacques Monod
F-91893 Orsay Cedex, FRANCE


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [Caml-list] Re: Question about float refs.
  2010-09-01  8:31             ` Fabrice Le Fessant
@ 2010-09-01  9:54               ` Ethan Burns
  2010-09-01 12:29                 ` Fabrice Le Fessant
  0 siblings, 1 reply; 14+ messages in thread
From: Ethan Burns @ 2010-09-01  9:54 UTC (permalink / raw)
  To: Fabrice Le Fessant; +Cc: caml-list

On Wed, Sep 1, 2010 at 4:31 AM, Fabrice Le Fessant
<fabrice.le_fessant@inria.fr> wrote:
> If you replace (!r +. 0.) by (!r), you are passing the content of the
> reference as an argument to the function. Function arguments are not
> unboxed in ocamlopt, so the compiler sees a boxed occurrence of the
> float, and so must allocate it. The compiler could decide to have the
> float both as boxed and unboxed values, but since the reference is
> modified in the loop, there would be a risk of inconsistency, and so the
> compiler decides to have only a boxed value, and so the loop is much
> slower since it has to allocate the float at each iteration (the
> reference itself is probably not allocated).

Is there a reason that it can not decide to leave the float unboxed
until right before the Printf.printf call?  This way it would be
modified in the loop without any allocations and then would be placed
in a box only when the box is finally needed.

Ethan


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [Caml-list] Re: Question about float refs.
  2010-09-01  9:54               ` Ethan Burns
@ 2010-09-01 12:29                 ` Fabrice Le Fessant
  0 siblings, 0 replies; 14+ messages in thread
From: Fabrice Le Fessant @ 2010-09-01 12:29 UTC (permalink / raw)
  To: Ethan Burns; +Cc: caml-list

Ethan Burns wrote, On 09/01/2010 11:54 AM:
> On Wed, Sep 1, 2010 at 4:31 AM, Fabrice Le Fessant
> <fabrice.le_fessant@inria.fr> wrote:
>> If you replace (!r +. 0.) by (!r), you are passing the content of the
>> reference as an argument to the function. Function arguments are not
>> unboxed in ocamlopt, so the compiler sees a boxed occurrence of the
>> float, and so must allocate it. The compiler could decide to have the
>> float both as boxed and unboxed values, but since the reference is
>> modified in the loop, there would be a risk of inconsistency, and so the
>> compiler decides to have only a boxed value, and so the loop is much
>> slower since it has to allocate the float at each iteration (the
>> reference itself is probably not allocated).
> 
> Is there a reason that it can not decide to leave the float unboxed
> until right before the Printf.printf call?  This way it would be
> modified in the loop without any allocations and then would be placed
> in a box only when the box is finally needed.

Yes, that would work. I suppose the reason is just that the analysis to
discover that is much more complex and expensive, and the result is not
always successful, so it was not clear it was worth it for typical OCaml
applications.

--Fabrice


^ permalink raw reply	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2010-09-01 12:30 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-08-19 11:52 Question about float refs Ethan Burns
2010-08-19 13:14 ` [Caml-list] " Lukasz Stafiniak
2010-08-19 13:34 ` David House
2010-08-19 13:49   ` Till Varoquaux
2010-08-23 12:06 ` Christophe TROESTLER
2010-08-23 12:14   ` Ethan Burns
2010-08-23 12:42     ` Sylvain Le Gall
2010-08-23 13:00       ` [Caml-list] " Dmitry Bely
2010-08-31 19:41         ` Jon Harrop
2010-09-01  7:18           ` Dmitry Bely
2010-09-01  7:46             ` Christophe TROESTLER
2010-09-01  8:31             ` Fabrice Le Fessant
2010-09-01  9:54               ` Ethan Burns
2010-09-01 12:29                 ` Fabrice Le Fessant

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).