;;
;; benchmark-gpu.scm - Comprehensive GPU performance benchmark
;;
;; Tests the atomese-simd OpenCL performance after applying:
;; 1. Binary caching (2.2x program load speedup)
;; 2. Dispatch thread build (eliminates per-session overhead)
;;
(use-modules (opencog) (opencog exec))
(use-modules (opencog persist) (opencog persist-rocks))
(use-modules (ice-9 format))
(use-modules (ice-9 time))
;; ─────────────────────────────────────────────────────────────────────
;; Setup
;; ─────────────────────────────────────────────────────────────────────
(define *kernel-path* "/storage/projects/code/hypercore-kb/opencog-kb/language-learning/mvp/pmfg-kernel.cl")
(define *rsn* (RocksStorageNode "rocks://data/pairs.rdb"))
(cog-open *rsn*)
;; Helper: time execution in milliseconds
(define (time-ms thunk)
(let* ((start (get-internal-real-time))
(result (thunk))
(end (get-internal-real-time)))
(values result (inexact->exact (round (* 1000.0 (/ (- end start) internal-time-units-per-second)))))))
(define (format-result label ms)
(format #t " ~a: ~a ms~%" label ms))
;; ─────────────────────────────────────────────────────────────────────
;; Test 1: OpenCL Node Creation (measures binary cache)
;; ─────────────────────────────────────────────────────────────────────
(display "\n═══════════════════════════════════════════════════════════════════\n")
(display "TEST 1: OpenCL Node Creation (binary cache effectiveness)\n")
(display "═══════════════════════════════════════════════════════════════════\n\n")
;; Clear the cache to get clean measurements
(display "Clearing OpenCL binary cache...\n")
(system "rm -rf ~/.cache/opencog/opencl/*")
;; First creation (cold - must compile)
(define gpu-node #f)
(call-with-values
(lambda ()
(time-ms (lambda ()
(set! gpu-node (OpenclNode (string-append "opencl://:" *kernel-path*))))))
(lambda (result ms)
(format-result "First OpenclNode creation (cold)" ms)))
;; Verify the node is valid
(format #t " GPU node: ~a~%" (cog-name gpu-node))
;; Check cache was created
(let ((cache-files (system "ls -la ~/.cache/opencog/opencl/ 2>&1 | head -5")))
(display ""))
;; Second creation (should use cached binary)
(define gpu-node2 #f)
(call-with-values
(lambda ()
(time-ms (lambda ()
(set! gpu-node2 (OpenclNode (string-append "opencl://:" *kernel-path*))))))
(lambda (result ms)
(format-result "Second OpenclNode creation (warm cache)" ms)))
;; ─────────────────────────────────────────────────────────────────────
;; Test 2: Kernel Execution (measures dispatch thread fix)
;; ─────────────────────────────────────────────────────────────────────
(display "\n═══════════════════════════════════════════════════════════════════\n")
(display "TEST 2: Kernel Execution (dispatch thread effectiveness)\n")
(display "═══════════════════════════════════════════════════════════════════\n\n")
;; Create test data
(define test-input (Number 1 2 3 4 5 6 7 8))
;; First execution (kernel object creation)
(define result1 #f)
(call-with-values
(lambda ()
(time-ms (lambda ()
(cog-set-value! gpu-node (Predicate "input")
(Section (Item "test_simple")
(ConnectorSeq test-input (Number 0 0 0 0 0 0 0 0))))
(set! result1 (cog-value gpu-node (Predicate "input"))))))
(lambda (result ms)
(format-result "First kernel execution" ms)))
(format #t " Result: ~a~%" result1)
;; Rapid subsequent executions
(display "\n Rapid execution (10 iterations):\n")
(do ((i 0 (+ i 1)))
((= i 10))
(call-with-values
(lambda ()
(time-ms (lambda ()
(cog-set-value! gpu-node (Predicate "input")
(Section (Item "test_simple")
(ConnectorSeq (Number i (+ i 1) (+ i 2) (+ i 3))
(Number 0 0 0 0))))
(cog-value gpu-node (Predicate "input")))))
(lambda (result ms)
(format #t " Iteration ~a: ~a ms~%" i ms))))
;; ─────────────────────────────────────────────────────────────────────
;; Test 3: PMFG Kernel (realistic workload)
;; ─────────────────────────────────────────────────────────────────────
(display "\n═══════════════════════════════════════════════════════════════════\n")
(display "TEST 3: PMFG Single Kernel (realistic linguistic workload)\n")
(display "═══════════════════════════════════════════════════════════════════\n\n")
;; Create a mock MI matrix (10x10 vocabulary)
;; In real usage this would be the full vocab×vocab matrix
(define vocab-size 10)
(define mi-matrix
(let ((vals '()))
(do ((i 0 (+ i 1))) ((= i (* vocab-size vocab-size)))
(set! vals (cons (random:uniform) vals)))
(apply Number (reverse vals))))
;; Test sentence: 5 words (indices 0-4)
(define test-sentence (Number 0 1 2 3 4))
(define n-words (Number 5))
(define vocab-arr (Number vocab-size))
;; Output arrays
(define pmfg-edges-out (Number 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) ;; max 3*5-6=9 edges × 2
(define n-edges-out (Number 0))
;; First PMFG execution
(define pmfg-result #f)
(call-with-values
(lambda ()
(time-ms (lambda ()
(cog-set-value! gpu-node (Predicate "pmfg")
(Section (Item "pmfg_single")
(ConnectorSeq mi-matrix test-sentence n-words vocab-arr
pmfg-edges-out n-edges-out)))
(set! pmfg-result (cog-value gpu-node (Predicate "pmfg"))))))
(lambda (result ms)
(format-result "First PMFG execution" ms)))
(format #t " PMFG result: ~a~%" pmfg-result)
;; Multiple PMFG executions
(display "\n Multiple PMFG executions (5 iterations):\n")
(do ((i 0 (+ i 1)))
((= i 5))
(call-with-values
(lambda ()
(time-ms (lambda ()
(cog-set-value! gpu-node (Predicate "pmfg")
(Section (Item "pmfg_single")
(ConnectorSeq mi-matrix test-sentence n-words vocab-arr
pmfg-edges-out n-edges-out)))
(cog-value gpu-node (Predicate "pmfg")))))
(lambda (result ms)
(format #t " PMFG iteration ~a: ~a ms~%" i ms))))
;; ─────────────────────────────────────────────────────────────────────
;; Summary
;; ─────────────────────────────────────────────────────────────────────
(display "\n═══════════════════════════════════════════════════════════════════\n")
(display "BENCHMARK COMPLETE\n")
(display "═══════════════════════════════════════════════════════════════════\n\n")
(display "Expected behavior with fixes applied:\n")
(display " • First OpenclNode creation: ~300-700ms (compiles + caches binary)\n")
(display " • Second creation: ~200-400ms (loads cached binary)\n")
(display " • Kernel executions: 0-5ms each (no per-session overhead)\n")
(display " • PMFG kernel: 1-10ms per call\n")
(display "\n")
(display "If you see 10+ second delays on kernel execution, the dispatch\n")
(display "thread fix may not be installed. Rebuild and reinstall atomese-simd.\n")
(cog-close *rsn*)
(exit 0)