Added basic CPU and heap profiling to Chapter 1 "Breeding Numbers" program.
authorAaron Taylor <ataylor@subgeniuskitty.com>
Sat, 15 May 2021 09:50:02 +0000 (02:50 -0700)
committerAaron Taylor <ataylor@subgeniuskitty.com>
Sat, 15 May 2021 09:50:02 +0000 (02:50 -0700)
chapter-1-experiments/ch1-breeding-numbers.go

index fd1901f..fbba0ec 100644 (file)
@@ -7,6 +7,9 @@ import (
     "flag"
     "fmt"
     "log"
     "flag"
     "fmt"
     "log"
+    "os"
+    "runtime/pprof"
+    "strconv"
 )
 
 // =============================================================================
 )
 
 // =============================================================================
@@ -209,6 +212,11 @@ func (s *surrealSet) remove(num surrealNumber, u surrealUniverse) {
 // =============================================================================
 
 func main() {
 // =============================================================================
 
 func main() {
+    // Flags to enable various performance profiling options.
+    cpuProfile := flag.String("cpuprofile", "", "Filename for saving CPU profile output.")
+    memProfilePrefix := flag.String("memprofileprefix", "", "Filename PREFIX for saving memory profile output.")
+    suppressOutput := flag.Bool("silent", false, "Suppress printing of numberline at end of simulation. Useful when profiling.")
+
     // Obtain termination conditions from user.
     totalGenerations := flag.Int("generations", 2, "Number of generations of surreal numbers to breed.")
     flag.Parse()
     // Obtain termination conditions from user.
     totalGenerations := flag.Int("generations", 2, "Number of generations of surreal numbers to breed.")
     flag.Parse()
@@ -217,6 +225,17 @@ func main() {
     }
     remainingGenerations := *totalGenerations - 1
 
     }
     remainingGenerations := *totalGenerations - 1
 
+    // Setup any CPU performance profiling requested by the user. This will run
+    // throughout program execution.
+    if *cpuProfile != "" {
+        cpuProFile, err := os.Create(*cpuProfile)
+        if err != nil {
+            log.Fatal("ERROR: Unable to open CPU profiling output file:", err)
+        }
+        pprof.StartCPUProfile(cpuProFile)
+        defer pprof.StopCPUProfile()
+    }
+
     // Build a universe to contain all the surreal numbers we breed.
     // Seed it by hand with the number zero as generation-0.
     var universe surrealUniverse
     // Build a universe to contain all the surreal numbers we breed.
     // Seed it by hand with the number zero as generation-0.
     var universe surrealUniverse
@@ -228,10 +247,12 @@ func main() {
     // add them all to the universe.
     fmt.Printf("Breeding Generation:")
     for generation := 1; generation <= remainingGenerations; generation++ {
     // add them all to the universe.
     fmt.Printf("Breeding Generation:")
     for generation := 1; generation <= remainingGenerations; generation++ {
+        // Give the user some idea of overall progress during long jobs.
         if generation != 1 {
             fmt.Printf(",")
         }
         fmt.Printf(" %d", generation)
         if generation != 1 {
             fmt.Printf(",")
         }
         fmt.Printf(" %d", generation)
+
         // First generate all possible reduced form symbols per Axiom 1.
         potentialNumbers := permuteExistingNumbers(generation, universe)
         // Now prune out any symbols which are NOT valid numbers per Axiom 2.
         // First generate all possible reduced form symbols per Axiom 1.
         potentialNumbers := permuteExistingNumbers(generation, universe)
         // Now prune out any symbols which are NOT valid numbers per Axiom 2.
@@ -239,13 +260,27 @@ func main() {
         // Attempt to add the new numbers to the universe. Any duplicates will
         // be weeded out in the attempt.
         addNumbersToUniverse(validNumbers, &universe)
         // Attempt to add the new numbers to the universe. Any duplicates will
         // be weeded out in the attempt.
         addNumbersToUniverse(validNumbers, &universe)
+
+        // Setup any memory profiling requested by the user. This will snapshot
+        // the heap at the end of every generation.
+        if *memProfilePrefix != "" {
+            memProFile, err := os.Create(*memProfilePrefix + "-gen" + strconv.Itoa(generation) + ".mprof")
+            if err != nil {
+                log.Fatal("ERROR: Unable to write heap profile to disk:", err)
+            }
+            pprof.WriteHeapProfile(memProFile)
+            memProFile.Close()
+        }
     }
     fmt.Printf(".\n")
 
     }
     fmt.Printf(".\n")
 
+
     // Print the number line with generation on the horizontal axis and
     // magnitude on the vertical axis.
     // Print the number line with generation on the horizontal axis and
     // magnitude on the vertical axis.
-    for i := 0; i < universe.cardinality(); i++ {
-        printlnNumber(universe.numbers[i])
+    if !(*suppressOutput) {
+        for i := 0; i < universe.cardinality(); i++ {
+            printlnNumber(universe.numbers[i])
+        }
     }
     fmt.Println("After", *totalGenerations, "generations, the universe contains", len(universe.numbers), "numbers.")
     fmt.Println("If output looks poor, ensure tabstop is eight spaces and that output doesn't wrap.")
     }
     fmt.Println("After", *totalGenerations, "generations, the universe contains", len(universe.numbers), "numbers.")
     fmt.Println("If output looks poor, ensure tabstop is eight spaces and that output doesn't wrap.")