Subject: [clisp-list] TCP-based Network Server in CLISP: read-char vs. buf fering

From: "Hoehle, Joerg-Cyril" <[email-masked]>
Date: Mon Sep 30 04:03:12 2002

Hi,

> > Using READ-CHAR-NO-HANG for i/o instead of sequence/array 
>functions is 
> > going to keep your program a _toy prototype_ with extremely poor 
>I think this conclusion is unjustified.  What do you consider to be
>extremely poor performance?  Do you have any useful data?

E.g. 3KB/sec or less instead of 300KB/sec, IIRC - see C) below

A) Reading articles on high-performance (e.g. HP-Apache) suggest the use of buffering at all levels.

B) Past experience with hard disk strongly suggest the use of buffering. The typical UNIX OS will read/write in chunks of 4 or 8 KB, which is fine. It's also my experimental lower limit below which disk i/o performance will seriously degrade.

It's not trivial to measure this at application level, since the OS will buffer disks no matter what you do there.

C) Old experience with CLISP's inspector strongly suggested buffering.
I used line-buffered output IIRC. The effect I observed without it was the following: CLISP would output one (or a few) character of HTML. The OS would then dispatch the browser, which would read these few chars, display a little bit or discover that it needs more, the OS then would switch back to CLISP. etc. etc. That was unusably slow: IIRC more than two seconds to finish displaying the output!

Moving the inspector from WRITE-CHAR to (line?) buffered output made the CLISP inspector + browser usable.

With sockets and without buffering, your application is indeed free to degrade i/o performance, unlike disk i/o.

>- How much slower is it to read one char at a time?
Orders of magnitude in my chosen examples. There were also a couple of qualitative threads on the topics of buffering and READ-CHAR vs READ-SEQUENCE in comp.lang.lisp during the last years.

>I expect you can get a large factor if you only compare something like
>cpu usage for a single read that returns, say, 1K bytes to 1K tests
Exactly.
>and reads on single bytes.  However I don't think that reflects the
>true cost of most services.
I'd say: Thank the frameworks which surround the services, not the service.

> If your
>server works well in that environment I think it deserves credit for
>that regardless of how it might fare in some other environment.

My experience is: the environment deserves credit for managing to make badly written applications/servers still perform at viable performance.

The servers ought to do buffering before throwing their stuff out. Remember one of the HP-Apache papers.

With sockets, it's IMHO the application's job to deal with buffering. Setting the TCP options "favour_throuput" or "favour_responsiveness" (name/sp?) is not enough for that.
The read() and write() the application does have a direct impact on how packets go to the fibre, and are controlled by the application, while MTU etc. are global values.

Regards,
	Jorg Hohle.

---

From: [email-masked] (Don Cohen)
Date: Mon Sep 30 08:05:08 2002

> E.g. 3KB/sec or less instead of 300KB/sec, IIRC - see C) below
Ok, here's my experiment on a 200MHz PC:

 (compile (defun f (file n) (with-open-file (s file)
  (setf (stream-element-type s) '(unsigned-byte 8))
  (loop for i below n do 
  (if (ext:READ-BYTE-WILL-HANG-P s) (return :hang)(read-byte s))))))

This is pretty much how my servers read.  I assume the cost of
read-byte and read-byte-will-hang-p is not greatly different for
socket streams than for files.  Files are just a lot more convenient
to test than sockets.

 [5]> (time (f "/tmp/25MB" 10000))

 Real time: 0.019146 sec.
 Run time: 0.01 sec.
 Space: 4584 Bytes
 NIL
 [6]> (time (f "/tmp/25MB" 100000))

 Real time: 0.239794 sec.
 Run time: 0.23 sec.
 Space: 4584 Bytes
 NIL
 [7]> (time (f "/tmp/25MB" 1000000))

 Real time: 2.016425 sec.
 Run time: 2.0 sec.
 Space: 4584 Bytes
 NIL
 [8]> 

 > A) Reading articles on high-performance (e.g. HP-Apache) suggest
   the use of buffering at all levels. 
I guess I am buffering at every level - I put the bytes I read into an
array for further lisp processing.  Similarly I generally have an
array of bytes waiting for output and do the output in an analogous way.
Clearly at the lower level IO is still linear.  Your argument is just
that copying n bytes in memory has a much lower constant factor than
doing n calls to read-byte and read-byte-will-hang-p.  I grant that.
But these calls are not very expensive compared to the other
processing required per character.

 > My experience is: the environment deserves credit for managing to
   make badly written applications/servers still perform at viable
   performance. 
And my view is that an application is not badly written in that case.

I agree that more control over lower levels is a good thing.  I just
don't agree that any application written as above is necessarily a
"toy".  To me that means not suitable for "real" work.  For instance,
I consider a mail server to be doing "real" work.  For the last year
(or two, I forget) ALL of my mail has been received by just such a
server.