caml-list - the Caml user's mailing list
 help / color / mirror / Atom feed
* [Caml-list] Out_of_memory exception in output_value
@ 2004-05-28  9:10 Richard Jones
  2004-05-28 14:31 ` Richard Jones
  0 siblings, 1 reply; 6+ messages in thread
From: Richard Jones @ 2004-05-28  9:10 UTC (permalink / raw)
  To: caml-list

I have a problem with a real program which tries to write an approx
200 MB data structure to disk using output_value.  The output_value
statement throws an Out_of_memory exception, even though the Unix
process itself is only using around 350 MB on a machine with plenty of
physical RAM and swap.

The test program below reproduces the problem.

Is there any way I can tell the memory allocator that it's allowed to
keep allocating memory, or work around this bug in another way?

Rich.

---
(* ocamlopt -w s unix.cmxa memtest.ml -o memtest *)

let arraylen = 200000
let stringlen = 1000

let () =
  prerr_endline "Creating big structure ...";
  let data = Array.init arraylen (fun i -> String.create stringlen) in
  prerr_endline "Sleeping for 5 seconds ...";
  Unix.sleep 5;
  prerr_endline "Saving big structure to a file ...";
  let chan = open_out_bin "/tmp/test.dat" in
  output_value chan data;
  close_out chan;
  ignore (Sys.command "ls -lh /tmp/test.dat")

---
$ ./memtest 
Creating big structure ...
Sleeping for 5 seconds ...
Saving big structure to a file ...
Fatal error: exception Out_of_memory

During the sleep:

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND           
 3098 rich      25   0  203m 198m 1640 S 43.4 42.0   0:03.21 memtest           

Just before the crash:

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND           
 3103 rich      18   0  333m 301m 1640 D  1.7 63.7   0:04.49 memtest           

-- 
Richard Jones. http://www.annexia.org/ http://www.j-london.com/
Merjis Ltd. http://www.merjis.com/ - improving website return on investment
http://www.winwinsales.co.uk/ - CRM improvement consultancy

-------------------
To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr
Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/
Beginner's list: http://groups.yahoo.com/group/ocaml_beginners


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

* Re: [Caml-list] Out_of_memory exception in output_value
  2004-05-28  9:10 [Caml-list] Out_of_memory exception in output_value Richard Jones
@ 2004-05-28 14:31 ` Richard Jones
  2004-05-28 16:47   ` Jacques Carette
  0 siblings, 1 reply; 6+ messages in thread
From: Richard Jones @ 2004-05-28 14:31 UTC (permalink / raw)
  To: caml-list

Thanks to some people who helped me off-list.  I've solved this now.

It turns out that during output_value, the OCaml code allocates a
block of memory to contain the marshalled representation of the data.
Each time it runs out of room, it doubles the size of that block using
realloc().

In my case the final allocation was something like 260MB because the
size of the data required was approx 144MB (ie. just over 130MB, so it
needed to double the block).  realloc() was actually returning NULL
because the kernel couldn't allocate enough memory.

Allocating another 1GB of swap solves the problem nicely.

Rich.

-- 
Richard Jones. http://www.annexia.org/ http://www.j-london.com/
Merjis Ltd. http://www.merjis.com/ - improving website return on investment
http://www.winwinsales.co.uk/ - CRM improvement consultancy

-------------------
To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr
Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/
Beginner's list: http://groups.yahoo.com/group/ocaml_beginners


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

* RE: [Caml-list] Out_of_memory exception in output_value
  2004-05-28 14:31 ` Richard Jones
@ 2004-05-28 16:47   ` Jacques Carette
  2004-05-28 19:44     ` skaller
  0 siblings, 1 reply; 6+ messages in thread
From: Jacques Carette @ 2004-05-28 16:47 UTC (permalink / raw)
  To: 'Richard Jones', caml-list

> It turns out that during output_value, the OCaml code allocates a
> block of memory to contain the marshalled representation of the data.
> Each time it runs out of room, it doubles the size of that block using
> realloc().

