[Openmcl-devel] Transfer the contents of a file to a tcp stream

Christian Nybø chr at nybo.no
Thu May 5 13:01:13 PDT 2005


On May 3, 2005, at 23:22, Gary Byers wrote:

> Hmm.  I was just about to recommend cheating (using sendfile) if it's
> available (it's available on Linux, but doesn't seem to be there on
> Darwin/OSX).

 From googling sendfile mac os x, I got the impression that sendfile (or 
transmitfile as it is called in NT)  is not yet present in mac os x.

I downloaded apache and had a look at writev_it_all in server/core.c, 
but I'm unfamiliar with reading production code in C, so I don't know 
whether apache does anything particular when sendfile is absent.

> You can cheat a bit
> by doing something like:
>
> (defun copy-file-to-socket (file-stream socket-stream)
>   (let* ((file-fd (ccl::stream-device file-stream :input))
>          (socket-fd (ccl::stream-devices socket-stream :output))
> 	 (bufsize 8192)) ; arbitrary
>     (force-output socket-stream) ; flush any buffered output
>     (%stack-block ((buffer bufsize))
>     ;; You could just say (#_lseek ...) here, but Linux's
>     ;; #_lseek may have difficulty with large file offsets.
>     (ccl::fd-lseek file-fd 0 #$SEEK_SET)
>     (loop
>       (let* ((nread (#_read file-fd buf bufsize)))
>         (cond ((zerop nread) (return))
>               ((minusp nread)(error ...))
>               (t (let* ((nwritten (#_write socket-fd buf nread)))
>                    (when (< nwritten nread)
>                           ;;; Handle partial writes, errors
>                          )))))))))
>
> That's an artist's conception and may be a little buggy, but using
> a single buffer and bypassing most of the buffered stream overhead
> might get things -closer- to Apache's performance.

nwritten was set to -1 quite often, so I added a loop that would try 
until #_write returned the right number of bytes.  That's probably not 
the right way, is it?

(defun copy-file-to-socket (file-stream socket-stream)
  (declare (optimize (speed 3) (safety 0)))
   (let* ((file-fd (ccl::stream-device file-stream :input))
          (socket-fd (ccl::stream-device socket-stream :output))
	 (bufsize 2048))		; arbitrary
     (force-output socket-stream)	; flush any buffered output
     (ccl::%stack-block ((buf bufsize));; %stack-block is not visible 
without the package prefix (chr)
       ;; You could just say (#_lseek ...) here, but Linux's
       ;; #_lseek may have difficulty with large file offsets.
       (ccl::fd-lseek file-fd 0 #$SEEK_SET)
        (loop
	  (let* ((nread (#_read file-fd buf bufsize)))
	    (cond ((zerop nread) (return))
		  ((minusp nread) (error "Read error: ~A." (ccl::%strerror 
(ccl::%get-errno))))
		  (t (loop (let* ((nwritten (#_write socket-fd buf nread)))
			     (when (= nwritten nread) (return)) ;; try again till we get it 
right
			     )))))))))

I got a speedup from using file descriptors:

First, how apache performs on a 20 megabyte file on the loopback
interface, with 10 repeated downloads.  I collected the reported MB/s
speed from wget, and looked at the average.

(avg 7.50 6.56 12.69 6.75 7.01 10.27 6.94 8.27 14.74 7.75) 8.84 MB/s

my own code:

(avg 5.45 10.78 5.73 4.90 1.44 2.82 6.05 8.00 5.88 10.22) 6.12 MB/s

Next, how the code with file descriptors, suggested by Gary Byers, 
performs:

(avg 6.06 7.23 8.68 7.31 6.92 9.92 7.86 5.95 11.03 7.19) 7.81 MB/s
-- 
chr




More information about the Openmcl-devel mailing list