Dictionary vs Ldata speed

Dictionary vs Ldata speed

office
Enthusiast Enthusiast
1,125 Views
5 Replies
Message 1 of 6

Dictionary vs Ldata speed

office
Enthusiast
Enthusiast

Why is dictionary is slower than ldata?

Here is an example of dictionary usage code.

 

 

;;;;;;;~~~~~~~~~~~~~~~~~~~~~~~~~~~~;;;;;;;
;;;;;;; The two working functions: ;;;;;;;
;;;;;;;____________________________;;;;;;;
;;
;;Sample use:
;; (dict-put "mydict" "mykey" '(1 (2 ("3" (44.4) (5.5 6.6 7.7) 8)) 999999))
;; (dict-get "mydict" "mykey")
;;POSSIBLE IMPROVEMENTS:
;; implement dict-append ( APPEND PREPEND )


;; Get A [Key] Value From Dictionary [Name]
(defun dict-get ( name key ) ;; name and key As strings
    (x-relist                  ;; decode the data!
      (cddr (member '(100 . "AcDbXrecord") ;; new (280) code group in r15
        (dict-getrawdata name key)))))

;; Put A [Val]ue Into [Name] Dictionary Under [Key]
;; Dictionary is created automatically if was not existing
;; three arguments As strings
;; NAME may also be an entity name of some existing dictionary
(defun dict-put ( name key val )
  (dict-clear name key) ;;  clear the old value under this Key
  (dictadd
    (setq name (cond ((dict-name name)) ((dict-new name)) ))
    key
    (entmakex
      (cons '(0 . "XRECORD")
        (cons '(100 . "AcDbXrecord")
          (x-enlist val)))) ) ;; encode the data!
  name )  ;; return the dictionary altered


;;;;;;;~~~~~~~~~~~~~~~~~~~~~~~~~~~~;;;;;;;
;;        XDICTIONARY SUPPORT
;;  two more similar functions to use:
;;;;;;;~~~~~~~~~~~~~~~~~~~~~~~~~~~~;;;;;;;

(defun xdict-put ( ename key val )
 (addxdict ename (dict-put nil key val)))

(defun xdict-get ( ename key )
 (dict-get (cdr (assoc 360 (entget ename))) key))


;;;;;;; utility functions in use ;;;;;;;

;; a general utility -- pop first value off the list
(defun pop (a / b)
  (setq b (eval a))
  (set a (cdr b))
  (car b))

;; general functions re/en-list, just to try things out
;; re-list can be also used in decoding xdata
(defun re-list ( lst / tok )
 (re-list-aux))

(defun re-list-aux()
 (cond
  ((atom lst) lst)
  ((/= "}" (setq tok (pop 'lst)))
   (cons (if (= "{" tok) (re-list-aux) tok)
         (re-list-aux)))))

(defun en-list ( lst )
  (cond
   ((null lst) lst)
   ((atom lst) (list lst))
   ((cons "{"    ;; add the markers around the list!
      (reverse
        (cons "}"
          (reverse
            (apply 'append  ;; open the lists
              (mapcar 'en-list lst) ;; recursion!
      ))))))))

;; a special encoding function for Xrecords to be used in dictionaries
(defun x-enlist ( lst )  ;; encode!
  (cond
   ((null lst) lst)
   ((atom lst)           ;; automatic code groups
     (cond
      ((= 'REAL (type lst))
        (list (cons 40 lst)))
      ((= 'INT (type lst))
       (if               ;; special handling of long integers
         (< -32768 lst 32767)
          (list (cons 70 lst))
          (list (cons 41 (float lst)))))
      ((= 'STR (type lst))
        (list (cons  1 lst)))
      (T nil)))
   ((and (cdr lst) (atom (cdr lst)))
      (list lst))      ;; pass dotted pair AS IS -- must be valid!!
   ((and (= (length lst) 3)
         (apply 'and (mapcar 'numberp lst)))
     (list (cons 10 lst)))
   ((cons '(2 . "{")
     (reverse
      (cons '(2 . "}")
       (reverse
        (apply 'append
         (mapcar 'x-enlist lst) ))))))))


(defun x-relist ( lst / tok )  ;; decode it!
 (car (x-relist-aux)))

(defun x-relist-aux()
 (cond
  ((null lst) nil)
  ((not (equal '(2 . "}") (setq tok (pop 'lst))))
   (cons (if (equal '(2 . "{") tok)
           (x-relist-aux)
           (cond      ;; special processing of TOK to recover
             ((= 41 (car tok)) ;; long integers back
              (fix (cdr tok)))
             (T
              (cdr tok)))
           )
         (x-relist-aux)))))


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; dictionary functions for R14
;; POSSIBLE IMPROVEMENTS: support trees of dictionaries
;;      now: all custom dictionaries are under the Root.
;;
;; Get Dictionary Object Name From Root Dictionary
(defun dict-name ( name ) ;; name As string or ename of exist dict
 (cond
   ( (= 'STR (type name))
     (cdr (assoc -1 (dictsearch (namedobjdict) name))))
   ( (and (= 'ENAME (type name))
          (= "AcDbDictionary" (cdr (assoc 100 (entget name)))) )
     name )))

;; Remove Dictionary [Name] From Root Dictionary
(defun dict-remove ( name / d0 ) ;; name As string
  (if (dictsearch (Setq d0 (namedobjdict)) name)
    (dictremove d0 name))
  )

;; Make A New Empty Dictionary and put into into Root; Reset Existing.
(defun dict-new ( name / d0 ) ;; name As string
  (cond
    ( (/= 'STR (type name))
      (dict-new-orphan) )
    ('t
      (if (dictsearch (Setq d0 (namedobjdict)) name)
        (dictremove d0 name))
      (dictadd d0 name
        (dict-new-orphan) ))
    ))

;; (dict-new-ex parent name) parent := ROOT | ename (dict or reg for xdict)

(defun dict-new-orphan ()
  (entmakex '((0 . "DICTIONARY")(100 . "AcDbDictionary"))))

;; List A Dictionary As Pairs {Name . Object Name}
(defun dict-list ( dname / d nl ) ;; dname As object name or string else ROOT
  (cond
    ((and (Setq d (cond
                    ((= 'ENAME (type dname))
                      dname)
                    ((= 'STR (type dname))
                      (dict-name dname))
                    (T (namedobjdict))))
          (Setq d (entget d)))
      (while (setq d (member (assoc 3 d) d))
        (if (or (= 350 (caadr d)) ;; regular dict
                (= 360 (caadr d)) ;; extension dict
                )
          (setq nl (cons (cons (cdar d) (cdadr d)) nl)))
        (setq d (cdr d)))
      (reverse nl))))

;; Get Raw [Name] Dictionary Data for [Key]
(defun dict-getrawdata ( name key / d ) ;; name and key As strings
  (cond
    ((='STR (type name))
      (if (setq d (dict-name name))
        (dictsearch d key)))
    ((='ENAME (type name))
      (dictsearch name key) )
    ( (dictsearch (namedobjdict) key) )))

;; Clear The [Key] From Dictionary [Name]
(defun dict-clear ( name key / d) ;; name and key As strings
  (if (setq d (dict-name name))
    (dictremove d key)))

;; add xdictionary DK into the carrier entity data ECD
;; you can use it like (addxdict _entname_ (dict-put nil _key_ _val_))
;; eg. (addxdict (tblobjname"layer""0") (dict-put nil "MYDATA" 1.23))
;; to associate some data with layer "0".
;; you'll retrieve this data later with
;;   (setq d (cdr(assoc 360 (entget (tblobjname "layer""0")))))
;; and
;;   (dict-get d _key_)
(defun addxdict (ECD DK / TT)
 (cond
   ( (= 'ENAME (type ecd))
     (addxdict (entget ecd) dk) )
   ( (or (atom ecd) (atom (car ecd)) (not (atom (cdar ecd))))
     nil )
   ( (= 'STR (type dk))
     (addxdict ecd (dictsearch (namedobjdict) dk)) )
   ( (/= 'ENAME (type dk))
     nil )
   ( (/= "AcDbDictionary" (cdr (assoc 100 (entget dk))))
     nil )
   ( (assoc 102 ecd)
   ;; there already is an extension dictionary attached to this entity
   ;; can I prepend another dictionary into it?? No! :-(
     (while (/= (caar ecd) 102)
       (setq tt  (cons (car ecd) tt)
             ecd (cdr  ecd)))
     (setq tt  (cons (car ecd) tt) ;; {
           ecd (cdr ecd))
     (while (/= 102 (caar ecd))    ;; }
       (setq ecd (cdr ecd)))

     (setq ecd (cons (cons 360 dk) ecd) )
     (while tt
       (setq ecd (cons (car tt) ecd)
             tt  (cdr tt)))
     (entmod ecd))
   ( (assoc 100 ecd)
     (while (/= (caar ecd) 100)
       (setq tt (cons (car ecd) tt)
            ecd (cdr  ecd)))
     (setq ecd
       (cons (quote (102 . "{ACAD_XDICTIONARY"))
         (cons (cons 360 dk)
           (cons (quote (102 . "}")) ecd))))
     (while tt
       (setq ecd (cons (car tt) ecd)
             tt  (cdr tt)))
     (entmod ecd))
 ))

;; EOF SaveData.lsp

 

 

 

(setq startRunTime (getvar 'millisecs))
(repeat 1000 (vlax-ldata-put "Test" "Test" '(1 (2 ("3" (44.4) (5.5 6.6 7.7) 8)) 999999)))
(rtos (/ (- (getvar 'millisecs) startRunTime) 1000.0) 2 2)
(setq startRunTime (getvar 'millisecs))
(repeat 1000 (dict-put "mydict" "mykey" '(1 (2 ("3" (44.4) (5.5 6.6 7.7) 8)) 999999)))
(rtos (/ (- (getvar 'millisecs) startRunTime) 1000.0) 2 2)

 

 

The ldata is much faster. Any idea?

1,126 Views
5 Replies
Replies (5)
Message 2 of 6

_gile
Consultant
Consultant

Hi,

My 2 cents:

Ldata are stored the same way as Xrecords in the AutoCAD database (in the named objects dictoionary or in extension dictionaries).

The speed difference is certainly due to the fact that built-in LISP functions are compiled from C/C++ instead of interpreted user defined AutoLISP expressions.

You should get similar result between any built-in function and its corresponding defun.



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 3 of 6

office
Enthusiast
Enthusiast

Ok, so what would be the solution?

There is any fast Dictionary lisp code as the Ldata or should I pass the list data to C# and to store/get the data via C#?

0 Likes
Message 4 of 6

_gile
Consultant
Consultant

As I said upper, none 'defun' function should be faster than the built-in ones, so if the execution speed is an issue (is it really?), you should abandon AutoLISP which is an interpretated language for a compiled one as C# or C++, but you will much more time to write code.

 What's wrong with LISP data (vlax-ldata-* function)? The only remaining inconvenient with LISP data is they cannot be accessed from other languages.



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

0 Likes
Message 5 of 6

office
Enthusiast
Enthusiast

The vlax-ldata functions are Ok. The LISPSYS system variable introduced in AutCAD 2021 is my problem. You have to change this variable and restart the autocad to retrieve the old vlax-ldata. VLIDE is much better than VS Code. And maybe the VLIDE will be removed in a feature release. If you develop with VS Code and save some Ldata you can't access the LDATA with previous version AutoCAD.

0 Likes
Message 6 of 6

martti.halminen
Collaborator
Collaborator

As _gile mentioned, compiled code tends to be somewhat faster; if I test a function both in AutoLISP and in (compiled) Common Lisp, the CL version is typically about three times faster.

- Additionally, internal AutoCAD parts have access to places where user code can't go, so that might be architecturally more efficient.

 

The may be some room for improvement in your Lisp code. Generally, memory allocation is quite expensive: you can run hundreds of CDR calls in the time it takes to run one CONS call.

In addition to raw CONS, each APPEND and REVERSE call conses up a fresh copy of the whole list.

 

So you might investigate whether there are some more lightweight ways to encode your data.