Hi Chickeneers,

yesterday I found that simply having a (use mailbox) in some code had a
huge impact (more than a factor of 3) at the performance of the
resulting executable.  Without using the mailbox stuff at all.

Meanwhile I figured out that this has nothing at all to do with the
mailbox egg.  But _all_ with the use of srfi-1.

But how is this possible?

Attached some test code.  As I ran into it from mailbox I prepared a
stripped down version of it to play with.  Towards the end of the file
there is a (use srfi-1) which makes all the difference.

So far I found (compiling the code with -O5 but similar things happen
with less aggressive optimization):

a) Using chicken 4.9.1 there is absolutely no difference.  Using srfi-1
or not I get roughly 100 messages passed per ms on my machine.  (But I
have to include the forced gc; see comment in the code.)

b) A slightly different version which avoids allocations in the queue
runs without the forced gc and yields about 160 ms^-1 on chicken 4.9.1
Again no difference whether or not I (use srfi-1) anywhere.

Now the interesting bits:

c) On master (built almost two weeks ago) I get - when (use srfi-1) is
included - about 180 ms^-1.  Those ~5% faster sound about right to me.

d) Comment out the (use srfi-1) at line 163 and it goes down to about
_50_ per millisecond!

e) The same happens for the alternative, allocation free version (not
attached), which uses vectors instead of pairs.


Speculating: The code I wrote has nothing to do with the difference.

But I'm confused.  Neither scheduler.scm nor srfi-18 seem to have any
dependency on srfi-1.  Also srfi-1 seems not to overwrite any global
bindings.

Should we simply always (use srfi-1) if we also (use srfi-18).  Looks
like a workaround, but not like the right thing to do.

How could I boil this down to the real reason?

Best

/Jörg
(declare
 ;;(unit mailbox)
 ;; requirements
 (disable-interrupts)
 ;; promises
 (strict-types)
 (usual-integrations)
 (no-procedure-checks-for-usual-bindings)
 (inline)
 (local)

 (no-bound-checks)
 (no-procedure-checks-for-usual-bindings)
 (bound-to-procedure
  ##sys#schedule
  ##sys#current-exception-handler)

  (always-bound
    ##sys#current-thread)
 )

(module
 mailbox3
 (
  make-mailbox
  mailbox?
  mailbox-empty?
  mailbox-send!
  mailbox-receive!
  )

(import scheme extras srfi-18)
(import (except chicken add1 sub1))

(: mailbox? (* --> boolean : (struct <mailbox>)))
(define-record-type <mailbox>
  (internal-make-mailbox condition head tail pred)
  mailbox?
  (condition %mailbox-condition)
  (head %mailbox-head %mailbox-head-set!)
  (tail %mailbox-tail %mailbox-tail-set!)
  (pred mailbox-predicate))

(cond-expand
 (never
  (define-inline (mailbox-condition mb) (%mailbox-condition mb))
  (define-inline (mailbox-head mb) (%mailbox-head mb))
  (define-inline (mailbox-head-set! mb v) (%mailbox-head-set! mb v))
  (define-inline (mailbox-tail mb) (%mailbox-tail mb))
  (define-inline (mailbox-tail-set! mb v) (%mailbox-tail-set! mb v)))
 (else
  (define-inline (mailbox-condition mb) (##sys#slot mb 1))
  (define-inline (mailbox-head mb) (##sys#slot mb 2))
  (define-inline (mailbox-head-set! mb v) (##sys#setslot mb 2 v))
  (define-inline (mailbox-tail mb) (##sys#slot mb 3))
  (define-inline (mailbox-tail-set! mb v) (##sys#setslot mb 3 v))
  ))

(: make-mailbox (* --> (struct <mailbox>)))
(define (make-mailbox name)
  (let ((x (cons #f '())))
    (internal-make-mailbox
     (make-condition-variable name) x x #f)))

(: mailbox-empty? ((struct <mailbox>) --> boolean))
(define (mailbox-empty? mb)
  (null? (cdr (mailbox-head mb))))

(: mailbox-number-of-items ((struct <mailbox>) -> fixnum))
(define (mailbox-number-of-items mb)
  (length (cdr (mailbox-head mb))))

(define-inline (%dequeue-message mb)
  (mailbox-head-set! mb (##sys#slot #;cdr (mailbox-head mb) 1))
  (##sys#slot #;car (mailbox-head mb) 0))

(: mailbox-send! ((struct <mailbox>) * -> undefined))
(define (mailbox-send! mailbox obj)
  (let ((p (cons obj '())))
    (##sys#setslot #;set-cdr! (mailbox-tail mailbox) 1 p)
    (mailbox-tail-set! mailbox p))
  #;(condition-variable-signal! (mailbox-condition mailbox))
  (let ((x (mailbox-condition mailbox))) (if (thread? x) (thread-resume! x)))
  )

(: mailbox-receive! ((struct <mailbox>) -> *))
(define (mailbox-receive! mailbox)
  ;;(dbg "receive-message! ~a ~a ~a" (current-thread) (mailbox-name mailbox) (##sys#slot (mailbox-condition mailbox) 2))
  (let loop ()
    (if (null? (##sys#slot (mailbox-head mailbox) 1))
	(begin
	  (##sys#setslot mailbox 1 ##sys#current-thread)
	  (thread-suspend! ##sys#current-thread)
	  
	  (##sys#setslot mailbox 1 #f)
	  (loop))
	(let ((obj (%dequeue-message mailbox)))
	  obj))))

) ;; module mailbox


(module
 test
 (test-run)
 (import scheme chicken srfi-18 extras)
 (import mailbox3)
 
;;(include "bench.scm")
;;----------

(define mb (make-mailbox 'm0))

(define turns 1000000)

(define tw
  (make-thread
   (lambda ()
     (do ((i 0 (add1 i)))
	 ((= i turns))
       (mailbox-send! mb i)
       ;; Must be active for my chicken 4.9.1 .
       ;; Otherwise will run into
       ;; "[panic] out of memory - heap full while resizing - execution terminated"
       ;;
       ;;(if (= (modulo i 1000) 999) (gc #t))
       (thread-yield!)
       ))
   'w))

(define tr
  (make-thread
   (lambda ()
     (do ((i 0 (add1 i)))
	 ((= i turns))
       (mailbox-receive! mb)
       (thread-yield!)
       ))
   'r))


(define (test-run)
(thread-start! tr)
(define t0 (current-milliseconds))
(thread-start! tw)

(thread-join! tr)

(define t1 (current-milliseconds))

(format #t "~a message passings in ~a (~a per ms)\n " turns (- t1 t0) (/ turns (- t1 t0)))
)

 
;;----------

)

;; !!!!!!!!!!!!!!!!
;;  THIS (use srfi-1) is the declaration which makes the effect.
;;  Comment out and it runs for me at about 

(use srfi-1)

(use srfi-18)
(use extras)

(import test)
(test-run)
_______________________________________________
Chicken-users mailing list
Chicken-users@nongnu.org
https://lists.nongnu.org/mailman/listinfo/chicken-users

Reply via email to