ch8/ch8-05 #174
Replies: 10 comments 3 replies
-
NB,最后一个closer |
Beta Was this translation helpful? Give feedback.
-
最后的close我竟然一遍看懂了,我觉得我有学go的天赋🤣 |
Beta Was this translation helpful? Give feedback.
-
最后一个close高明的地方在于主函数的for range会在channel里没有元素也没有关闭的时候阻塞,因此函数不会提前退出。而当
|
Beta Was this translation helpful? Give feedback.
-
8.5 改写mandelbrot程序有几个规律:
package main
import (
"crypto/rand"
"fmt"
"image"
"image/color"
"image/png"
"math"
"math/cmplx"
"os"
"runtime"
"sync"
"time"
)
const (
xmin, ymin, xmax, ymax = -2, -2, +2, +2
width, height = 1024, 1024
)
const (
RUN_TIMES = 1e1
CAP = 1024
RATIO = 0.9
)
var CORES int
var GOROUTINE_NUM int
type pixel struct {
x int
y int
c color.Color
}
func init() {
CORES = runtime.NumCPU()
GOROUTINE_NUM = int(float64(CORES) * RATIO)
fmt.Printf("Cores: %d, Go routine count: %d\n", CORES, GOROUTINE_NUM)
}
func main() {
// 朴素版本
var total int64 = 0
for i := 1; i <= RUN_TIMES; i++ {
total += drawPlain()
}
fmt.Printf("it takes %.2f milliseconds for drawPlain() to execute\n", float64(total)/float64(RUN_TIMES))
// 异步版本,很多go routine,破坏局部性
// total = 0
// for i := 1; i <= RUN_TIMES; i++ {
// total += drawAsync()
// }
// fmt.Printf("it takes %.2f milliseconds for drawAsync() to execute\n", float64(total)/float64(RUN_TIMES))
// 异步版本,局部性
// total = 0
// for i := 1; i <= RUN_TIMES; i++ {
// total += drawAsyncLocality()
// }
// fmt.Printf("it takes %.2f milliseconds for drawAsyncLocality() to execute\n", float64(total)/float64(RUN_TIMES))
// 减少竞争
total = 0
for i := 1; i <= RUN_TIMES; i++ {
total += drawAsyncTender()
}
fmt.Printf("it takes %.2f milliseconds for drawAsyncTender() to execute\n", float64(total)/float64(RUN_TIMES))
}
// 朴素版本
func drawPlain() (duration int64) {
defer func(start time.Time) {
duration = calcExecTime(start)
}(time.Now())
img := image.NewRGBA(image.Rect(0, 0, width, height))
for py := 0; py < height; py++ {
y := float64(py)/height*(ymax-ymin) + ymin
for px := 0; px < width; px++ { // cache locality
x := float64(px)/width*(xmax-xmin) + xmin
z := complex(x, y)
// Image point (px, py) represents complex value z.
img.Set(px, py, mandelbrot(z))
}
}
file, err := os.OpenFile("./plain.png", os.O_RDWR|os.O_CREATE, 0666)
if err != nil {
fmt.Println("open file err:", err)
}
defer file.Close()
png.Encode(file, img) // NOTE: ignoring errors
return
}
// 异步执行
func drawAsync() (duration int64) {
defer func(start time.Time) {
duration = calcExecTime(start)
}(time.Now())
var wg sync.WaitGroup
pixelCh := make(chan pixel, CAP)
img := image.NewRGBA(image.Rect(0, 0, width, height))
for py := 0; py < height; py++ {
y := float64(py)/height*(ymax-ymin) + ymin
for px := 0; px < width; px++ { // cache locality
x := float64(px)/width*(xmax-xmin) + xmin
z := complex(x, y)
// Image point (px, py) represents complex value z.
wg.Add(1)
go func(x, y int, z complex128) {
defer wg.Done()
pixelCh <- pixel{x: x, y: y, c: mandelbrot(z)}
}(px, py, z)
}
}
go func() {
wg.Wait()
close(pixelCh)
}()
for pixel := range pixelCh {
img.Set(pixel.x, pixel.y, pixel.c)
}
file, err := os.OpenFile("./async.png", os.O_RDWR|os.O_CREATE, 0666)
if err != nil {
fmt.Println("open file err:", err)
}
defer file.Close()
png.Encode(file, img) // NOTE: ignoring errors
return
}
// 提高局部性
func drawAsyncLocality() (duration int64) {
defer func(start time.Time) {
duration = calcExecTime(start)
}(time.Now())
var wg sync.WaitGroup
pixelCh := make(chan pixel, CAP)
img := image.NewRGBA(image.Rect(0, 0, width, height))
for py := 0; py < height; py++ {
y := float64(py)/height*(ymax-ymin) + ymin
wg.Add(1)
go func(py int, y float64) {
defer wg.Done()
for px := 0; px < width; px++ {
x := float64(px)/width*(xmax-xmin) + xmin
z := complex(x, y)
pixelCh <- pixel{x: px, y: py, c: mandelbrot(z)}
}
}(py, y)
}
go func() {
wg.Wait()
close(pixelCh)
}()
for pixel := range pixelCh {
// fmt.Printf("%v\n", pixel)
img.Set(pixel.x, pixel.y, pixel.c)
}
file, err := os.OpenFile("./asyncLocality.png", os.O_RDWR|os.O_CREATE, 0666)
if err != nil {
fmt.Println("open file err:", err)
}
defer file.Close()
png.Encode(file, img) // NOTE: ignoring errors
return
}
// 限制协程数目,减少交流和同步的时间
func drawAsyncTender() (duration int64) {
defer func(start time.Time) {
duration = calcExecTime(start)
}(time.Now())
pixelCh := make(chan pixel, CAP)
done := make(chan int)
wg := sync.WaitGroup{}
routinePool := make(chan struct{}, GOROUTINE_NUM)
for i := 0; i < GOROUTINE_NUM; i++ {
routinePool <- struct{}{}
}
img := image.NewRGBA(image.Rect(0, 0, width, height))
go func() {
for pixel := range pixelCh {
// fmt.Printf("%v\n", pixel)
img.Set(pixel.x, pixel.y, pixel.c)
}
done <- 1
}()
for py := 0; py < height; py++ {
y := float64(py)/height*(ymax-ymin) + ymin
// 取出一个routine
<-routinePool
wg.Add(1)
go func(py int, y float64) {
defer func() {
// 添加一个routine
routinePool <- struct{}{}
wg.Done()
}()
for px := 0; px < width; px++ {
x := float64(px)/width*(xmax-xmin) + xmin
z := complex(x, y)
pixelCh <- pixel{x: px, y: py, c: mandelbrot(z)}
}
}(py, y)
}
go func() {
wg.Wait()
close(pixelCh)
}()
<-done
// fmt.Println("hello")
file, err := os.OpenFile("./asyncTender.png", os.O_RDWR|os.O_CREATE, 0666)
if err != nil {
fmt.Println("open file err:", err)
}
defer file.Close()
png.Encode(file, img) // NOTE: ignoring errors
return
}
func mandelbrot(z complex128) color.Color {
const iterations = 200
const contrast = 15
// writeLargeFile("./temp.txt")
const gn = 10
alpha := [][]float64{
{3.6, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 0.2, 0.1},
{0.3, 3.5, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.2},
{0.4, 0.2, 3.4, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.3},
{0.5, 0.3, 0.1, 3.3, 0.1, 0.2, 0.3, 0.4, 0.5, 0.4},
{0.6, 0.4, 0.2, 0.1, 3.2, 0.1, 0.2, 0.3, 0.4, 0.5},
{0.7, 0.5, 0.3, 0.2, 0.1, 3.1, 0.1, 0.2, 0.3, 0.6},
{0.8, 0.6, 0.4, 0.3, 0.2, 0.1, 3.0, 0.1, 0.2, 0.7},
{0.9, 0.7, 0.5, 0.4, 0.3, 0.2, 0.1, 2.9, 0.1, 0.8},
{0.2, 0.8, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1, 2.8, 0.9},
{0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 2.7},
}
beta := []float64{1.1, 1.5, 2.4, 4.1, 6.4, 9.0, 12.9, 18.0, 24.3, 32.8}
gaussJordan(gn, alpha, beta)
// fmt.Println(x)
var v complex128
for n := uint8(0); n < iterations; n++ {
v = v*v + z
if cmplx.Abs(v) > 2 {
return color.Gray{255 - contrast*n}
}
}
return color.Black
}
func calcExecTime(start time.Time) (duration int64) {
return time.Since(start).Milliseconds()
}
func writeLargeFile(filepath string) error {
// 创建文件
f, err := os.Create(filepath)
if err != nil {
fmt.Println("create file err:", err)
return err
}
defer f.Close()
// 写入 10kB 数据
var data [1024]byte // 1MB
for i := 0; i < 10; i++ {
_, err := rand.Read(data[:])
if err != nil {
fmt.Println("read rand err:", err)
return err
}
_, err = f.Write(data[:])
if err != nil {
fmt.Println("write data err:", err)
return err
}
}
return nil
}
func gaussJordan(n int, A [][]float64, b []float64) []float64 {
//高斯-约旦方法的实现
for k := 0; k < n; k++ {
p := k
for i := k + 1; i < n; i++ {
if math.Abs(A[i][k]) > math.Abs(A[p][k]) {
p = i
}
}
A[k], A[p] = A[p], A[k]
b[k], b[p] = b[p], b[k]
for i := k + 1; i < n; i++ {
alpha := A[i][k] / A[k][k]
for j := k; j < n; j++ {
A[i][j] -= alpha * A[k][j]
}
b[i] -= alpha * b[k]
}
}
//回带解方程
x := make([]float64, n)
for i := n - 1; i >= 0; i-- {
sum := b[i]
for j := i + 1; j < n; j++ {
sum -= A[i][j] * x[j]
}
x[i] = sum / A[i][i]
}
return x
} |
Beta Was this translation helpful? Give feedback.
-
这是一个典型的生产者-消费者模型,文中处理的方式的特别之处是采用了阻塞的channel,文中的方式实际上是构建了一种类似semaphore的模型。 |
Beta Was this translation helpful? Give feedback.
-
我靠这个wg.add(1)的位置还有closer goroutine写得真是既优雅又巧妙! |
Beta Was this translation helpful? Give feedback.
-
有一个点很好奇:在makeThumbnails6中如果两次for循环间隔太大,是否导致该函数提前退出。 |
Beta Was this translation helpful? Give feedback.
-
··· 对于另一种方案,这里分两种情况 |
Beta Was this translation helpful? Give feedback.
-
sync.WaitGroup 内部维护了一个计数器,用于记录仍需等待的 Goroutine 数量。这个计数器具有以下特性: |
Beta Was this translation helpful? Give feedback.
-
如下写法也并没有全部输出5呢, import ( |
Beta Was this translation helpful? Give feedback.
-
ch8/ch8-05
中文版
https://gopl-zh.github.io/ch8/ch8-05.html
Beta Was this translation helpful? Give feedback.
All reactions