Whacking down memory usage of CLSQL-SYS::MAP-QUERY-FOR-EFFECT
Jason H <[email protected]> Sat, 10 Jul 2010 11:52:54 -0700
| Newsgroups | gmane.lisp.clsql.general |
|---|---|
| Message-ID | <[email protected]> |
Hi,
A few days ago, I posted a message about CLSQL:MAP-QUERY using more memory
than CLSQL:QUERY. After poking around a bit deeper in the source code,
I came up with a hack to greatly reduce the amount of memory used by
(CLSQL:MAP-QUERY NIL ...). With this hack, the profile report of the
following 2 test functions:
(defun test-1 ()
(clsql:map-query nil
#'identity
(clsql:query "select c1, c2, c3, c4, c5, c6, c7 from Cells")))
(defun test-2 ()
(clsql:query "select c1, c2, c3, c4, c5, c6, c7 from Cells") )
is
seconds | consed | calls | sec/call | name
---------------------------------------------------------
46.778 | 147,860,728 | 1 | 46.777996 | TEST-2
44.614 | 14,768,136 | 1 | 44.613995 | TEST-1
The old profile report without this hack was:
seconds | consed | calls | sec/call | name
---------------------------------------------------------
46.957 | 147,875,120 | 1 | 46.957000 | TEST-2
44.244 | 161,921,920 | 1 | 44.244000 | TEST-1
The profiling runs were done with the win32 version of sbcl 1.0.37.12.
The hack is attached at the end of this email and is for the ODBC back-end
of clsql-5.0.2.
Best,
Jason
;;-------------------------------------------------------------------------------------------------------------
(in-package :clsql-sys)
;; new hack
(defvar *map-query-for-effect-row*)
;; replace the old MAP-QUERY-FOR-EFFECT.
(defun map-query-for-effect (function query-expression database result-types)
(multiple-value-bind (result-set columns)
(database-query-result-set query-expression database :full-set nil
:result-types result-types)
(let ((flatp (and (= columns 1)
(typep query-expression 'sql-query)
(slot-value query-expression 'flatp))))
(when result-set
(unwind-protect
(let ((*map-query-for-effect-row* (make-list columns)))
(do ((row (make-list columns)))
((not (database-store-next-row result-set database row))
nil)
(if flatp
(apply function row)
(funcall function row))))
(database-dump-result-set result-set database))))))
(in-package :odbc-dbi)
;; new hack adapted from DB-FETCH-QUERY-RESULTS.
(defun db-fetch-single-row (query)
(declare (type odbc-query query)
(special clsql-sys::*map-query-for-effect-row*))
(when (query-active-p query)
(with-slots (column-data-ptrs column-c-types column-sql-types
column-out-len-ptrs column-precisions hstmt
computed-result-types)
query
(let ((cols-fetched 0))
(unless (= (%sql-fetch hstmt) odbc::$SQL_NO_DATA_FOUND)
(loop for result-type across computed-result-types
for data-ptr across column-data-ptrs
for c-type across column-c-types
for sql-type across column-sql-types
for out-len-ptr across column-out-len-ptrs
for precision across column-precisions
for j from 0 ; column count is zero based in lisp
for rest on clsql-sys::*map-query-for-effect-row*
do
(setf (car rest)
(cond ((< 0 precision (query-width query))
(read-data data-ptr c-type sql-type out-len-ptr result-type))
((zerop (get-cast-long out-len-ptr))
nil)
(t
(read-data-in-chunks hstmt j data-ptr c-type sql-type
out-len-ptr result-type))))
(incf cols-fetched)))
(values clsql-sys::*map-query-for-effect-row* query cols-fetched)))))
;; replace the old FETCH-ROW.
(defun fetch-row (query &optional (eof-errorp t) eof-value)
(multiple-value-bind (row query count) (db-fetch-single-row query)
(cond
((zerop count)
(close-query query)
(when eof-errorp
(error 'clsql:sql-database-data-error
:message "ODBC: Ran out of data in fetch-row"))
eof-value)
(t
row))))