OverviewFilesCommitsIssuesDocsReleasesPeers

hypercore-kb / opencog-kb / language-learning / mvp / benchmark-gpu.scm

184 lines · scm ref:
;;
;; 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)