Skip to content
This repository was archived by the owner on Aug 13, 2025. It is now read-only.

Commit e65eb59

Browse files
committed
Try to reduce consing a reasonable amount.
1 parent 8d8ad6c commit e65eb59

File tree

5 files changed

+179
-56
lines changed

5 files changed

+179
-56
lines changed

controller.lisp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,11 @@
5858
(with-simple-restart (skip "Skip processing the message batch.")
5959
(loop for i from 0
6060
for thing across queue
61-
do (with-simple-restart (continue "Continue processing messages, skipping ~a" thing)
62-
(pass pipeline thing))
63-
(setf (aref queue i) 0)))
61+
do (unwind-protect
62+
(with-simple-restart (continue "Continue processing messages, skipping ~a" thing)
63+
(pass pipeline thing))
64+
(maybe-release-message thing)
65+
(setf (aref queue i) 0))))
6466
(setf (fill-pointer queue) 0))
6567

6668
(defmethod controller-loop ((controller controller))
@@ -89,5 +91,6 @@
8991
(vector-push-extend message (queue controller)))
9092
(bt:condition-notify (queue-condition controller)))
9193
(T
92-
(pass (pipeline controller) message))))
94+
(pass (pipeline controller) message)
95+
(maybe-release-message message))))
9396
NIL)

message.lisp

