250 likes | 404 Views
11 Speed & Debug. Functional Programming. Speed - Introduction. Lisp is really two languages: A language for writing fast programs A language for writing programs fast
E N D
11 Speed & Debug Functional Programming
Speed - Introduction • Lisp is really two languages: • A language for writing fast programs • A language for writing programs fast • In the early stage, you can trade speed for convenience. Then, you can refine critical portions to make them faster once your program begins to crystallize
Speed - The Bottleneck Rule • Optimization • Should be focused on bottlenecks • Should not begin too early • Should begin with algorithms • Programs tend to have a few bottlenecks that account for a great part of the execution time • “Most of the running time in none-IO-bound programs is concentrated in about 3% of the source text. Optimizing these parts of the program will make it run noticeably faster; optimizing the rest of the program will be a waste of time on comparison.”, by Knuth
Speed - The Bottleneck Rule • Optimize the program from the top • Make sure that you’re using the most efficient algorithm before you resort to low-level coding tricks • Decisions about algorithms have to be made early
Speed - Compilation • Five parameters control the way your code is compiled • Speed: the speed of the code produced by the compiler • Compilation-speed: the speed at which your program will be compiled • Safety: the amount of error-checking done in the object code • Space: the size and memory needs of the object code • Debug: the amount of information retained for debugging
Speed - Compilation • The compilation parameters are not real variables. They are assigned weights from 0 (unimportant) to 3 (most important) in declarations • (defunbottlenect (…) (do (…) (…) (do (…) (…) (declare (optimize (speed 3) (safety 0))) …))) • Add such declarations until the code was finished and tested
Speed - Compilation • Inline function • Without the cost of calling functions • Similar to macros • Macro • Since macros use mere textual substitution, this may result in unintended side-effects and inefficiency due to re-evaluation of arguments and order of operations • Compiler errors within macros are often difficult to understand, because they refer to the expanded code • Recursive functions cannot be inlined • If an inlined function is redefined, we have to recompile any function that calls it
Speed - Compilation • (declaim(inline single?))(defun single? (lst) (and (consplst) (null (cdrlst)))) • (defunfoo (x) (single? (bar x))) • When foo is compiled(defunfoo (x) (let ((lst (bar x))) (and (consplst) (null (cdrlst)))))
Speed - Type Declarations • In most languages, you have to declare the type of each variable, and the variable can only hold values of that type → strongly typed language • Common List uses manifest typing (run-time typing) • Type information is attached to the data objects • Type information is used at run-time • Variables can hold objects of any type • We have to pay for this flexibility in speed • The function have to look at the types of each of its arguments at run-time • If we just want one type, say, fixnum, this is an inefficient way
Type Declarations • In Common Lisp, type declarations are completely optional • Global declarations are made with declaim • (declaim (typefixnum *count*)) ;type can be omitted • Local declarations are made with declare • (defun poly (a b x) (declare (fixnum a b x)) (+ (* a (expt x 2)) (* b x)))
Type Declarations • Type declarations are particularly important for the contents of complex objects, including arrays and structures • Declarations can improve efficiencyThe compiler can determine the types of arguments to functions and represent these objects more efficients • If nothing is known about the type of elements an array will contain, it has to be represented in memory as a block of pointers • If it is known that the array will only contain, say, double-floats, then the array can be represented as a block of actual double-floats
Type Declarations • (setf x (vector 1.234d0 2.345d0 3.456d0) y (make-array 3 :element-type ‘double-float) (aref y 0) 1.234d0 (aref y 1) 2.345d0 (aref y 2) 3.456d0)
Speed - Type Declarations • (setf a (make-array ‘(1000 1000) :element-type ‘single-float :initial-element 1.0))(defunsim-elts (a) (declare (type (simple-array single-float (1000 1000)) a)) (let ((sum 0.0)) (declare (type single-float sum)) (dotimes (r 1000) (dotimes (c 1000) (incf sum (aref a r c)))) sum))
Speed - Type Declarations • > (compile-file “..\\test\\declare.lsp”) • > (load “..\\test\\declare.fas”) • > (time (sum-elts a)) Real time: 0.823 sec. Run time: 0.8112052 sec. Space: 11999988 Bytes GC: 1, GC time: 0.1092007 sec. 1000000.0 • > (compile-file “..\\test\\nodeclare.lsp”) • > (load “..\\test\\nodeclare.fas”) • > (time (sum-elts-nodeclare a))Real time: 1.026 sec. Run time: 0.9984064 sec. Space: 11999988 Bytes GC: 2, GC time: 0.2340015 sec. 1000000.0
Speed - Garbage Avoidance • Dynamic allocation is slow • Programs that cons a lot tend to run slowly in Lisp implementations with bad garbage collectors • Until recently, most Lisp implementations have had bad garbage collectors • Efficient programs should cons as little as possible
Speed - Garbage Avoidance • One of the easiest way is to use destructive functions • When you know it’s safe to modify a list, you can use delete instead of remove, nreverse instead of reverse, and so on
Speed - Garbage Avoidance • Use a pre-allocated vector instead of building it using conses • > (setf *print-array* t)T> (setfvec (make-array 10 :fill-pointer 2 :initial-element nil))#(NIL NIL)> (length vec)2> (vector-push ‘a vec)2> vec#(NIL NIL A)> (vector-pop vec)A> vec#(NIL NIL)
Speed – Fast Operators • svref is more efficient than aref • eq is more efficient than eql • (reduce #’+ ‘(1 2 3)) is more efficient than (apply #’+ ‘(1 2 3))
Debug • trace • (defun count-atoms (expression) (if (atom expression) 1 (+ (count-atoms (first expression)) (count-atoms (rest expresstion))))) • > (count-atoms ‘((this is) (a test)))7 ;we expect 4
Debug • > (trace count-atoms);; Tracing function COUNT-ATOMS. (COUNT-ATOMS) • (count-atoms '((this is) (a test)))
Debug • 4. Trace: (COUNT-ATOMS '(A TEST)) • 5. Trace: (COUNT-ATOMS 'A) • 5. Trace: COUNT-ATOMS ==> 1 • 5. Trace: (COUNT-ATOMS '(TEST)) • 6. Trace: (COUNT-ATOMS 'TEST) • 6. Trace: COUNT-ATOMS ==> 1 • 6. Trace: (COUNT-ATOMS 'NIL) • 6. Trace: COUNT-ATOMS ==> 1 • 5. Trace: COUNT-ATOMS ==> 2 • 4. Trace: COUNT-ATOMS ==> 3 • 4. Trace: (COUNT-ATOMS 'NIL) • 4. Trace: COUNT-ATOMS ==> 1 • 3. Trace: COUNT-ATOMS ==> 4 • 2. Trace: COUNT-ATOMS ==> 7 • 7 2. Trace: (COUNT-ATOMS '((THIS IS) (A TEST))) 3. Trace: (COUNT-ATOMS '(THIS IS)) 4. Trace: (COUNT-ATOMS 'THIS) 4. Trace: COUNT-ATOMS ==> 1 4. Trace: (COUNT-ATOMS '(IS)) 5. Trace: (COUNT-ATOMS 'IS) 5. Trace: COUNT-ATOMS ==> 1 5. Trace: (COUNT-ATOMS 'NIL) 5. Trace: COUNT-ATOMS ==> 1 4. Trace: COUNT-ATOMS ==> 2 3. Trace: COUNT-ATOMS ==> 3 3. Trace: (COUNT-ATOMS '((A TEST)))
Debug • (defun count-atoms (expression) (cond ((atom expression) 1) ((null expression) 0) (t (+ (count-atoms (first expression)) (count-atoms (rest expression)))))) • > (count-atoms ‘((this is) (a test)))7
Debug • (defun count-atoms (expression) (cond ((null expression) 0) ((atom expression) 1) (t (+ (count-atoms (first expression)) (count-atoms (rest expression)))))) • > (count-atoms ‘((this is) (a test)))4 • > (untrace count-atoms)
Debug • > (dribble “record.txt”) ;記錄在toplevel的過程……> (dribble) ;stop dribbling • > (ed “record.txt”)
Final Projects • Write a Lisp program with at least 15 functions • Due June 30 • Report • Source code • Demo