unofficial mirror of help-gnu-emacs@gnu.org
 help / color / mirror / Atom feed
From: Pascal Bourguignon <spam@mouse-potato.com>
Subject: Re: make-hash-table :size
Date: 29 Aug 2004 13:59:33 +0200	[thread overview]
Message-ID: <87r7pqp32y.fsf@thalassa.informatimago.com> (raw)
In-Reply-To: usma69rtk.fsf@ID-87814.user.uni-berlin.de

Oliver Scholz <alkibiades@gmx.de> writes:

> Pascal Bourguignon <spam@mouse-potato.com> writes:
> 
> > Kai Grossjohann <kai@emptydomain.de> writes:
> >
> >> Joost Kremers <joostkremers@yahoo.com> writes:
> >> 
> >> > i'm writing a program in emacs that makes rather extensive use of small
> >> > hash tables, about a dozen elemens or so. i'm going over my code again, to
> >> > see if i can improve things here and there, and i suddenly realise that
> >> > MAKE-HASH-TABLE takes a :size argument.
> >> 
> >> I wonder if hash tables are really faster than alists in this case.
> >> Have you tried both?
> >
> > Well, it seems that yes, non-empty emacs hash tables are faster than
> > alists...
> [...]
> 
> For about a dozen elements? I doubt that. I would assume that the
> small overhead in hash key access is likely to make alists more
> efficient for such few elements. And indeed, profiling supports that
> assumption:
> 
> 
> (defconst test-alist
>   (let ((lst (make-list 14 '(lirum . larum))))
>     (append lst '((alpha . beta)))))
> 
> (defconst test-hash
>   (let ((hash (make-hash-table :size 15 :test 'eq)))
>     (puthash 'alpha 'beta hash)
>     hash))
> 
> (defun test-hash-access ()
>   (gethash 'alpha test-hash))
> 
> (defun test-alist-access ()
>   (cdr (assq 'alpha test-alist)))
> 
> 
> (defun test-run ()
>   (interactive)
>   (dolist (func '(test-hash-access test-alist-access))
>     (unless (byte-code-function-p
> 	     (symbol-function func))
>       (byte-compile func)))
>   (elp-instrument-function 'test-hash-access)
>   (elp-instrument-function 'test-alist-access)
>   (dotimes (ignore-me 10000)
>     (test-hash-access)
>     (test-alist-access))
>   (elp-results))
> 
> 
> On my Emacs 21.3 on a 1.5 GHtz Athlon M-x test-run gives:
> 
> Function Name      Call Count  Elapsed Time  Average Time
> =================  ==========  ============  ============
> test-hash-access   10000       0.09          9e-006
> test-alist-access  10000       0.04          4e-006

Actually, I wrote a more complete benchmark before delivering my conclusion:


--------------------(hash-table.el)--------------------------------------

;; Let's include some CL stuff:
(require 'cl)

(defconstant internal-time-units-per-second 1000000
  "Common-Lisp: Implementation-dependent number of time units in a second.

[cltl2] This value is an integer, the implementation-dependent number of
internal time units in a second. (The internal time unit must be
chosen so that one second is an integral multiple of it.)"
    );;internal-time-units-per-second


(defun pjb-cl%%emacs-time-to-seconds (et)
  "PRIVATE.
PRE:     et is a time in emacs time format, ie. a list ( h l [us])
         with h and l being in [0..65535] and us in [0..999999].
RETURN:  et expressed as a scalar."
  (+ (let ((h (nth 0 et))) (if (< h 1024) (* h 65536) (* h 65536.0)))
     (nth 1 et)
     (let ((us (nth 2 et))) (if (or (null us) (= 0 us)) 0 (/ us 1000000.0))))
  );;pjb-cl%%emacs-time-to-seconds


(defun get-internal-real-time ()
  "Common-Lisp: Current clock time is returned in Internal Time format.

RETURN: The current time is returned as a single integer in Internal
        Time format. This time is relative to an arbitrary time base,
        but the difference between the values of two calls to this
        function will be the amount of elapsed real time between the
        two calls, measured in the units defined by
        internal-time-units-per-second.

NOTE:   Emacs implementation returns the number of micro-seconds
        since 1970-01-01 00:00:00 as a float.
"
  (* internal-time-units-per-second
     (pjb-cl%%emacs-time-to-seconds (current-time)))
  );;get-internal-real-time


(defmacro time (&rest body)
  "Common-Lisp:  time evaluates form in the current environment (lexical and \
dynamic). A call to time can be compiled.
DO:      time prints various timing data and other information to trace output.
         The nature and format of the printed information is
         implementation-defined . Implementations are encouraged to provide
         such information as elapsed real time, machine run time, and storage
         management statistics.
URL: http://www.informatimago.com/local/lisp/HyperSpec/Body/m_time.htm
"
  (let ((start (gensym))
        (result (gensym))
        (stop (gensym))
        (time (gensym)))
    `(let* ( (,start  (get-internal-real-time))
             (,result (progn ,@body))
             (,stop   (get-internal-real-time)) 
             (,time   (- ,stop ,start)) )
       (princ (format  "Time: %12.3f µs" ,time)))));;time


;; The meat of the benchmark:

(defparameter +repeat+ 1000)

(defun hash-fill  (size test)
  (let ((table (make-hash-table :test test :size size)))
    (dotimes (i size) (setf (gethash (format "%d" i) table) i))
    table))

(defun hash-get-keys (table)
  (let ((keys '()))
    (maphash (lambda (k v) (push k keys)) table)
    keys))

(defun hash-fetch (table test key)  (gethash key table))

(defun alist-fill  (size test)
  (let ((alist '()))
    (dotimes (i size) (push (cons (format "%d" i) i) alist))
    alist))

(defun alist-get-keys (alist)   (mapcar (function car) alist))

(defun alist-fetch (table test key)  (assoc key table :test test))

(defun tally ()
  (dotimes (size 15)
    (dolist (test '(eq eql equal))
      (dolist (cl '((hash-fill  hash-get-keys  hash-fetch  :hash)
                    (alist-fill alist-get-keys alist-fetch :alist)))
        (let* ((compound (funcall (first cl) size test))
               (keys     (funcall (second cl) compound)))
          (princ (format "%20s" (list (fourth cl) size test)))
          (time
           (dotimes (i +repeat+)
             (dolist (k keys)  (funcall (third cl) compound test k))
             (dotimes (k size) (funcall (third cl) compound test k))))
          (terpri))) (terpri)) (terpri)));;tally


;; end of hash-table.el


(progn (byte-compile-file "hash-table.el")
       (load-file "hash-table.elc")
       (setf +repeat+ 10000)
       (tally))

==>

        (:hash 0 eq)Time:     9519.125 µs
       (:alist 0 eq)Time:     9780.250 µs

       (:hash 0 eql)Time:     9074.875 µs
      (:alist 0 eql)Time:    89049.125 µs

     (:hash 0 equal)Time:   159119.125 µs
    (:alist 0 equal)Time:     9649.125 µs


        (:hash 1 eq)Time:    39810.000 µs
       (:alist 1 eq)Time:   581610.000 µs

       (:hash 1 eql)Time:    47351.125 µs
      (:alist 1 eql)Time:   509031.125 µs

     (:hash 1 equal)Time:    76388.875 µs
    (:alist 1 equal)Time:   471781.000 µs


        (:hash 2 eq)Time:    89989.000 µs
       (:alist 2 eq)Time:   904411.125 µs

       (:hash 2 eql)Time:   112393.000 µs
      (:alist 2 eql)Time:  1076395.000 µs

     (:hash 2 equal)Time:   110723.875 µs
    (:alist 2 equal)Time:  1403409.000 µs


        (:hash 3 eq)Time:   157952.125 µs
       (:alist 3 eq)Time:   992513.875 µs

       (:hash 3 eql)Time:   118767.000 µs
      (:alist 3 eql)Time:  1898227.000 µs

     (:hash 3 equal)Time:   119163.000 µs
    (:alist 3 equal)Time:  1825305.000 µs


        (:hash 4 eq)Time:   225544.875 µs
       (:alist 4 eq)Time:  1772409.000 µs

       (:hash 4 eql)Time:   183063.000 µs
      (:alist 4 eql)Time:  1964644.000 µs

     (:hash 4 equal)Time:   195405.875 µs
    (:alist 4 equal)Time:  2681090.750 µs


        (:hash 5 eq)Time:   239486.875 µs
       (:alist 5 eq)Time:  2732085.000 µs

       (:hash 5 eql)Time:   203306.875 µs
      (:alist 5 eql)Time:  2946955.000 µs

     (:hash 5 equal)Time:   248938.125 µs
    (:alist 5 equal)Time:  3488260.000 µs


        (:hash 6 eq)Time:   242912.125 µs
       (:alist 6 eq)Time:  2372359.000 µs

       (:hash 6 eql)Time:   252884.875 µs
      (:alist 6 eql)Time:  3490458.000 µs

     (:hash 6 equal)Time:   274726.875 µs
    (:alist 6 equal)Time:  4264734.000 µs


        (:hash 7 eq)Time:   683985.875 µs
       (:alist 7 eq)Time:  2923297.875 µs

       (:hash 7 eql)Time:   321365.125 µs
      (:alist 7 eql)Time:  4333614.750 µs

     (:hash 7 equal)Time:   322398.000 µs
    (:alist 7 equal)Time:  4733676.250 µs


        (:hash 8 eq)Time:   348424.875 µs
       (:alist 8 eq)Time:  3196606.875 µs

       (:hash 8 eql)Time:   379910.000 µs
      (:alist 8 eql)Time:  6019358.875 µs

     (:hash 8 equal)Time:   381468.000 µs
    (:alist 8 equal)Time:  6545720.125 µs


        (:hash 9 eq)Time:   418741.000 µs
       (:alist 9 eq)Time:  3517043.750 µs

       (:hash 9 eql)Time:   410253.000 µs
      (:alist 9 eql)Time:  5796592.250 µs

     (:hash 9 equal)Time:   949384.875 µs
    (:alist 9 equal)Time:  6860958.125 µs


       (:hash 10 eq)Time:   439682.000 µs
      (:alist 10 eq)Time:  3974476.125 µs

      (:hash 10 eql)Time:   465018.000 µs
     (:alist 10 eql)Time: 13181789.125 µs

    (:hash 10 equal)Time:  1056310.250 µs
   (:alist 10 equal)Time: 10347398.000 µs


       (:hash 11 eq)Time:   525230.250 µs
      (:alist 11 eq)Time:  4749943.125 µs

      (:hash 11 eql)Time:   500923.125 µs
     (:alist 11 eql)Time: 10596731.875 µs

    (:hash 11 equal)Time:   528394.000 µs
   (:alist 11 equal)Time:  9048454.125 µs


       (:hash 12 eq)Time:  1025217.000 µs
      (:alist 12 eq)Time:  5466906.125 µs

      (:hash 12 eql)Time:   523550.000 µs
     (:alist 12 eql)Time:  8851365.125 µs

    (:hash 12 equal)Time:   536098.000 µs
   (:alist 12 equal)Time:  9239335.125 µs


       (:hash 13 eq)Time:   574123.000 µs
      (:alist 13 eq)Time:  5268079.000 µs

      (:hash 13 eql)Time:   568025.750 µs
     (:alist 13 eql)Time: 10765734.875 µs

    (:hash 13 equal)Time:   564960.000 µs
   (:alist 13 equal)Time:  9361194.125 µs


       (:hash 14 eq)Time:   585877.000 µs
      (:alist 14 eq)Time:  5390757.125 µs

      (:hash 14 eql)Time:   635772.000 µs
     (:alist 14 eql)Time: 12009460.000 µs

    (:hash 14 equal)Time:   677022.875 µs
   (:alist 14 equal)Time: 14172797.000 µs


-- 
__Pascal Bourguignon__                     http://www.informatimago.com/

Our enemies are innovative and resourceful, and so are we. They never
stop thinking about new ways to harm our country and our people, and
neither do we.

  reply	other threads:[~2004-08-29 11:59 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2004-08-23 20:36 make-hash-table :size Joost Kremers
2004-08-23 20:45 ` Stefan Monnier
2004-08-23 21:00   ` Joost Kremers
2004-08-28 14:07 ` Kai Grossjohann
     [not found] ` <mailman.506.1093702372.1998.help-gnu-emacs@gnu.org>
2004-08-28 16:03   ` Pascal Bourguignon
2004-08-29 10:11     ` Oliver Scholz
2004-08-29 11:59       ` Pascal Bourguignon [this message]
2004-08-29 21:10         ` Oliver Scholz
2004-08-29 21:44           ` Oliver Scholz
2004-08-29 21:48             ` Oliver Scholz
2004-08-30  1:25               ` Pascal Bourguignon

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/emacs/

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

  git send-email \
    --in-reply-to=87r7pqp32y.fsf@thalassa.informatimago.com \
    --to=spam@mouse-potato.com \
    /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).