Lines changed: 37 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
(defvar *levels* NIL)
44
(defvar *default-message-class* 'message)
5+
(defvar *pool* NIL)
56

67
(declaim (inline log-object))
78
(defun log-object (object)
@@ -27,31 +28,52 @@
2728
(unless (every #'keywordp categories)
2829
(cl:error "Categories must be keywords.")))
2930

31+
(defmethod print-object ((message message) stream)
32+
(print-unreadable-object (message stream :type T)
33+
(format-message stream message)))
34+
35+
(defmethod format-message ((null null) thing)
36+
(with-output-to-string (stream)
37+
(format-message stream thing)))
38+
39+
(defmethod format-message ((vector string) thing)
40+
(with-output-to-string (stream vector)
41+
(format-message stream thing)))
42+
3043
(defmethod format-message ((stream stream) (message message))
31-
(format stream "~a [~5,a] ~{<~a>~}: ~a"
32-
(format-time :timestamp (timestamp message))
44+
(format-time :timestamp (timestamp message) :stream stream)
45+
(format stream " [~5,a] ~{<~a>~}: "
3346
(level message)
34-
(categories message)
35-
(format-message NIL (content message))))
47+
(categories message))
48+
(format-message stream (content message)))
3649

37-
(defmethod format-message ((null null) message)
38-
(princ-to-string message))
50+
(defmethod format-message ((stream stream) thing)
51+
(princ thing stream))
3952

40-
(defmethod format-message ((null null) (func function))
41-
(princ-to-string (funcall func)))
53+
(defmethod format-message (target (func function))
54+
(format-message target (funcall func)))
4255

43-
(defmethod format-message ((null null) (message message))
44-
(with-output-to-string (stream)
45-
(format-message stream message)))
56+
(setf *pool* (make-pool (lambda () (allocate-instance (find-class 'message)))))
4657

47-
(defmethod print-object ((message message) stream)
48-
(print-unreadable-object (message stream :type T)
49-
(format-message stream message)))
58+
(defun maybe-release-message (message)
59+
(when (and *pool* (eq (type-of message) 'message))
60+
(release-instance *pool* message)))
61+
62+
(defun maybe-draw-message (level categories content class &rest initargs)
63+
(if (and *pool* (eq class 'message))
64+
(let ((instance (draw-instance *pool*)))
65+
(setf (timestamp instance) (get-universal-time))
66+
(setf (thread instance) (bt:current-thread))
67+
(setf (level instance) level)
68+
(setf (categories instance) categories)
69+
(setf (content instance) content)
70+
instance)
71+
(apply #'make-instance class :level level :categories categories :content content initargs)))
5072

5173
(defun log-message (level categories content &optional (class *default-message-class*) &rest initargs)
5274
(unless (listp categories)
5375
(setf categories (list categories)))
54-
(log-object (apply #'make-instance class :level level :categories categories :content content initargs)))
76+
(log-object (apply #'maybe-draw-message level categories content class initargs)))
5577

5678
(defmethod log (level categories (datum string) &rest args)
5779
(log-message level categories (apply #'format NIL datum args)))

pipes.lisp

Lines changed: 43 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -54,42 +54,49 @@
5454

5555
(defmethod format-message ((faucet repl-faucet) (message message))
5656
(if (ansi-colors faucet)
57-
(flet ((f (color format &rest args)
58-
(when color (format (output faucet) "~c[38;5;~am" (code-char #x1b) color))
59-
(format (output faucet) "~?" format args)
60-
(when color (format (output faucet) "~c[0m" (code-char #x1b))))
61-
(fb (fg bg format &rest args)
62-
(format (output faucet) "~c[38;5;~am~c[48;5;~am" (code-char #x1b) fg (code-char #x1b) bg)
63-
(format (output faucet) "~?" format args)
64-
(format (output faucet) "~c[0m" (code-char #x1b))))
65-
(f 7 "~a " (format-time :timestamp (timestamp message)))
66-
(f 8 "[")
67-
(f (case (level message)
68-
(:trace 8)
69-
(:debug 7)
70-
(:info NIL)
71-
(:warn 9)
72-
(:error 1)
73-
(:severe 11)
74-
(:fatal 11)) "~5,a" (level message))
75-
(f 8 "] ")
76-
(dolist (category (categories message))
77-
(f 8 "<")
78-
(f NIL "~a" category)
79-
(f 8 ">"))
80-
(f 8 ": ")
81-
(flet ((msg (fg &optional bg)
82-
(if bg
83-
(fb fg bg "~a" (format-message NIL (content message)))
84-
(f fg "~a" (format-message NIL (content message))))))
85-
(case (level message)
86-
(:trace (msg 8))
87-
(:debug (msg 7))
88-
(:info (msg NIL))
89-
(:warn (msg 9))
90-
(:error (msg 1))
91-
(:severe (msg 11))
92-
(:fatal (msg 0 11)))))
57+
(let ((stream (output faucet)))
58+
(macrolet ((f (color format &rest args)
59+
`(progn
60+
,@(when color `((format stream "~c[38;5;~am" (code-char #x1b) ,color)))
61+
,(if (stringp format)
62+
`(format stream ,format ,@args)
63+
`(progn ,format ,@args))
64+
,@(when color `((format stream "~c[0m" (code-char #x1b))))))
65+
(fb (fg bg format &rest args)
66+
`(progn
67+
(format stream "~c[38;5;~am~c[48;5;~am" (code-char #x1b) ,fg (code-char #x1b) ,bg)
68+
,(if (stringp format)
69+
`(format stream ,format ,@args)
70+
`(progn ,format ,@args))
71+
(format stream "~c[0m" (code-char #x1b)))))
72+
(f 7 (format-time :timestamp (timestamp message) :stream stream))
73+
(f 8 " [")
74+
(f (case (level message)
75+
(:trace 8)
76+
(:debug 7)
77+
(:info NIL)
78+
(:warn 9)
79+
(:error 1)
80+
(:severe 11)
81+
(:fatal 11)) "~5,a" (level message))
82+
(f 8 "] ")
83+
(dolist (category (categories message))
84+
(f 8 "<")
85+
(f NIL "~a" category)
86+
(f 8 ">"))
87+
(f 8 ": ")
88+
(flet ((msg (fg &optional bg)
89+
(if bg
90+
(fb fg bg (format-message stream (content message)))
91+
(f fg (format-message stream (content message))))))
92+
(case (level message)
93+
(:trace (msg 8))
94+
(:debug (msg 7))
95+
(:info (msg NIL))
96+
(:warn (msg 9))
97+
(:error (msg 1))
98+
(:severe (msg 11))
99+
(:fatal (msg 0 11))))))
93100
(call-next-method)))
94101

95102
(defmethod stop ((faucet repl-faucet)))

pool.lisp

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
(in-package #:org.shirakumo.verbose)
2+
3+
(defstruct (pool
4+
(:predicate NIL)
5+
(:copier NIL)
6+
(:constructor %make-pool (instances constructor)))
7+
(instances NIL :type simple-vector)
8+
(index 0 :type (unsigned-byte 32))
9+
(constructor NIL :type function)
10+
(lock (bt:make-lock "verbose pool")))
11+
12+
(defmethod print-object ((pool pool) stream)
13+
(print-unreadable-object (pool stream :type T :identity T)
14+
(format stream "~d/~d" (pool-index pool) (length (pool-instances pool)))))
15+
16+
(defun make-pool (constructor &optional (initial-size 128))
17+
(let ((instances (make-array initial-size)))
18+
(map-into instances constructor)
19+
(%make-pool instances constructor)))
20+
21+
(defun clear-pool (pool)
22+
(bt:with-lock-held ((pool-lock pool))
23+
;; The order is no longer guaranteed to be consistent
24+
;; and due to out of order release/draw, some objects may
25+
;; not be present at all, or present multiple times, we have
26+
;; to refill the pool entirely.
27+
(setf (pool-index pool) 0)
28+
(map-into (pool-instances pool) (pool-constructor pool))
29+
pool))
30+
31+
(defun resize-pool (pool new-size &key (lock T))
32+
(flet ((resize ()
33+
(let* ((old (pool-instances pool))
34+
(new (adjust-array old new-size)))
35+
(loop for i from (length old) below new-size
36+
do (setf (aref new i) (funcall (pool-constructor pool))))
37+
(setf (pool-instances pool) new))))
38+
(if lock
39+
(bt:with-lock-held ((pool-lock pool))
40+
(resize))
41+
(resize))))
42+
43+
(defun draw-instance (pool)
44+
#+atomics-cas-struct-slot
45+
(loop
46+
(let ((instances (pool-instances pool))
47+
(index (pool-index pool)))
48+
(cond ((< index (length instances))
49+
(when (atomics:cas (pool-index pool) index (1+ index))
50+
(return (aref instances index))))
51+
((= index (length instances))
52+
;; We filled, so we are responsible for resizing it.
53+
(resize-pool pool (* 2 index) :lock T))
54+
(T
55+
;; Re-use the lock as a barrier, then retry
56+
(bt:with-lock-held ((pool-lock pool)))))))
57+
#-atomics-cas-struct-slot
58+
(bt:with-lock-held ((pool-lock pool))
59+
(let ((instances (pool-instances pool))
60+
(index (pool-index pool)))
61+
;; We filled, so we are responsible for resizing it.
62+
(loop while (<= (length instances) index)
63+
do (resize-pool pool (* 2 index) :lock NIL)
64+
(setf instances (pool-instances pool)))
65+
(setf (pool-index pool) (1+ index))
66+
(aref instances index))))
67+
68+
(defun release-instance (pool instance)
69+
#+atomics-cas-struct-slot
70+
(loop
71+
(let ((index (pool-index pool))
72+
(instances (pool-instances pool)))
73+
(cond ((= 0 index)
74+
;; What the fuck... (double-release?)
75+
(return))
76+
;; FIXME: I don't think this is quite consistent...
77+
((= index (length instances))
78+
;; What the fuck... (how? wait for a resize maybe?)
79+
())
80+
((atomics:cas (pool-index pool) index (1- index))
81+
(return (setf (aref instances index) instance))))))
82+
#-atomics-cas-struct-slot
83+
(bt:with-lock-held ((pool-lock pool))
84+
(let ((index (pool-index pool)))
85+
(cond ((= 0 index)
86+
(cl:warn "What the fuck?"))
87+
(T
88+
(setf (aref (pool-instances pool) index) instance)
89+
(setf (pool-index pool) (1- index)))))))

verbose.asd

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
(asdf:defsystem verbose
22
:name "Verbose"
3-
:version "2.1.0"
3+
:version "2.2.0"
44
:author "Yukari Hafner <shinmera@tymoon.eu>"
55
:license "zlib"
66
:description "A logging framework using the piping library."
@@ -11,6 +11,7 @@
1111
:components ((:file "package")
1212
(:file "toolkit")
1313
(:file "controller")
14+
(:file "pool")
1415
(:file "message")
1516
(:file "pipes")
1617
(:file "convenience")
@@ -21,5 +22,6 @@
2122
(:file "documentation"))
2223
:depends-on (:piping
2324
:bordeaux-threads
25+
:atomics
2426
:dissect
2527
:documentation-utils))

0 commit comments

Comments
 (0)