github.com/joshprzybyszewski/masyu@v0.0.0-20230508015604-f31a025f6e7e/results/generate.go (about) 1 package results 2 3 import ( 4 "fmt" 5 "os" 6 "runtime" 7 "sort" 8 "strings" 9 "time" 10 11 "github.com/joshprzybyszewski/masyu/fetch" 12 "github.com/joshprzybyszewski/masyu/model" 13 "github.com/joshprzybyszewski/masyu/solve" 14 15 "github.com/shirou/gopsutil/cpu" 16 ) 17 18 const ( 19 numPuzzlesPerIter = 500 20 21 readmeFilename = `README.md` 22 resultsStartMarker = `<resultsMarker>` 23 resultsStopMarker = `</resultsMarker>` 24 ) 25 26 const ( 27 resultsTimeout = 90 * time.Second 28 ) 29 30 func Update() { 31 var allResults [model.MaxIterator + 1][]time.Duration 32 for iter := model.MinIterator; iter <= model.MaxIterator; iter++ { 33 durs, err := getResults(iter, resultsTimeout) 34 if err != nil { 35 fmt.Printf("Error: %+v\n", err) 36 continue 37 } 38 allResults[iter] = append(allResults[iter], durs...) 39 } 40 41 var sb strings.Builder 42 43 sb.WriteByte('\n') 44 sb.WriteByte('\n') 45 sb.WriteString(fmt.Sprintf("_GOOS: %s_\n\n", runtime.GOOS)) 46 sb.WriteString(fmt.Sprintf("_GOARCH: %s_\n\n", runtime.GOARCH)) 47 48 if stats, err := cpu.Info(); err == nil { 49 sb.WriteString(fmt.Sprintf("_cpu: %s_\n\n", stats[0].ModelName)) 50 } 51 52 sb.WriteString(fmt.Sprintf("_Solve timeout: %s_\n\n", resultsTimeout)) 53 54 sb.WriteString("|Puzzle|Min|p25|Median|p75|p95|max|sample size|\n") 55 sb.WriteString("|-|-|-|-|-|-|-|-:|\n") 56 57 for iter := model.MinIterator; iter < model.Iterator(len(allResults)); iter++ { 58 sb.WriteString(getTableRow(iter, allResults[iter])) 59 } 60 61 sb.WriteString(fmt.Sprintf("\n_Last Updated: %s_\n", time.Now().Format(time.RFC822))) 62 63 writeResultsToReadme(sb.String()) 64 65 fmt.Printf("Results updated.\n") 66 } 67 68 func getResults( 69 iter model.Iterator, 70 timeout time.Duration, 71 ) ([]time.Duration, error) { 72 fmt.Printf("Starting %s\n", iter) 73 74 inputs, err := fetch.ReadN(iter, numPuzzlesPerIter) 75 if err != nil { 76 return nil, err 77 } 78 79 durs := make([]time.Duration, 0, len(inputs)) 80 81 for i, sr := range inputs { 82 ns := sr.Input.ToNodes() 83 84 t0 := time.Now() 85 sol, err := solve.FromNodesWithTimeout( 86 iter.GetSize(), 87 ns, 88 timeout, 89 ) 90 dur := time.Since(t0) 91 durs = append(durs, dur) 92 if i%10 == 0 { 93 fmt.Printf("\n%2d: ", i/10) 94 } 95 if err != nil { 96 fmt.Print("X") 97 continue 98 } 99 if dur > 5*time.Second { 100 fmt.Print("!") 101 } else if dur > time.Second { 102 fmt.Print("*") 103 } else if dur > 500*time.Millisecond { 104 fmt.Print("-") 105 } else { 106 fmt.Print(".") 107 } 108 109 if sr.Answer != `` { 110 if sr.Answer != sol.ToAnswer() { 111 fmt.Printf("\nIs this correct?\n") 112 fmt.Printf("%s\n", sr.Input) 113 fmt.Printf("%s\n", sol.Pretty(ns)) 114 } 115 } 116 117 for numGCs := 0; numGCs < 3; numGCs++ { 118 time.Sleep(10 * time.Millisecond) 119 runtime.GC() 120 } 121 } 122 123 fmt.Printf("\nFinished %s\n\n", iter) 124 fmt.Printf("%s\n\n", getTableRow(iter, durs)) 125 return durs, nil 126 } 127 128 func getTableRow( 129 iter model.Iterator, 130 durs []time.Duration, 131 ) string { 132 if len(durs) == 0 { 133 return `` 134 } 135 136 sort.Slice(durs, func(i, j int) bool { 137 return durs[i] < durs[j] 138 }) 139 140 var sb strings.Builder 141 sb.WriteString(fmt.Sprintf("|%s|", iter)) 142 sb.WriteString(fmt.Sprintf("%s|", roundDuration(durs[0]))) 143 sb.WriteString(fmt.Sprintf("%s|", roundDuration(durs[len(durs)/4]))) 144 sb.WriteString(fmt.Sprintf("%s|", roundDuration(durs[len(durs)/2]))) 145 sb.WriteString(fmt.Sprintf("%s|", roundDuration(durs[len(durs)*3/4]))) 146 sb.WriteString(fmt.Sprintf("%s|", roundDuration(durs[len(durs)*19/20]))) 147 sb.WriteString(fmt.Sprintf("%s|", roundDuration(durs[len(durs)-1]))) 148 sb.WriteString(fmt.Sprintf("%d|\n", len(durs))) 149 return sb.String() 150 } 151 152 func roundDuration( 153 d time.Duration, 154 ) time.Duration { 155 156 if d > time.Second { 157 return d.Truncate(10 * time.Millisecond) 158 } 159 160 if d > time.Millisecond { 161 return d.Truncate(10 * time.Microsecond) 162 } 163 164 if d > time.Microsecond { 165 return d.Truncate(10 * time.Nanosecond) 166 } 167 168 return d 169 } 170 171 func writeResultsToReadme( 172 res string, 173 ) { 174 175 fmt.Printf("Writing these results to the readme:\n%s\n", res) 176 177 content, err := getREADMEContent() 178 if err != nil { 179 fmt.Printf("Error fetching readme content: %s\n", err.Error()) 180 return 181 } 182 183 i1 := strings.Index(content, resultsStartMarker) 184 if i1 < 0 { 185 fmt.Printf("Could not find %q in the README\n", resultsStartMarker) 186 return 187 } 188 i2 := strings.Index(content, resultsStopMarker) 189 if i2 < 0 { 190 fmt.Printf("Could not find %q in the README\n", resultsStopMarker) 191 return 192 } 193 194 newContent := content[:i1+len(resultsStartMarker)] + res + content[i2:] 195 196 err = writeREADME(newContent) 197 if err != nil { 198 fmt.Printf("Error writing readme content: %s\n", err.Error()) 199 return 200 } 201 } 202 203 func getREADMEContent() (string, error) { 204 data, err := os.ReadFile(readmeFilename) 205 if err != nil { 206 return ``, err 207 } 208 return string(data), nil 209 } 210 211 func writeREADME( 212 content string, 213 ) error { 214 f, err := os.Create(readmeFilename) 215 if err != nil { 216 return err 217 } 218 defer f.Close() 219 220 _, err = f.WriteString(content) 221 if err != nil { 222 return err 223 } 224 return nil 225 }