caml-list - the Caml user's mailing list
 help / color / mirror / Atom feed
* [Caml-list] classes not optimized?
@ 2017-10-25  9:31 Christoph Höger
  2017-10-25  9:44 ` Gabriel Scherer
  2017-11-08 21:42 ` Mr. Herr
  0 siblings, 2 replies; 8+ messages in thread
From: Christoph Höger @ 2017-10-25  9:31 UTC (permalink / raw)
  To: OCaml Mailing List

[-- Attachment #1: Type: text/plain, Size: 1388 bytes --]

Dear OCaml users,

consider the following microbenchmark:

<snip>
class s (z : string)  (y : int)  (x : int) =
  object method z = z method y = y method x = x
end

type t = { x : int; y : int; z : string}

let foo_s _ =
 (new s) "Example" 0 1

let foo_t _ = {x=1; y=0; z="Example"}

let one_s _ = (foo_s ())#x

let one_t _ = (foo_t ()).x

let fac =
  let rec fac n =
    let f =
      let rec f n a = if n <= 1 then a else f (n - (one_s ())) (n * a)  in
f (* change one_t to one_s or vice-versa *)
       in
    f n 1  in
  fac
let bench =
  let rec bench n a =
    if n <= 0
    then a
    else (let x = a && ((fac 20) == (20 * (fac 19)))  in bench (n - 1) x)
in
  bench
let test = bench 10000000 true
let main _ = test
</snip>

If I run it with ocamlopt 4.05.0+flambda and -O3, the version that uses
one_s takes about 7.5s whereas the one with one_t uses 0.35s. I know that
object method lookup is more costly than records, of course. This
particular case baffles me, though. Why is the class not completely inlined?

Also as a related question, is there a way to have the lookup semantics of
methods without the open recursion part? That is, can I have a class that
consists of values, not methods? It would love to have open tuples in some
cases. For example, I'd like to write a function that takes a tuple of any
length, because it only needs the first element.

thanks,

Christoph

[-- Attachment #2: Type: text/html, Size: 1792 bytes --]

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

* Re: [Caml-list] classes not optimized?
  2017-10-25  9:31 [Caml-list] classes not optimized? Christoph Höger
@ 2017-10-25  9:44 ` Gabriel Scherer
  2017-10-25 13:27   ` vadim
  2017-11-08 21:42 ` Mr. Herr
  1 sibling, 1 reply; 8+ messages in thread
From: Gabriel Scherer @ 2017-10-25  9:44 UTC (permalink / raw)
  To: Christoph Höger; +Cc: OCaml Mailing List

[-- Attachment #1: Type: text/plain, Size: 2592 bytes --]

Object-oriented code tends to generate large intermediate representations,
too large to be inlined and too complex to be partially evaluated at
compile-time. I wouldn't expect this sort of optimization to work.

You might have better luck with first-class modules if you want some sort
of tuple subtyping.
(Performance model: with modules, subtyping coercions are compiled as a
field-reordering copy,
so the runtime cost is on the coercion rather than the field access.)

> Also as a related question, is there a way to have the lookup semantics
of methods without the open recursion part?

We could add row-typed extensible records to the language. Given how little
the object-oriented layer is used in practice, I am not sure that this
highly work-demanding addition would be a good use of a contributor's time
-- and its utility would have to be weighted against the complexity cost of
adding yet another kind of product structure.

On Wed, Oct 25, 2017 at 11:31 AM, Christoph Höger <
christoph.hoeger@celeraone.com> wrote:

> Dear OCaml users,
>
> consider the following microbenchmark:
>
> <snip>
> class s (z : string)  (y : int)  (x : int) =
>   object method z = z method y = y method x = x
> end
>
> type t = { x : int; y : int; z : string}
>
> let foo_s _ =
>  (new s) "Example" 0 1
>
> let foo_t _ = {x=1; y=0; z="Example"}
>
> let one_s _ = (foo_s ())#x
>
> let one_t _ = (foo_t ()).x
>
> let fac =
>   let rec fac n =
>     let f =
>       let rec f n a = if n <= 1 then a else f (n - (one_s ())) (n * a)  in
> f (* change one_t to one_s or vice-versa *)
>        in
>     f n 1  in
>   fac
> let bench =
>   let rec bench n a =
>     if n <= 0
>     then a
>     else (let x = a && ((fac 20) == (20 * (fac 19)))  in bench (n - 1) x)
> in
>   bench
> let test = bench 10000000 true
> let main _ = test
> </snip>
>
> If I run it with ocamlopt 4.05.0+flambda and -O3, the version that uses
> one_s takes about 7.5s whereas the one with one_t uses 0.35s. I know that
> object method lookup is more costly than records, of course. This
> particular case baffles me, though. Why is the class not completely inlined?
>
> Also as a related question, is there a way to have the lookup semantics of
> methods without the open recursion part? That is, can I have a class that
> consists of values, not methods? It would love to have open tuples in some
> cases. For example, I'd like to write a function that takes a tuple of any
> length, because it only needs the first element.
>
> thanks,
>
> Christoph
>
>

[-- Attachment #2: Type: text/html, Size: 3186 bytes --]

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

* Re: [Caml-list] classes not optimized?
  2017-10-25  9:44 ` Gabriel Scherer
@ 2017-10-25 13:27   ` vadim
  2017-10-25 13:48     ` Ivan Gotovchits
  0 siblings, 1 reply; 8+ messages in thread
From: vadim @ 2017-10-25 13:27 UTC (permalink / raw)
  To: OCaml Mailing List; +Cc: Gabriel Scherer, Christoph Höger

[-- Attachment #1: Type: text/html, Size: 3556 bytes --]

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

* Re: [Caml-list] classes not optimized?
  2017-10-25 13:27   ` vadim
@ 2017-10-25 13:48     ` Ivan Gotovchits
  2017-10-25 13:49       ` Ivan Gotovchits
  0 siblings, 1 reply; 8+ messages in thread
From: Ivan Gotovchits @ 2017-10-25 13:48 UTC (permalink / raw)
  To: vadim; +Cc: OCaml Mailing List, Gabriel Scherer, Christoph Höger

[-- Attachment #1: Type: text/plain, Size: 4128 bytes --]

On Wed, Oct 25, 2017 at 9:27 AM, <vadim@radovel.ru> wrote:

>
> Excuse me, does this mean that I should use modules instead of objects if
> its posible. For example: I have a big list of different modules with the
> same signature containing some values and functions inside - would it give
> the best performance in OCaml comparing to classes, objects, etc?
>


If you can solve your problem without relying on objects, then it is better
not to use them. There is nothing wrong with them. It just because it is a
very powerful mechanism, that sometimes is not easy to control. They give
you an extra flexibility so that you can express more complex abstractions,
but this comes with a price. Given your particular example, yes, calling a
function is faster, than calling a method, as the latter introduces an
extra layer of indirection via the virtual method table. Moreover, if a
function is know at compile time, it can be inlined, that may provide even
further optimization opportunities. It is very hard in general to inline a
method, as a compiler needs a proof that this and only this method will be
called in runtime. Moreover, if it is the case (i.e., the method can be
resolved in runtime), then it is a signal that an object should not be used
at all. Basically, objects should be used when you need an open recursion
with runtime dispatch. This is a very rare case, in fact.




>
> 25.10.2017, 12:45, "Gabriel Scherer" <gabriel.scherer@gmail.com>:
>
> Object-oriented code tends to generate large intermediate representations,
> too large to be inlined and too complex to be partially evaluated at
> compile-time. I wouldn't expect this sort of optimization to work.
>
> You might have better luck with first-class modules if you want some sort
> of tuple subtyping.
> (Performance model: with modules, subtyping coercions are compiled as a
> field-reordering copy,
> so the runtime cost is on the coercion rather than the field access.)
>
> > Also as a related question, is there a way to have the lookup semantics
> of methods without the open recursion part?
>
> We could add row-typed extensible records to the language. Given how
> little the object-oriented layer is used in practice, I am not sure that
> this highly work-demanding addition would be a good use of a contributor's
> time -- and its utility would have to be weighted against the complexity
> cost of adding yet another kind of product structure.
>
> On Wed, Oct 25, 2017 at 11:31 AM, Christoph Höger <
> christoph.hoeger@celeraone.com> wrote:
>
> Dear OCaml users,
>
> consider the following microbenchmark:
>
> <snip>
> class s (z : string)  (y : int)  (x : int) =
>   object method z = z method y = y method x = x
> end
>
> type t = { x : int; y : int; z : string}
>
> let foo_s _ =
>  (new s) "Example" 0 1
>
> let foo_t _ = {x=1; y=0; z="Example"}
>
> let one_s _ = (foo_s ())#x
>
> let one_t _ = (foo_t ()).x
>
> let fac =
>   let rec fac n =
>     let f =
>       let rec f n a = if n <= 1 then a else f (n - (one_s ())) (n * a)  in
> f (* change one_t to one_s or vice-versa *)
>        in
>     f n 1  in
>   fac
> let bench =
>   let rec bench n a =
>     if n <= 0
>     then a
>     else (let x = a && ((fac 20) == (20 * (fac 19)))  in bench (n - 1) x)
> in
>   bench
> let test = bench 10000000 true
> let main _ = test
> </snip>
>
> If I run it with ocamlopt 4.05.0+flambda and -O3, the version that uses
> one_s takes about 7.5s whereas the one with one_t uses 0.35s. I know that
> object method lookup is more costly than records, of course. This
> particular case baffles me, though. Why is the class not completely inlined?
>
> Also as a related question, is there a way to have the lookup semantics of
> methods without the open recursion part? That is, can I have a class that
> consists of values, not methods? It would love to have open tuples in some
> cases. For example, I'd like to write a function that takes a tuple of any
> length, because it only needs the first element.
>
> thanks,
>
> Christoph
>
>
>

[-- Attachment #2: Type: text/html, Size: 5204 bytes --]

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

* Re: [Caml-list] classes not optimized?
  2017-10-25 13:48     ` Ivan Gotovchits
@ 2017-10-25 13:49       ` Ivan Gotovchits
  2017-10-25 14:35         ` vadim
  0 siblings, 1 reply; 8+ messages in thread
From: Ivan Gotovchits @ 2017-10-25 13:49 UTC (permalink / raw)
  To: vadim; +Cc: OCaml Mailing List, Gabriel Scherer, Christoph Höger

[-- Attachment #1: Type: text/plain, Size: 4470 bytes --]

a typo: "(i.e., the method can be resolved in runtime)" should be read as
"(i.e., the method can be resolved at compile time)"

On Wed, Oct 25, 2017 at 9:48 AM, Ivan Gotovchits <ivg@ieee.org> wrote:

>
>
> On Wed, Oct 25, 2017 at 9:27 AM, <vadim@radovel.ru> wrote:
>
>>
>> Excuse me, does this mean that I should use modules instead of objects if
>> its posible. For example: I have a big list of different modules with the
>> same signature containing some values and functions inside - would it give
>> the best performance in OCaml comparing to classes, objects, etc?
>>
>
>
> If you can solve your problem without relying on objects, then it is
> better not to use them. There is nothing wrong with them. It just because
> it is a very powerful mechanism, that sometimes is not easy to control.
> They give you an extra flexibility so that you can express more complex
> abstractions, but this comes with a price. Given your particular example,
> yes, calling a function is faster, than calling a method, as the latter
> introduces an extra layer of indirection via the virtual method table.
> Moreover, if a function is know at compile time, it can be inlined, that
> may provide even further optimization opportunities. It is very hard in
> general to inline a method, as a compiler needs a proof that this and only
> this method will be called in runtime. Moreover, if it is the case (i.e.,
> the method can be resolved in runtime), then it is a signal that an object
> should not be used at all. Basically, objects should be used when you need
> an open recursion with runtime dispatch. This is a very rare case, in fact.
>
>
>
>
>>
>> 25.10.2017, 12:45, "Gabriel Scherer" <gabriel.scherer@gmail.com>:
>>
>> Object-oriented code tends to generate large intermediate
>> representations, too large to be inlined and too complex to be partially
>> evaluated at compile-time. I wouldn't expect this sort of optimization to
>> work.
>>
>> You might have better luck with first-class modules if you want some sort
>> of tuple subtyping.
>> (Performance model: with modules, subtyping coercions are compiled as a
>> field-reordering copy,
>> so the runtime cost is on the coercion rather than the field access.)
>>
>> > Also as a related question, is there a way to have the lookup semantics
>> of methods without the open recursion part?
>>
>> We could add row-typed extensible records to the language. Given how
>> little the object-oriented layer is used in practice, I am not sure that
>> this highly work-demanding addition would be a good use of a contributor's
>> time -- and its utility would have to be weighted against the complexity
>> cost of adding yet another kind of product structure.
>>
>> On Wed, Oct 25, 2017 at 11:31 AM, Christoph Höger <
>> christoph.hoeger@celeraone.com> wrote:
>>
>> Dear OCaml users,
>>
>> consider the following microbenchmark:
>>
>> <snip>
>> class s (z : string)  (y : int)  (x : int) =
>>   object method z = z method y = y method x = x
>> end
>>
>> type t = { x : int; y : int; z : string}
>>
>> let foo_s _ =
>>  (new s) "Example" 0 1
>>
>> let foo_t _ = {x=1; y=0; z="Example"}
>>
>> let one_s _ = (foo_s ())#x
>>
>> let one_t _ = (foo_t ()).x
>>
>> let fac =
>>   let rec fac n =
>>     let f =
>>       let rec f n a = if n <= 1 then a else f (n - (one_s ())) (n * a)
>> in f (* change one_t to one_s or vice-versa *)
>>        in
>>     f n 1  in
>>   fac
>> let bench =
>>   let rec bench n a =
>>     if n <= 0
>>     then a
>>     else (let x = a && ((fac 20) == (20 * (fac 19)))  in bench (n - 1)
>> x)  in
>>   bench
>> let test = bench 10000000 true
>> let main _ = test
>> </snip>
>>
>> If I run it with ocamlopt 4.05.0+flambda and -O3, the version that uses
>> one_s takes about 7.5s whereas the one with one_t uses 0.35s. I know that
>> object method lookup is more costly than records, of course. This
>> particular case baffles me, though. Why is the class not completely inlined?
>>
>> Also as a related question, is there a way to have the lookup semantics
>> of methods without the open recursion part? That is, can I have a class
>> that consists of values, not methods? It would love to have open tuples in
>> some cases. For example, I'd like to write a function that takes a tuple of
>> any length, because it only needs the first element.
>>
>> thanks,
>>
>> Christoph
>>
>>
>>
>

[-- Attachment #2: Type: text/html, Size: 5808 bytes --]

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

* Re: [Caml-list] classes not optimized?
  2017-10-25 13:49       ` Ivan Gotovchits
@ 2017-10-25 14:35         ` vadim
       [not found]           ` <3e0f1001-b730-18b2-670d-cec4e0e89ef4@frisch.fr>
  0 siblings, 1 reply; 8+ messages in thread
From: vadim @ 2017-10-25 14:35 UTC (permalink / raw)
  To: Ivan Gotovchits, OCaml Mailing List; +Cc: Gabriel Scherer, Christoph Höger

[-- Attachment #1: Type: text/html, Size: 6795 bytes --]

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

* Re: [Caml-list] classes not optimized?
       [not found]           ` <3e0f1001-b730-18b2-670d-cec4e0e89ef4@frisch.fr>
@ 2017-10-25 15:23             ` vadim
  0 siblings, 0 replies; 8+ messages in thread
From: vadim @ 2017-10-25 15:23 UTC (permalink / raw)
  To: Alain Frisch, Ivan Gotovchits, OCaml Mailing List
  Cc: Gabriel Scherer, Christoph Höger

[-- Attachment #1: Type: text/html, Size: 1067 bytes --]

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

* Re: [Caml-list] classes not optimized?
  2017-10-25  9:31 [Caml-list] classes not optimized? Christoph Höger
  2017-10-25  9:44 ` Gabriel Scherer
@ 2017-11-08 21:42 ` Mr. Herr
  1 sibling, 0 replies; 8+ messages in thread
From: Mr. Herr @ 2017-11-08 21:42 UTC (permalink / raw)
  To: caml-list

[-- Attachment #1: Type: text/plain, Size: 1899 bytes --]

Dear list readers,

to me it seems this benches only the object creation, not the method dispatch.

So the main result here is that object creation is quite expensive, and record field
selection is 2 times faster than getting a value by a method.

Just change foo_s to a constant class instance removing the _ parameter.


Max


On 25.10.2017 11:31, Christoph Höger wrote:
> Dear OCaml users,
>
> consider the following microbenchmark:
>
> <snip>
> class s (z : string)  (y : int)  (x : int) =
>   object method z = z method y = y method x = x
> end
>
> type t = { x : int; y : int; z : string}
>
> let foo_s _ =
>  (new s) "Example" 0 1
>
> let foo_t _ = {x=1; y=0; z="Example"}
>
> let one_s _ = (foo_s ())#x
>
> let one_t _ = (foo_t ()).x
>
> let fac =
>   let rec fac n =
>     let f =
>       let rec f n a = if n <= 1 then a else f (n - (one_s ())) (n * a)  in f (*
> change one_t to one_s or vice-versa *)
>        in
>     f n 1  in
>   fac
> let bench =
>   let rec bench n a =
>     if n <= 0
>     then a
>     else (let x = a && ((fac 20) == (20 * (fac 19)))  in bench (n - 1) x)  in
>   bench
> let test = bench 10000000 true
> let main _ = test
> </snip>
>
> If I run it with ocamlopt 4.05.0+flambda and -O3, the version that uses one_s takes
> about 7.5s whereas the one with one_t uses 0.35s. I know that object method lookup
> is more costly than records, of course. This particular case baffles me, though.
> Why is the class not completely inlined?
>
> Also as a related question, is there a way to have the lookup semantics of methods
> without the open recursion part? That is, can I have a class that consists of
> values, not methods? It would love to have open tuples in some cases. For example,
> I'd like to write a function that takes a tuple of any length, because it only
> needs the first element.
>
> thanks,
>
> Christoph
>  


[-- Attachment #2: Type: text/html, Size: 3879 bytes --]

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

end of thread, other threads:[~2017-11-08 21:45 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-10-25  9:31 [Caml-list] classes not optimized? Christoph Höger
2017-10-25  9:44 ` Gabriel Scherer
2017-10-25 13:27   ` vadim
2017-10-25 13:48     ` Ivan Gotovchits
2017-10-25 13:49       ` Ivan Gotovchits
2017-10-25 14:35         ` vadim
     [not found]           ` <3e0f1001-b730-18b2-670d-cec4e0e89ef4@frisch.fr>
2017-10-25 15:23             ` vadim
2017-11-08 21:42 ` Mr. Herr

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).