caml-list - the Caml user's mailing list
 help / color / mirror / Atom feed
* thread i/o
@ 1999-01-12 20:12 Michael Hicks
  1999-01-13 13:20 ` Xavier Leroy
  0 siblings, 1 reply; 2+ messages in thread
From: Michael Hicks @ 1999-01-12 20:12 UTC (permalink / raw)
  To: caml-list

One of the adverisements of OCaml 2.01 was that user-level (not POSIX)
thread I/O is made faster.  The trick used in the implementation is to make
sockets non-blocking, and then only perform a select() when the I/O would
have blocked (by signalling EINTR or EWOULDBLOCK).  However, looking at the
implementation, it looks like in many cases the select() is performed
anyway.  Here is an excerpt from threadUnix.ml:

let rec read fd buff ofs len =
  Thread.wait_read fd;
  try Unix.read fd buff ofs len
  with Unix_error((EAGAIN | EWOULDBLOCK), _, _) -> read fd buff ofs len

The implementation of Thread.wait_read and Thread.wait_write will perform
either a select() or go through the scheduler which will perform the
select().  It seems to me that the implementation should be:

let rec read fd buff ofs len =
  try Unix.read fd buff ofs len
  with Unix_error((EAGAIN | EWOULDBLOCK), _, _) -> 
    begin
      Thread.wait_read fd;
      read fd buff ofs len
    end

This way, if I/O is already available, there is no need to do the extra
select().  Am I missing something?
Mike

-- 
Michael Hicks
Ph.D. Candidate, the University of Pennsylvania
http://www.cis.upenn.edu/~mwh            mailto://mwh@dsl.cis.upenn.edu
"I worked with an individual who plugged his power strip back into itself
and for the life of him could not understand why his computer would not
turn on."




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

* Re: thread i/o
  1999-01-12 20:12 thread i/o Michael Hicks
@ 1999-01-13 13:20 ` Xavier Leroy
  0 siblings, 0 replies; 2+ messages in thread
From: Xavier Leroy @ 1999-01-13 13:20 UTC (permalink / raw)
  To: Michael Hicks, caml-list

> One of the adverisements of OCaml 2.01 was that user-level (not POSIX)
> thread I/O is made faster.

Change logs are lists of changes, not advertisements.  At any rate:

>  The trick used in the implementation is to make
> sockets non-blocking, and then only perform a select() when the I/O would
> have blocked (by signalling EINTR or EWOULDBLOCK).  However, looking at the
> implementation, it looks like in many cases the select() is performed
> anyway.

This is not quite right.  What was made faster in 2.01 is the
interface with select().  In 2.00, the only primitive available for
suspending a thread until I/O can be done was Thread.select().  That
primitive is quite expensive in itself (independently of the cost of the
select() system call), because it builds and destructures lists.
In 2.01, I added more basic primitives to wait for I/O on one file
descriptor.  Those primitives involve no list processing and no heap
allocation.  This causes noticeable speedups on threaded programs that
do a lot of I/O.

In parallel, the user-level thread package was also made safer by
handling the EAGAIN or EWOULDBLOCK errors that could occur in the
following situation:
        - we select() on some file descriptors;
        - select() tells us we can e.g. read on file descriptor F;
        - we read on F and find that actually there is no data to read
          (maybe another thread has already read from F in the
           meantime, or select() is a bit sloppy);
        - an EAGAIN error condition is generated.
Then, the right thing to do is to retry the select and the read.
This is a mostly theoretical scenario (I haven't been able to trigger
it in my test programs), but still the Unix specs allow for it.

> The implementation of Thread.wait_read and Thread.wait_write will perform
> either a select() or go through the scheduler which will perform the
> select().  It seems to me that the implementation should be:
> 
> let rec read fd buff ofs len =
>   try Unix.read fd buff ofs len
>   with Unix_error((EAGAIN | EWOULDBLOCK), _, _) -> 
>     begin
>       Thread.wait_read fd;
>       read fd buff ofs len
>     end
> 
> This way, if I/O is already available, there is no need to do the extra
> select().  Am I missing something?

No.  You could indeed do this; I just didn't think about it.
It will run faster than the current implementation if data is
available most of the time (no need for select()), but slower if data
is not available most of the time (you'll do a useless read() before
calling select()).  I'd be interested to know which implementation is
better on your application.

All the best,

- Xavier Leroy




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

end of thread, other threads:[~1999-01-13 17:49 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
1999-01-12 20:12 thread i/o Michael Hicks
1999-01-13 13:20 ` Xavier Leroy

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