chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
// Copyright 2024 Dolthub, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
)
|
||||
|
||||
var benchmarkFolder BenchmarkFolderLocation // benchmarkFolder is the disk location of the benchmark folder
|
||||
|
||||
// BenchmarkFolderLocation is the location of this project's root folder.
|
||||
type BenchmarkFolderLocation struct {
|
||||
path string
|
||||
}
|
||||
|
||||
// GetBenchmarkFolder returns the location of the benchmark folder (scripts/mini_benchmark). This is used to find the
|
||||
// absolute position of our output files.
|
||||
func GetBenchmarkFolder() (BenchmarkFolderLocation, error) {
|
||||
_, currentFileLocation, _, ok := runtime.Caller(0)
|
||||
if !ok {
|
||||
return BenchmarkFolderLocation{}, errors.Errorf("failed to fetch the location of the current file")
|
||||
}
|
||||
benchmarkFolder = BenchmarkFolderLocation{filepath.Clean(filepath.Join(filepath.Dir(currentFileLocation),
|
||||
"../../../scripts/mini_sysbench"))}
|
||||
return benchmarkFolder, nil
|
||||
}
|
||||
|
||||
// MoveRoot returns a new BenchmarkFolderLocation that defines the root at the new path. The parameter should be
|
||||
// relative to the current root.
|
||||
func (root BenchmarkFolderLocation) MoveRoot(relativePath string) BenchmarkFolderLocation {
|
||||
return BenchmarkFolderLocation{filepath.Clean(filepath.Join(root.path, relativePath))}
|
||||
}
|
||||
|
||||
// GetAbsolutePath returns the absolute path of the given path, which should be relative to the project's root
|
||||
// folder.
|
||||
func (root BenchmarkFolderLocation) GetAbsolutePath(relativePath string) string {
|
||||
return filepath.ToSlash(filepath.Join(root.path, relativePath))
|
||||
}
|
||||
|
||||
// Exists returns whether the file or directory at the given path (relative to the root path) exists. Returns an error
|
||||
// if the check was unable to be completed.
|
||||
func (root BenchmarkFolderLocation) Exists(relativePath string) (bool, error) {
|
||||
_, err := os.Stat(root.GetAbsolutePath(relativePath))
|
||||
if os.IsNotExist(err) {
|
||||
return false, nil
|
||||
} else if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// ReadDir is equivalent to os.ReadDir, except that it uses the root path and the given relative path.
|
||||
func (root BenchmarkFolderLocation) ReadDir(relativePath string) ([]os.DirEntry, error) {
|
||||
return os.ReadDir(root.GetAbsolutePath(relativePath))
|
||||
}
|
||||
|
||||
// ReadFile is equivalent to os.ReadFile, except that it uses the root path and the given relative path.
|
||||
func (root BenchmarkFolderLocation) ReadFile(relativePath string) ([]byte, error) {
|
||||
return os.ReadFile(root.GetAbsolutePath(relativePath))
|
||||
}
|
||||
|
||||
// WriteFile is equivalent to os.WriteFile, except that it uses the root path and the given relative path.
|
||||
func (root BenchmarkFolderLocation) WriteFile(relativePath string, data []byte, perm os.FileMode) error {
|
||||
directory := filepath.ToSlash(filepath.Dir(relativePath))
|
||||
exists, err := root.Exists(directory)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !exists {
|
||||
if err = os.MkdirAll(root.GetAbsolutePath(directory), 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return os.WriteFile(root.GetAbsolutePath(relativePath), data, perm)
|
||||
}
|
||||
|
||||
// init is used to load the location of the benchmark folder.
|
||||
func init() {
|
||||
var err error
|
||||
benchmarkFolder, err = GetBenchmarkFolder()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
// Copyright 2024 Dolthub, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// AllowedVariance represents the amount that must change before a result is noteworthy. The number represents a whole
|
||||
// percentage, so "10" equals "10%".
|
||||
const AllowedVariance = 10
|
||||
|
||||
// main analyzes two separate runs of scripts/quick_sysbench.sh, and creates a table comparing the differences. This
|
||||
// table is intended for display in a GitHub PR.
|
||||
func main() {
|
||||
prBenchmark, err := benchmarkFolder.ReadFile("results1.log")
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
mainBenchmark, err := benchmarkFolder.ReadFile("results2.log")
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
prSections := SectionResults(string(prBenchmark))
|
||||
mainSections := SectionResults(string(mainBenchmark))
|
||||
sb := strings.Builder{}
|
||||
sb.WriteString("| | Main | PR | |\n")
|
||||
sb.WriteString("| --- | --- | --- | --- |\n")
|
||||
for _, kv := range GetMapKVsSorted(mainSections) {
|
||||
mainSection := kv.Value
|
||||
prSection, ok := prSections[mainSection.Test]
|
||||
if !ok {
|
||||
sb.WriteString(fmt.Sprintf("| %s | %.2f/s | ${\\color{red}DNF}$ | |\n",
|
||||
mainSection.Test, mainSection.Time))
|
||||
continue
|
||||
}
|
||||
percentChange := math.Floor(((prSection.Time/mainSection.Time)-1.0)*1000.0) / 10.0
|
||||
if percentChange > AllowedVariance { // Greatly positive
|
||||
sb.WriteString(fmt.Sprintf("| %s | %.2f/s | ${\\color{lightgreen}%.2f/s}$ | ${\\color{lightgreen}+%.1f\\\\%%}$ |\n",
|
||||
mainSection.Test, mainSection.Time, prSection.Time, percentChange))
|
||||
} else if percentChange < -AllowedVariance { // Greatly negative
|
||||
sb.WriteString(fmt.Sprintf("| %s | %.2f/s | ${\\color{red}%.2f/s}$ | ${\\color{red}%.1f\\\\%%}$ |\n",
|
||||
mainSection.Test, mainSection.Time, prSection.Time, percentChange))
|
||||
} else if percentChange > 0 { // Positive
|
||||
sb.WriteString(fmt.Sprintf("| %s | %.2f/s | %.2f/s | +%.1f%% |\n",
|
||||
mainSection.Test, mainSection.Time, prSection.Time, percentChange))
|
||||
} else if percentChange < 0 { // Negative
|
||||
sb.WriteString(fmt.Sprintf("| %s | %.2f/s | %.2f/s | %.1f%% |\n",
|
||||
mainSection.Test, mainSection.Time, prSection.Time, percentChange))
|
||||
} else { // No Change
|
||||
sb.WriteString(fmt.Sprintf("| %s | %.2f/s | %.2f/s | 0.0%% |\n",
|
||||
mainSection.Test, mainSection.Time, prSection.Time))
|
||||
}
|
||||
}
|
||||
fmt.Println(sb.String())
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
// Copyright 2024 Dolthub, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// KeyValue represents an entry in a map.
|
||||
type KeyValue[K comparable, V any] struct {
|
||||
Key K
|
||||
Value V
|
||||
}
|
||||
|
||||
// GetMapKVs returns the map's KeyValue entries as an unsorted slice.
|
||||
func GetMapKVs[K comparable, V any](m map[K]V) []KeyValue[K, V] {
|
||||
allEntries := make([]KeyValue[K, V], len(m))
|
||||
i := 0
|
||||
for k, v := range m {
|
||||
allEntries[i] = KeyValue[K, V]{Key: k, Value: v}
|
||||
i++
|
||||
}
|
||||
return allEntries
|
||||
}
|
||||
|
||||
// GetMapKVsSorted returns the map's KeyValue entries as a sorted slice. The keys are sorted in ascending order.
|
||||
func GetMapKVsSorted[K cmp.Ordered, V any](m map[K]V) []KeyValue[K, V] {
|
||||
allEntries := GetMapKVs(m)
|
||||
sort.Slice(allEntries, func(i, j int) bool {
|
||||
return allEntries[i].Key < allEntries[j].Key
|
||||
})
|
||||
return allEntries
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
// Copyright 2024 Dolthub, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Section represents a benchmark section.
|
||||
type Section struct {
|
||||
Test string
|
||||
Time float64 // This is in number of iterations per second
|
||||
}
|
||||
|
||||
// SectionResults creates a section for each test.
|
||||
func SectionResults(fileData string) map[string]Section {
|
||||
sections := make(map[string]Section)
|
||||
for {
|
||||
headerStartIdx := strings.Index(fileData, `----`)
|
||||
if headerStartIdx == -1 {
|
||||
break
|
||||
}
|
||||
headerEndIdx := strings.Index(fileData[headerStartIdx+4:], `----`) + headerStartIdx + 4
|
||||
if headerEndIdx == headerStartIdx+4 {
|
||||
break
|
||||
}
|
||||
headerFull := fileData[headerStartIdx : headerEndIdx+4]
|
||||
endingHeaderIdx := strings.LastIndex(fileData, headerFull)
|
||||
if endingHeaderIdx == -1 {
|
||||
break
|
||||
}
|
||||
section := Section{
|
||||
Test: headerFull[4 : len(headerFull)-4],
|
||||
Time: -1,
|
||||
}
|
||||
sectionText := strings.TrimSpace(fileData[len(headerFull):endingHeaderIdx])
|
||||
fileData = fileData[endingHeaderIdx+len(headerFull):]
|
||||
for _, line := range strings.Split(sectionText, "\n") {
|
||||
if strings.Contains(line, `queries:`) {
|
||||
parenIdx := strings.Index(line, `(`)
|
||||
perSecIdx := strings.Index(line, ` per sec.)`)
|
||||
if parenIdx != -1 && perSecIdx != -1 {
|
||||
timeString := line[parenIdx+1 : perSecIdx]
|
||||
parsedTime, err := strconv.ParseFloat(timeString, 64)
|
||||
if err == nil {
|
||||
section.Time = parsedTime
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
if section.Time == -1 {
|
||||
continue
|
||||
}
|
||||
sections[section.Test] = section
|
||||
}
|
||||
return sections
|
||||
}
|
||||
Reference in New Issue
Block a user