Wouldn't it be more system-friendly to try successively factors *2, *1.5,
*1.1, and *1.05 before actually failing?  This would not cost any
performance penalty in successful cases, and could decrease the number of
failing cases.  Seems win-win to me.  And as long as it is a multiplicative
factor, this does not lead to algorithmic degenerescence.

Jacques

-------------------
To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr
Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/
Beginner's list: http://groups.yahoo.com/group/ocaml_beginners


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

* RE: [Caml-list] Out_of_memory exception in output_value
  2004-05-28 16:47   ` Jacques Carette
@ 2004-05-28 19:44     ` skaller
  2004-05-28 20:54       ` Eric Dahlman
  0 siblings, 1 reply; 6+ messages in thread
From: skaller @ 2004-05-28 19:44 UTC (permalink / raw)
  To: carette; +Cc: 'Richard Jones', caml-list

On Sat, 2004-05-29 at 02:47, Jacques Carette wrote:
> > It turns out that during output_value, the OCaml code allocates a
> > block of memory to contain the marshalled representation of the data.
> > Each time it runs out of room, it doubles the size of that block using
> > realloc().
> 
> Wouldn't it be more system-friendly to try successively factors *2, *1.5,
> *1.1, and *1.05 before actually failing? 

I have a published book chapter part of which deals with
this issue in some detail, including some performance
measurements.

The general solution is much cleaner -- to use
a user supplied allocation function something like:

new_size = f old_size requested_size

Doubling the size, or indeed even using a pure multiplier,
is only one possible option: simply adding a fixed amout
is another option, and an arbitrary function handles
both cases and many more. So a general solution is to make
the operation polymorphic by making the calculator function
an argument (in practice with a sensible default).

My experiments were with variable length arrays used
to hold big integers, so some operations produced
small increases (like addition) whereas other
produced increases much faster (like multiplication).

A quick and easy fix would be to use a global variable
containing the function which the client can reset.
Yeah, I hate global state but here the actual function
chosen has no semantic impact, it only affects performance
(unless you run out of memory .. of course). So this
time a global might be OK :)

-- 
John Skaller, mailto:skaller@users.sf.net
voice: 061-2-9660-0850, 
snail: PO BOX 401 Glebe NSW 2037 Australia
Checkout the Felix programming language http://felix.sf.net



-------------------
To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr
Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/
Beginner's list: http://groups.yahoo.com/group/ocaml_beginners


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

* Re: [Caml-list] Out_of_memory exception in output_value
  2004-05-28 19:44     ` skaller
@ 2004-05-28 20:54       ` Eric Dahlman
  2004-05-29  7:13         ` skaller
  0 siblings, 1 reply; 6+ messages in thread
From: Eric Dahlman @ 2004-05-28 20:54 UTC (permalink / raw)
  To: skaller; +Cc: carette, 'Richard Jones', caml-list


On May 28, 2004, at 2:44 PM, skaller wrote:

> On Sat, 2004-05-29 at 02:47, Jacques Carette wrote:
>>> It turns out that during output_value, the OCaml code allocates a
>>> block of memory to contain the marshalled representation of the data.
>>> Each time it runs out of room, it doubles the size of that block 
>>> using
>>> realloc().
>>
>> Wouldn't it be more system-friendly to try successively factors *2, 
>> *1.5,
>> *1.1, and *1.05 before actually failing?

I am not sure that it would have that much benefit for all of the 
complexity it would introduce.  I expect the set of interesting things 
with a marshaled representation which is too large for the present 
approach but still small enough to work for a smaller factors is really 
small.  Keep in mind that when the size is grown the assumption is that 
you will have to copy the lower half of the already marshaled data into 
the new object since it is almost guaranteed that it will not be 
possible to grow the object in place as something else will almost 
surely have been allocated after it.  A better approach would be to 
have an output_really_big_object which allocated a huge buffer in a 
single go or an approach which streamed the marshaled data so as not to 
have to keep it in memory.  (I don't know if that approach is possible 
using ocaml's marshaling, i didn't look.) At any rate this still 
addresses what I believe to be a small class of useful situations, on 
the whole I suspect that the data will be small enough to work with the 
present approach or so big that the fractional approach will also fail.

