unofficial mirror of guile-devel@gnu.org 
 help / color / mirror / Atom feed
From: Mark H Weaver <mhw@netris.org>
To: Stefan Monnier <monnier@iro.umontreal.ca>
Cc: guile-devel@gnu.org
Subject: Re: Efficiency of `map`
Date: Sat, 10 Jun 2017 00:28:09 -0400	[thread overview]
Message-ID: <87poec4gbq.fsf@netris.org> (raw)
In-Reply-To: <jwvzidi2y4q.fsf-monnier+gmane.lisp.guile.devel@gnu.org> (Stefan Monnier's message of "Thu, 08 Jun 2017 13:27:05 -0400")

Stefan Monnier <monnier@iro.umontreal.ca> writes:

>>   (define (map f l)
>>     (if (pair? l)
>>         (cons (f (car l))
>>               (map f (cdr l)))
>>         '()))
>>
>> whereas we used to have to write code like this in order to support long
>> lists without overflowing the stack:
>>
>>   (define (map f l)
>>     (let loop ((l l) (out '()))
>>       (if (pair? l)
>>           (loop (cdr l) (cons (f (car l)) out))
>>           (reverse out))))
>
> Ignoring stack usage, is the first version faster or slower than the second?
> [ Or is the speed difference negligible?  ]

I don't have time to perform proper benchmarks, but some admittedly
inadequate measurements on my Thinkpad X200 suggest that the first
version is about 1.5% faster, mainly because it makes a lot less work
for the GC.  See below for details of what I did.  

> How 'bout using a side-effecting `reverse!` (like Lisp's nreverse)?

We can't do that, because in the presence of first-class continuations
where the 'f' passed to map may return more than once, using mutation to
build the result list will change the result for some programs.  Both
modern scheme standards (R6RS and R7RS) explicitly specify that "If
multiple returns occur from map, the values returned by earlier returns
are not mutated".

      Mark


mhw@jojen ~$ guile
GNU Guile 2.2.2
Copyright (C) 1995-2017 Free Software Foundation, Inc.

Guile comes with ABSOLUTELY NO WARRANTY; for details type `,show w'.
This program is free software, and you are welcome to redistribute it
under certain conditions; type `,show c' for details.

Enter `,help' for help.
scheme@(guile-user)> (define (map1 f l) (if (pair? l) (cons (f (car l)) (map f (cdr l))) '()))
scheme@(guile-user)> (define (map2 f l) (let loop ((l l) (out '())) (if (pair? l) (loop (cdr l) (cons (f (car l)) out)) (reverse out))))
scheme@(guile-user)> (define var #f)
scheme@(guile-user)> (define test-list (iota 100000))
scheme@(guile-user)> ,time (let loop ((i 1000)) (when (positive? i) (set! var (map1 1+ test-list)) (loop (- i 1))))
;; 13.121194s real time, 14.072380s run time.  2.346036s spent in GC.
scheme@(guile-user)> ,time (let loop ((i 1000)) (when (positive? i) (set! var (map1 1+ test-list)) (loop (- i 1))))
;; 13.006867s real time, 13.940452s run time.  2.263353s spent in GC.
scheme@(guile-user)> ,time (let loop ((i 1000)) (when (positive? i) (set! var (map1 1+ test-list)) (loop (- i 1))))
;; 13.079656s real time, 13.946879s run time.  2.197271s spent in GC.
scheme@(guile-user)> ,time (let loop ((i 1000)) (when (positive? i) (set! var (map2 1+ test-list)) (loop (- i 1))))
;; 13.029591s real time, 15.312230s run time.  5.601020s spent in GC.
scheme@(guile-user)> ,time (let loop ((i 1000)) (when (positive? i) (set! var (map2 1+ test-list)) (loop (- i 1))))
;; 13.041985s real time, 15.306287s run time.  5.581253s spent in GC.
scheme@(guile-user)> ,time (let loop ((i 1000)) (when (positive? i) (set! var (map2 1+ test-list)) (loop (- i 1))))
;; 12.003391s real time, 13.421369s run time.  3.662504s spent in GC.
scheme@(guile-user)> ,time (let loop ((i 1000)) (when (positive? i) (set! var (map2 1+ test-list)) (loop (- i 1))))
;; 11.993404s real time, 13.367805s run time.  3.496328s spent in GC.
scheme@(guile-user)> ,time (let loop ((i 1000)) (when (positive? i) (set! var (map2 1+ test-list)) (loop (- i 1))))
;; 11.964659s real time, 13.362942s run time.  3.544227s spent in GC.
scheme@(guile-user)> ,time (let loop ((i 1000)) (when (positive? i) (set! var (map1 1+ test-list)) (loop (- i 1))))
;; 12.619559s real time, 13.153612s run time.  1.471917s spent in GC.
scheme@(guile-user)> ,time (let loop ((i 1000)) (when (positive? i) (set! var (map1 1+ test-list)) (loop (- i 1))))
;; 12.600161s real time, 13.136094s run time.  1.476574s spent in GC.
scheme@(guile-user)> ,time (let loop ((i 1000)) (when (positive? i) (set! var (map1 1+ test-list)) (loop (- i 1))))
;; 12.584059s real time, 13.142257s run time.  1.478289s spent in GC.
scheme@(guile-user)> ,time (let loop ((i 1000)) (when (positive? i) (set! var (map1 1+ test-list)) (loop (- i 1))))
;; 12.626346s real time, 13.146946s run time.  1.466499s spent in GC.
scheme@(guile-user)> ,time (let loop ((i 1000)) (when (positive? i) (set! var (map2 1+ test-list)) (loop (- i 1))))
;; 12.009064s real time, 13.361389s run time.  3.690290s spent in GC.
scheme@(guile-user)> ,time (let loop ((i 1000)) (when (positive? i) (set! var (map2 1+ test-list)) (loop (- i 1))))
;; 11.970217s real time, 13.317716s run time.  3.352065s spent in GC.
scheme@(guile-user)> 

Here are the 4 best times for each procedure:

map1:
;; 12.619559s real time, 13.153612s run time.  1.471917s spent in GC.
;; 12.600161s real time, 13.136094s run time.  1.476574s spent in GC.
;; 12.584059s real time, 13.142257s run time.  1.478289s spent in GC.
;; 12.626346s real time, 13.146946s run time.  1.466499s spent in GC.

averages of 4 best times:
;; 12.607531s real time, 13.144727s run time.  1.473320s spent in GC.


map2:
;; 12.009064s real time, 13.361389s run time.  3.690290s spent in GC.
;; 11.993404s real time, 13.367805s run time.  3.496328s spent in GC.
;; 11.964659s real time, 13.362942s run time.  3.544227s spent in GC.
;; 11.970217s real time, 13.317716s run time.  3.352065s spent in GC.

averages of 4 best times:
;; 11.984336s real time, 13.352463s run time.  3.520728s spent in GC.



  reply	other threads:[~2017-06-10  4:28 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-05-30  9:26 [PATCH] On Hurd, don't use not implemented madvise() manolis837
2017-05-30 19:41 ` Mark H Weaver
2017-06-01 16:27   ` Manolis Ragkousis
2017-06-01 23:29     ` Mark H Weaver
2017-06-02  8:39       ` tomas
2017-06-03  2:00         ` Mark H Weaver
2017-06-03  6:41           ` tomas
2017-06-08 17:27           ` Efficiency of `map` (was: [PATCH] On Hurd, don't use not implemented madvise()) Stefan Monnier
2017-06-10  4:28             ` Mark H Weaver [this message]
2017-06-10  4:38               ` Efficiency of `map` Nala Ginrut
2017-06-11  5:27                 ` Mark H Weaver
2017-07-02 18:57       ` [PATCH] On Hurd, silently ignore ENOSYS for madvise() manolis837
2017-07-02 18:57         ` manolis837

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/guile/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=87poec4gbq.fsf@netris.org \
    --to=mhw@netris.org \
    --cc=guile-devel@gnu.org \
    --cc=monnier@iro.umontreal.ca \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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 read-only IMAP folder(s) and NNTP newsgroup(s).