summaryrefslogtreecommitdiff
path: root/tests/test-lorem-optimum-benchmark.el
diff options
context:
space:
mode:
Diffstat (limited to 'tests/test-lorem-optimum-benchmark.el')
-rw-r--r--tests/test-lorem-optimum-benchmark.el227
1 files changed, 227 insertions, 0 deletions
diff --git a/tests/test-lorem-optimum-benchmark.el b/tests/test-lorem-optimum-benchmark.el
new file mode 100644
index 00000000..d3ca2873
--- /dev/null
+++ b/tests/test-lorem-optimum-benchmark.el
@@ -0,0 +1,227 @@
+;;; test-lorem-optimum-benchmark.el --- Performance tests for lorem-optimum.el -*- lexical-binding: t; -*-
+
+;;; Commentary:
+;; Benchmark and performance tests for the Markov chain implementation.
+;;
+;; These tests measure:
+;; - Learning time scaling with input size
+;; - Multiple learning operations (exposes key rebuild overhead)
+;; - Generation time scaling
+;; - Memory usage (hash table growth)
+;;
+;; Performance baseline targets (on modern hardware):
+;; - Learn 1000 words: < 10ms
+;; - Learn 10,000 words: < 100ms
+;; - 100 learn operations of 100 words each: < 500ms (current bottleneck!)
+;; - Generate 100 words: < 5ms
+
+;;; Code:
+
+(require 'ert)
+(require 'testutil-general)
+
+;; Add modules directory to load path
+(add-to-list 'load-path (expand-file-name "modules" user-emacs-directory))
+
+;; Load the module
+(require 'lorem-optimum)
+
+;;; Benchmark Helpers
+
+(defun benchmark-time (func)
+ "Time execution of FUNC and return milliseconds."
+ (let ((start (current-time)))
+ (funcall func)
+ (let ((end (current-time)))
+ (* 1000.0 (float-time (time-subtract end start))))))
+
+(defun generate-test-text (word-count)
+ "Generate WORD-COUNT words of test text with some repetition."
+ (let ((words '("lorem" "ipsum" "dolor" "sit" "amet" "consectetur"
+ "adipiscing" "elit" "sed" "do" "eiusmod" "tempor"
+ "incididunt" "ut" "labore" "et" "dolore" "magna" "aliqua"))
+ (result '()))
+ (dotimes (i word-count)
+ (push (nth (mod i (length words)) words) result)
+ (when (zerop (mod i 10))
+ (push "." result)))
+ (mapconcat #'identity (nreverse result) " ")))
+
+(defun benchmark-report (name time-ms)
+ "Report benchmark NAME with TIME-MS."
+ (message "BENCHMARK [%s]: %.2f ms" name time-ms))
+
+;;; Learning Performance Tests
+
+(ert-deftest benchmark-learn-1k-words ()
+ "Benchmark learning 1000 words."
+ (let* ((text (generate-test-text 1000))
+ (chain (cj/markov-chain-create))
+ (time (benchmark-time
+ (lambda () (cj/markov-learn chain text)))))
+ (benchmark-report "Learn 1K words" time)
+ (should (< time 50.0)))) ; Should be < 50ms
+
+(ert-deftest benchmark-learn-10k-words ()
+ "Benchmark learning 10,000 words."
+ (let* ((text (generate-test-text 10000))
+ (chain (cj/markov-chain-create))
+ (time (benchmark-time
+ (lambda () (cj/markov-learn chain text)))))
+ (benchmark-report "Learn 10K words" time)
+ (should (< time 500.0)))) ; Should be < 500ms
+
+(ert-deftest benchmark-learn-100k-words ()
+ "Benchmark learning 100,000 words (stress test)."
+ :tags '(:slow)
+ (let* ((text (generate-test-text 100000))
+ (chain (cj/markov-chain-create))
+ (time (benchmark-time
+ (lambda () (cj/markov-learn chain text)))))
+ (benchmark-report "Learn 100K words" time)
+ ;; This may be slow due to key rebuild
+ (message "Hash table size: %d bigrams"
+ (hash-table-count (cj/markov-chain-map chain)))))
+
+;;; Multiple Learning Operations (Exposes Quadratic Behavior)
+
+(ert-deftest benchmark-multiple-learns-10x100 ()
+ "Benchmark 10 learn operations of 100 words each."
+ (let ((chain (cj/markov-chain-create))
+ (times '()))
+ (dotimes (i 10)
+ (let* ((text (generate-test-text 100))
+ (time (benchmark-time
+ (lambda () (cj/markov-learn chain text)))))
+ (push time times)))
+ (let ((total (apply #'+ times))
+ (avg (/ (apply #'+ times) 10.0))
+ (max-time (apply #'max times)))
+ (benchmark-report "10x learn 100 words - TOTAL" total)
+ (benchmark-report "10x learn 100 words - AVG" avg)
+ (benchmark-report "10x learn 100 words - MAX" max-time)
+ (message "Times: %S" (nreverse times))
+ ;; Note: Watch if later operations are slower (quadratic behavior)
+ (should (< total 100.0))))) ; Total should be < 100ms
+
+(ert-deftest benchmark-multiple-learns-100x100 ()
+ "Benchmark 100 learn operations of 100 words each (key rebuild overhead)."
+ :tags '(:slow)
+ (let ((chain (cj/markov-chain-create))
+ (times '())
+ (measurements '()))
+ (dotimes (i 100)
+ (let* ((text (generate-test-text 100))
+ (time (benchmark-time
+ (lambda () (cj/markov-learn chain text)))))
+ (push time times)
+ ;; Sample measurements every 10 iterations
+ (when (zerop (mod i 10))
+ (push (cons i time) measurements))))
+ (let ((total (apply #'+ times))
+ (avg (/ (apply #'+ times) 100.0))
+ (first-10-avg (/ (apply #'+ (last times 10)) 10.0))
+ (last-10-avg (/ (apply #'+ (seq-take times 10)) 10.0)))
+ (benchmark-report "100x learn 100 words - TOTAL" total)
+ (benchmark-report "100x learn 100 words - AVG" avg)
+ (benchmark-report "100x learn - First 10 AVG" first-10-avg)
+ (benchmark-report "100x learn - Last 10 AVG" last-10-avg)
+ (message "Sampled times (iteration, ms): %S" (nreverse measurements))
+ (message "Hash table size: %d bigrams"
+ (hash-table-count (cj/markov-chain-map chain)))
+ ;; This exposes the quadratic behavior: last operations much slower
+ (when (> last-10-avg (* 2.0 first-10-avg))
+ (message "WARNING: Learning slows down significantly over time!")
+ (message " First 10 avg: %.2f ms" first-10-avg)
+ (message " Last 10 avg: %.2f ms" last-10-avg)
+ (message " Ratio: %.1fx slower" (/ last-10-avg first-10-avg))))))
+
+;;; Generation Performance Tests
+
+(ert-deftest benchmark-generate-100-words ()
+ "Benchmark generating 100 words."
+ (let* ((text (generate-test-text 1000))
+ (chain (cj/markov-chain-create)))
+ (cj/markov-learn chain text)
+ (let ((time (benchmark-time
+ (lambda () (cj/markov-generate chain 100)))))
+ (benchmark-report "Generate 100 words" time)
+ (should (< time 20.0))))) ; Should be < 20ms
+
+(ert-deftest benchmark-generate-1000-words ()
+ "Benchmark generating 1000 words."
+ (let* ((text (generate-test-text 10000))
+ (chain (cj/markov-chain-create)))
+ (cj/markov-learn chain text)
+ (let ((time (benchmark-time
+ (lambda () (cj/markov-generate chain 1000)))))
+ (benchmark-report "Generate 1000 words" time)
+ (should (< time 100.0))))) ; Should be < 100ms
+
+;;; Tokenization Performance Tests
+
+(ert-deftest benchmark-tokenize-10k-words ()
+ "Benchmark tokenizing 10,000 words."
+ (let* ((text (generate-test-text 10000))
+ (time (benchmark-time
+ (lambda () (cj/markov-tokenize text)))))
+ (benchmark-report "Tokenize 10K words" time)
+ (should (< time 50.0)))) ; Tokenization should be fast
+
+;;; Memory/Size Tests
+
+(ert-deftest benchmark-chain-growth ()
+ "Measure hash table growth with increasing input."
+ (let ((chain (cj/markov-chain-create))
+ (sizes '()))
+ (dolist (word-count '(100 500 1000 5000 10000))
+ (let ((text (generate-test-text word-count)))
+ (cj/markov-learn chain text)
+ (let ((size (hash-table-count (cj/markov-chain-map chain))))
+ (push (cons word-count size) sizes)
+ (message "After %d words: %d unique bigrams" word-count size))))
+ (message "Growth pattern: %S" (nreverse sizes))))
+
+;;; Comparison: Tokenization vs Learning
+
+(ert-deftest benchmark-tokenize-vs-learn ()
+ "Compare tokenization time to total learning time."
+ (let* ((text (generate-test-text 5000))
+ (tokenize-time (benchmark-time
+ (lambda () (cj/markov-tokenize text))))
+ (chain (cj/markov-chain-create))
+ (learn-time (benchmark-time
+ (lambda () (cj/markov-learn chain text)))))
+ (benchmark-report "Tokenize 5K words" tokenize-time)
+ (benchmark-report "Learn 5K words (total)" learn-time)
+ (message "Tokenization is %.1f%% of total learning time"
+ (* 100.0 (/ tokenize-time learn-time)))))
+
+;;; Real-world Scenario
+
+(ert-deftest benchmark-realistic-usage ()
+ "Benchmark realistic usage: learn from multiple sources, generate paragraphs."
+ (let ((chain (cj/markov-chain-create))
+ (learn-total 0.0)
+ (gen-total 0.0))
+ ;; Simulate learning from 10 different sources
+ (dotimes (i 10)
+ (let ((text (generate-test-text 500)))
+ (setq learn-total
+ (+ learn-total
+ (benchmark-time (lambda () (cj/markov-learn chain text)))))))
+
+ ;; Generate 5 paragraphs
+ (dotimes (i 5)
+ (setq gen-total
+ (+ gen-total
+ (benchmark-time (lambda () (cj/markov-generate chain 50))))))
+
+ (benchmark-report "Realistic: 10 learns (500 words each)" learn-total)
+ (benchmark-report "Realistic: 5 generations (50 words each)" gen-total)
+ (benchmark-report "Realistic: TOTAL TIME" (+ learn-total gen-total))
+ (message "Final chain size: %d bigrams"
+ (hash-table-count (cj/markov-chain-map chain)))))
+
+(provide 'test-lorem-optimum-benchmark)
+;;; test-lorem-optimum-benchmark.el ends here