> I have a published book chapter part of which deals with
> this issue in some detail, including some performance
> measurements.
>
> The general solution is much cleaner -- to use
> a user supplied allocation function something like:
>
> new_size = f old_size requested_size
>
> Doubling the size, or indeed even using a pure multiplier,
> is only one possible option: simply adding a fixed amout
> is another option, and an arbitrary function handles
> both cases and many more. So a general solution is to make
> the operation polymorphic by making the calculator function
> an argument (in practice with a sensible default).

Since you will have to copy the data on a realloc doubling has the nice 
effect of giving us a constant bound on the costs of reallocation in a 
calculation.  This does not hold for approaches like increasing in 
fixed increments which will convert an algorithm which in linear in the 
size of the data into one which is quadratic.  I think that doubling 
might well be the best "guestimate" for a general library to make.

> My experiments were with variable length arrays used
> to hold big integers, so some operations produced
> small increases (like addition) whereas other
> produced increases much faster (like multiplication).

In this domain specific case you have a wealth of information and you 
can calculate a very tight bound on the size  of the resulting array so 
you should never have to grow the result as you go.  Just allocate it 
correctly in the beginning or am I missing something?

> A quick and easy fix would be to use a global variable
> containing the function which the client can reset.
> Yeah, I hate global state but here the actual function
> chosen has no semantic impact, it only affects performance
> (unless you run out of memory .. of course). So this
> time a global might be OK :)

I don't know about this one, it really smacks of premature optimization 
and abstraction inversion.  The present approach to buffer growing has 
a constant amortized cost.  If one was in the position of being able to 
significantly improve on that for a given problem domain by carefully 
tweaking such an allocation function then I would almost guarantee that 
it would be better to create a custom solution for that domain.  This 
would allow explicitly using that information to create a better 
algorithm rather than just tweaking a general mechanism.

Just my two cents,
-Eric

-------------------
To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr
Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/
Beginner's list: http://groups.yahoo.com/group/ocaml_beginners


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

* Re: [Caml-list] Out_of_memory exception in output_value
  2004-05-28 20:54       ` Eric Dahlman
@ 2004-05-29  7:13         ` skaller
  0 siblings, 0 replies; 6+ messages in thread
From: skaller @ 2004-05-29  7:13 UTC (permalink / raw)
  To: Eric Dahlman; +Cc: carette, 'Richard Jones', caml-list

On Sat, 2004-05-29 at 06:54, Eric Dahlman wrote:
> On May 28, 2004, at 2:44 PM, skaller wrote:

> >> Wouldn't it be more system-friendly to try successively factors *2, 
> >> *1.5,
> >> *1.1, and *1.05 before actually failing?
> 
> I am not sure that it would have that much benefit for all of the 
> complexity it would introduce. 

I don't quite agree for the following reason: if something
fails when you're only using 50% of memory instead of 90%,
you're likely to be both puzzled and annoyed. In practice
this can make quite a difference at what sized problems
you can handle on your machine. It can also really trash out
a large server because malloc() is *required* to actually
allocate memory, not just address space.

Since allocations of this kind are rare the extra cost
doing a most sophisticated calculation isn't important.

As you point out though, the extra complexity is a real
problem: we could argue forever how to choose an optimial
calculation. Which is why I suggested the user be able
to do it. This delegates the complexity back to the
client and out of the library.

-- 
John Skaller, mailto:skaller@users.sf.net
voice: 061-2-9660-0850, 
snail: PO BOX 401 Glebe NSW 2037 Australia
Checkout the Felix programming language http://felix.sf.net



-------------------
To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr
Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/
Beginner's list: http://groups.yahoo.com/group/ocaml_beginners


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

end of thread, other threads:[~2004-05-29  7:14 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2004-05-28  9:10 [Caml-list] Out_of_memory exception in output_value Richard Jones
2004-05-28 14:31 ` Richard Jones
2004-05-28 16:47   ` Jacques Carette
2004-05-28 19:44     ` skaller
2004-05-28 20:54       ` Eric Dahlman
2004-05-29  7:13         ` skaller

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