Golang中goroutines的性能优化实践
Golang中goroutines的性能优化实践 你好,
作为作业要求,我们需要优化生成分形图片的代码。
我们已经尝试通过两个for循环将图片分割成更小的部分(并将运行时间缩短了三分之二),但还希望通过在启动函数时创建多个goroutine来进一步加速。然而,这带来的改善微乎其微。
我们应该如何做才能加快程序运行速度?
另外,请检查plottingSmallSquares函数。为什么这个函数在Julia函数内部作为匿名函数使用时无法正常工作?那样做时,我们只得到垂直的线条。
提前感谢您的回复。
//this program creates pictures with Julia
//imports omitted
type ComplexFunc func(complex128) complex128
var Funcs []ComplexFunc = []ComplexFunc{
func(z complex128) complex128 { return z*z - 0.61803398875 },
func(z complex128) complex128 { return z*z + complex(0, 1) },
//more functions omitted for clarity
}
func main() {
wgr1 := new(sync.WaitGroup)
//this is the function that can be ameliorated
for n, fn := range Funcs {
//sending one to wait group
//the done is executed in the parallelPainting function
wgr1.Add(1)
go parallelPainting(fn, n, wgr1)
}
wgr1.Wait()
}
func parallelPainting(fn ComplexFunc, n int, wgr1 *sync.WaitGroup) {
err := CreatePng("picture-"+strconv.Itoa(n)+".png", fn, 1024)
wgr1.Done()
if err != nil {
log.Fatal(err)
}
}
// CreatePng creates a PNG picture file with a Julia image of size n x n.
func CreatePng(filename string, f ComplexFunc, n int) (err error) {
file, err := os.Create(filename)
if err != nil {
return
}
defer file.Close()
err = png.Encode(file, Julia(f, n))
return
}
// Julia returns an image of size n x n of the Julia set for f.
func Julia(f ComplexFunc, n int) image.Image {
wg := new(sync.WaitGroup)
bounds := image.Rect(-n/2, -n/2, n/2, n/2)
img := image.NewRGBA(bounds)
s := float64(n / 4)
divisor := 10
//division of the image in 100 smaller squares
width := bounds.Max.X - bounds.Min.X
height := bounds.Max.Y - bounds.Min.Y
sizeX := width / divisor
sizeY := height / divisor
startX := bounds.Min.X
startY := bounds.Min.Y
for startX, stopX := bounds.Min.X, startX+sizeX; startX < width; startX, stopX = stopX, startX+sizeX {
for startY, stopY := bounds.Min.Y, startY+sizeY; startY < height; startY, stopY = stopY, startY+sizeY {
wg.Add(1)
go plottingSmallSquares(f, n, img, startX, stopX, startY, stopY, s, wg)
}
}
wg.Wait()
return img
}
func plottingSmallSquares(f ComplexFunc, n int, img *image.RGBA, startX int, stopX int, startY int, stopY int, s float64, wg *sync.WaitGroup) {
for i := startX; i < stopX; i++ {
for j := startY; j < stopY; j++ {
n := Iterate(f, complex(float64(i)/s, float64(j)/s), 256)
r := uint8(0)
g := uint8(0)
b := uint8(n % 32 * 8)
img.Set(i, j, color.RGBA{r, g, b, 255})
}
}
wg.Done()
}
// Iterate sets z_0 = z, and repeatedly computes z_n = f(z_{n-1}), n ≥ 1,
// until |z_n| > 2 or n = max and returns this n.
func Iterate(f ComplexFunc, z complex128, max int) (n int) {
for ; n < max; n++ {
if real(z)*real(z)+imag(z)*imag(z) > 4 {
break
}
z = f(z)
}
return
}
更多关于Golang中goroutines的性能优化实践的实战教程也可以访问 https://www.itying.com/category-94-b0.html
更多关于Golang中goroutines的性能优化实践的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
以下是针对您问题的专业解答,包含性能优化建议和匿名函数问题的分析。
Goroutines性能优化
当前代码的主要性能瓶颈在于goroutine创建过多(100个小方块 × 多个Julia函数),导致调度开销过大。以下是优化方案:
// 优化版本 - 使用工作池模式控制goroutine数量
func Julia(f ComplexFunc, n int) image.Image {
bounds := image.Rect(-n/2, -n/2, n/2, n/2)
img := image.NewRGBA(bounds)
s := float64(n / 4)
divisor := 10
width := bounds.Max.X - bounds.Min.X
height := bounds.Max.Y - bounds.Min.Y
sizeX := width / divisor
sizeY := height / divisor
// 使用有缓冲channel控制并发数
workers := runtime.NumCPU() // 根据CPU核心数确定worker数量
jobs := make(chan struct {
startX, stopX, startY, stopY int
}, divisor*divisor)
var wg sync.WaitGroup
// 启动worker池
for i := 0; i < workers; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for job := range jobs {
for i := job.startX; i < job.stopX; i++ {
for j := job.startY; j < job.stopY; j++ {
n := Iterate(f, complex(float64(i)/s, float64(j)/s), 256)
r := uint8(0)
g := uint8(0)
b := uint8(n % 32 * 8)
img.Set(i, j, color.RGBA{r, g, b, 255})
}
}
}
}()
}
// 分发任务
for startX := bounds.Min.X; startX < width; startX += sizeX {
stopX := startX + sizeX
if stopX > width {
stopX = width
}
for startY := bounds.Min.Y; startY < height; startY += sizeY {
stopY := startY + sizeY
if stopY > height {
stopY = height
}
jobs <- struct {
startX, stopX, startY, stopY int
}{startX, stopX, startY, stopY}
}
}
close(jobs)
wg.Wait()
return img
}
匿名函数问题分析
plottingSmallSquares作为匿名函数使用时出现垂直线条的原因是循环变量捕获问题。在Go中,循环变量在迭代间共享,当在goroutine中使用时会产生竞态条件。
错误用法示例:
// 错误:所有goroutine共享相同的循环变量值
for startX, stopX := bounds.Min.X, startX+sizeX; startX < width; startX, stopX = stopX, startX+sizeX {
for startY, stopY := bounds.Min.Y, startY+sizeY; startY < height; startY, stopY = stopY, startY+sizeY {
wg.Add(1)
go func() {
plottingSmallSquares(f, n, img, startX, stopX, startY, stopY, s, wg)
wg.Done()
}()
}
}
正确解决方案是传递参数副本:
// 正确:创建局部变量副本
for startX, stopX := bounds.Min.X, startX+sizeX; startX < width; startX, stopX = stopX, startX+sizeX {
for startY, stopY := bounds.Min.Y, startY+sizeY; startY < height; startY, stopY = stopY, startY+sizeY {
wg.Add(1)
go func(sX, sXEnd, sY, sYEnd int) {
plottingSmallSquares(f, n, img, sX, sXEnd, sY, sYEnd, s, wg)
}(startX, stopX, startY, stopY)
}
}
这些优化将显著提升性能并解决垂直线条问题。工作池模式减少了goroutine创建开销,而正确的变量传递消除了竞态条件。

