diff options
Diffstat (limited to 'tests/test-lorem-optimum-benchmark.el')
| -rw-r--r-- | tests/test-lorem-optimum-benchmark.el | 227 |
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 |
