去超时不开火
Go timeout not firing
为了好玩,我在 Go 中实现了一些排序算法,现在我想测试它们在 运行dom 整数上的性能。所以我写了下面的程序。我遵循了类似的格式:https://gobyexample.com/timeouts
但是,超时似乎没有正确触发。下面是我的代码:
package main
import (
"allanpinkerton.com/algorithms/sorting"
"fmt"
"math/rand"
"os"
"strconv"
"time"
)
// Prints out the time elapsed since start
func timeExecution(startTime time.Time, functionName string, inputSize int) string {
executionTime := time.Since(startTime)
return fmt.Sprintf("%-20s took %10.4fms to sort %d elements\n", functionName, float64(executionTime.Nanoseconds())/1000000, inputSize)
}
// Generates file with n random ints named integerArray + n
func generateRandomIntegers(n int, filename string) {
arr := make([]int, n)
for i := 0; i < n; i++ {
arr[i] = rand.Int()
}
f, _ := os.Create(filename)
defer f.Close()
for _, num := range arr {
f.WriteString(strconv.Itoa(num) + " ")
}
f.WriteString("\n")
f.Sync()
fmt.Printf("Generated " + filename + " with " + strconv.Itoa(n) + " elements.\n")
}
func checkError(err error) {
if err != nil {
panic(err)
}
}
func main() {
sortingFunctions := map[string]interface{}{
"InsertionSort": sorting.InsertionSort,
"QuickSortLastElement": sorting.QuickSortLastElement,
"QuickSortRandom": sorting.QuickSortRandom,
}
if len(os.Args) != 2 {
fmt.Printf("No size specified.\n")
return
}
size := os.Args[1]
sizeInt, err := strconv.Atoi(size)
checkError(err)
arr := make([]int, sizeInt)
for i := 0; i < sizeInt; i++ {
arr[i] = rand.Int()
}
fmt.Println("Generated " + size + " integers.")
mainChannel := make(chan string)
for k, v := range sortingFunctions {
newArr := make([]int, len(arr))
copy(newArr, arr)
go func(name string, v interface{}) {
start := time.Now()
v.(func([]int))(newArr)
result := timeExecution(start, name, len(newArr))
mainChannel <- result
}(k, v)
}
for _ = range sortingFunctions {
select {
case result := <-mainChannel:
fmt.Printf(result)
case <-time.After(time.Second):
fmt.Println("Timeout")
}
}
return
}
top 只是一堆助手,但是 main 函数发生了一些有趣的事情。我 运行 go install
和 运行 它针对 150,000 个元素,并得到以下响应:
Generated 150000 integers.
QuickSortLastElement took 15.0037ms to sort 150000 elements
InsertionSort took 7599.5884ms to sort 150000 elements
QuickSortRandom took 15.1697ms to sort 150000 elements
很明显插入排序用了 7 秒,但超时应该在 1 秒后触发。超时不触发有什么原因吗?
因此,我尝试通过将 sortingFuncs 映射更改为:
,从排序包中切换出用于 sort.Ints 函数的自定义排序程序
sortingFunctions := map[string]func([]int){
"InsertionSort": sort.Ints,
"QuickSortLastElement": sort.Ints,
"QuickSortRandom": sort.Ints,
}
然后问题就解决了。所以是我的自定义排序函数阻止了超时被触发。我必须向这些函数添加一些东西才能使它们 运行 并行吗?
这是 playground 中所有代码的合并版本。
https://play.golang.org/p/SBgDTGyUyp
由于您在最后一个循环中使用了 time.After(time.Second)
,因此每次结果到达时您都将重置超时。相反,尝试
timeoutChannel := time.After(time.Second)
for _ = range sortingFunctions {
select {
case result := <-mainChannel:
fmt.Printf(result)
case <-timeoutChannel:
fmt.Println("Timeout")
}
}
以上代码现在可以正确捕获超时。它仍然没有按预期工作,因为循环的内容总是执行三次(因为 sortingFunctions
有三个元素),并且任何超时都计入这三次迭代。使用你在 go playground 中的代码,我现在得到以下输出:
Generated 90000 integers.
QuickSortRandom took 9.4268ms to sort 90000 elements
Timeout
QuickSortLastElement took 8.6096ms to sort 90000 elements
您在 Go playground 上发布的代码给出了以下输出:
Generated 90000 integers.
InsertionSort took 4465.6230ms to sort 90000 elements
QuickSortLastElement took 11.2758ms to sort 90000 elements
QuickSortRandom took 11.6547ms to sort 90000 elements
我怀疑您没有看到超时被调用的事实是由于 InsertionSort
确实进行了任何函数调用,因此 does not allow 调度程序在 goroutine 之间切换。由于默认情况下 Go 仅使用单个线程,因此其他一切都必须等到 InsertionSort
完成。
为了验证这个假设,我尝试使用 GOMAXPROCS=4 调用程序(允许 Go 调度程序使用 4 个操作系统线程):在这种情况下,我得到输出
Generated 90000 integers.
QuickSortRandom took 21.1900ms to sort 90000 elements
QuickSortLastElement took 11.4538ms to sort 90000 elements
Timeout
符合预期。 (奇怪的是,对于 GOMAXPROCS=2,行为不是确定性的,有时会触发超时,有时不会。我没有试图找出为什么 2 个线程在这里并不总是足够。)
为了好玩,我在 Go 中实现了一些排序算法,现在我想测试它们在 运行dom 整数上的性能。所以我写了下面的程序。我遵循了类似的格式:https://gobyexample.com/timeouts
但是,超时似乎没有正确触发。下面是我的代码:
package main
import (
"allanpinkerton.com/algorithms/sorting"
"fmt"
"math/rand"
"os"
"strconv"
"time"
)
// Prints out the time elapsed since start
func timeExecution(startTime time.Time, functionName string, inputSize int) string {
executionTime := time.Since(startTime)
return fmt.Sprintf("%-20s took %10.4fms to sort %d elements\n", functionName, float64(executionTime.Nanoseconds())/1000000, inputSize)
}
// Generates file with n random ints named integerArray + n
func generateRandomIntegers(n int, filename string) {
arr := make([]int, n)
for i := 0; i < n; i++ {
arr[i] = rand.Int()
}
f, _ := os.Create(filename)
defer f.Close()
for _, num := range arr {
f.WriteString(strconv.Itoa(num) + " ")
}
f.WriteString("\n")
f.Sync()
fmt.Printf("Generated " + filename + " with " + strconv.Itoa(n) + " elements.\n")
}
func checkError(err error) {
if err != nil {
panic(err)
}
}
func main() {
sortingFunctions := map[string]interface{}{
"InsertionSort": sorting.InsertionSort,
"QuickSortLastElement": sorting.QuickSortLastElement,
"QuickSortRandom": sorting.QuickSortRandom,
}
if len(os.Args) != 2 {
fmt.Printf("No size specified.\n")
return
}
size := os.Args[1]
sizeInt, err := strconv.Atoi(size)
checkError(err)
arr := make([]int, sizeInt)
for i := 0; i < sizeInt; i++ {
arr[i] = rand.Int()
}
fmt.Println("Generated " + size + " integers.")
mainChannel := make(chan string)
for k, v := range sortingFunctions {
newArr := make([]int, len(arr))
copy(newArr, arr)
go func(name string, v interface{}) {
start := time.Now()
v.(func([]int))(newArr)
result := timeExecution(start, name, len(newArr))
mainChannel <- result
}(k, v)
}
for _ = range sortingFunctions {
select {
case result := <-mainChannel:
fmt.Printf(result)
case <-time.After(time.Second):
fmt.Println("Timeout")
}
}
return
}
top 只是一堆助手,但是 main 函数发生了一些有趣的事情。我 运行 go install
和 运行 它针对 150,000 个元素,并得到以下响应:
Generated 150000 integers.
QuickSortLastElement took 15.0037ms to sort 150000 elements
InsertionSort took 7599.5884ms to sort 150000 elements
QuickSortRandom took 15.1697ms to sort 150000 elements
很明显插入排序用了 7 秒,但超时应该在 1 秒后触发。超时不触发有什么原因吗?
因此,我尝试通过将 sortingFuncs 映射更改为:
,从排序包中切换出用于 sort.Ints 函数的自定义排序程序sortingFunctions := map[string]func([]int){
"InsertionSort": sort.Ints,
"QuickSortLastElement": sort.Ints,
"QuickSortRandom": sort.Ints,
}
然后问题就解决了。所以是我的自定义排序函数阻止了超时被触发。我必须向这些函数添加一些东西才能使它们 运行 并行吗?
这是 playground 中所有代码的合并版本。 https://play.golang.org/p/SBgDTGyUyp
由于您在最后一个循环中使用了 time.After(time.Second)
,因此每次结果到达时您都将重置超时。相反,尝试
timeoutChannel := time.After(time.Second)
for _ = range sortingFunctions {
select {
case result := <-mainChannel:
fmt.Printf(result)
case <-timeoutChannel:
fmt.Println("Timeout")
}
}
以上代码现在可以正确捕获超时。它仍然没有按预期工作,因为循环的内容总是执行三次(因为 sortingFunctions
有三个元素),并且任何超时都计入这三次迭代。使用你在 go playground 中的代码,我现在得到以下输出:
Generated 90000 integers.
QuickSortRandom took 9.4268ms to sort 90000 elements
Timeout
QuickSortLastElement took 8.6096ms to sort 90000 elements
您在 Go playground 上发布的代码给出了以下输出:
Generated 90000 integers.
InsertionSort took 4465.6230ms to sort 90000 elements
QuickSortLastElement took 11.2758ms to sort 90000 elements
QuickSortRandom took 11.6547ms to sort 90000 elements
我怀疑您没有看到超时被调用的事实是由于 InsertionSort
确实进行了任何函数调用,因此 does not allow 调度程序在 goroutine 之间切换。由于默认情况下 Go 仅使用单个线程,因此其他一切都必须等到 InsertionSort
完成。
为了验证这个假设,我尝试使用 GOMAXPROCS=4 调用程序(允许 Go 调度程序使用 4 个操作系统线程):在这种情况下,我得到输出
Generated 90000 integers.
QuickSortRandom took 21.1900ms to sort 90000 elements
QuickSortLastElement took 11.4538ms to sort 90000 elements
Timeout
符合预期。 (奇怪的是,对于 GOMAXPROCS=2,行为不是确定性的,有时会触发超时,有时不会。我没有试图找出为什么 2 个线程在这里并不总是足够。)