亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

為什么添加并發會使該golang代碼變慢?

為什么添加并發會使該golang代碼變慢?

Go
鴻蒙傳說 2021-05-13 13:19:44
我已經修改了一些Go代碼,以解決與我姐夫玩的視頻游戲有關的我的好奇心。本質上,下面的代碼模擬了游戲中與怪物的互動,以及他期望他們在失敗后掉落物品的頻率。我遇到的問題是,我希望像這樣的一段代碼對于并行化來說是完美的,但是當我并發添加時,完成所有模擬所花費的時間往往會使原始代碼的速度降低4-6倍。沒有并發。為了使您更好地理解代碼的工作方式,我有三個主要功能:交互功能,它是玩家和怪物之間的簡單交互。如果怪物掉落物品,則返回1,否則返回0。仿真功能運行多個交互,并返回一部分交互結果(即1和0表示成功/不成功的交互)。最后,有一個測試函數,它運行一組模擬并返回一部分模擬結果,這些結果是導致掉落物品的相互作用的總數。這是我嘗試并行運行的最后一個函數?,F在,我可以理解,如果我為要運行的每個測試創建一個goroutine,為什么代碼會變慢。假設我正在運行100個測試,則在MacBook Air的4個CPU上的每個goroutine之間進行上下文切換會降低性能,但是我只創建與我擁有的處理器一樣多的goroutine,并將測試次數除以goroutines。我希望這實際上可以提高代碼的性能,因為我可以并行運行每個測試,但是,當然,我會遇到嚴重的問題。我很想弄清楚為什么會這樣,所以任何幫助將不勝感激。以下是不帶go例程的常規代碼:package mainimport (    "fmt"    "math/rand"    "time")const (    NUMBER_OF_SIMULATIONS = 1000    NUMBER_OF_INTERACTIONS = 1000000    DROP_RATE = 0.0003)/** * Simulates a single interaction with a monster * * Returns 1 if the monster dropped an item and 0 otherwise */func interaction() int {    if rand.Float64() <= DROP_RATE {        return 1    }    return 0}/** * Runs several interactions and retuns a slice representing the results */func simulation(n int) []int {    interactions := make([]int, n)    for i := range interactions {        interactions[i] = interaction()    }    return interactions}/** * Runs several simulations and returns the results */func test(n int) []int {    simulations := make([]int, n)    for i := range simulations {        successes := 0        for _, v := range simulation(NUMBER_OF_INTERACTIONS) {            successes += v        }        simulations[i] = successes    }    return simulations}func main() {    rand.Seed(time.Now().UnixNano())    fmt.Println("Successful interactions: ", test(NUMBER_OF_SIMULATIONS))}并且,這是帶有goroutines的并發代碼:package mainimport (    "fmt"    "math/rand"    "time"    "runtime")const (    NUMBER_OF_SIMULATIONS = 1000    NUMBER_OF_INTERACTIONS = 1000000    DROP_RATE = 0.0003)
查看完整描述

3 回答

?
守著一只汪

TA貢獻1872條經驗 獲得超4個贊

在我的Linux四核i7筆記本電腦上測試您的代碼我明白了

這是一個Google Spreadsheet

http://img1.sycdn.imooc.com//60b44441000185c305620081.jpg

這表明,在Linux下,至少每個內核的縮放比例幾乎是線性的。

我認為您可能沒有看到這有兩個原因。

首先是您的macbook air僅具有2個真正的核心。它有4個超線程,這就是為什么它報告4個最大cpus的原因。通常,超線程僅在單個內核上提供15%的額外性能,而不是您期望的100%。因此,僅在macbook air上基準測試1個或2個CPU!

另一個原因可能是與Linux相比OS X線程的性能。他們使用不同的線程模型,這可能會影響性能。


查看完整回答
反對 回復 2021-05-31
?
MMTTMM

TA貢獻1869條經驗 獲得超4個贊

您的代碼正在抽樣一個二項式隨機變量B(N,p),其中N是試驗次數(此處為1M),p是成功進行單個試驗的概率(此處為0.0003)。


一種方法是建立一個累積概率表T,其中T [i]包含試驗總數小于或等于i的概率。要生成樣本,您可以選擇一個統一的隨機變量(通過rand.Float64),然后在表中找到包含大于或等于該概率的第一個索引。


這里有點復雜,因為您有一個非常大的N和一個相當小的p,因此,如果您嘗試構建表,那么數字和算術精度就會非常麻煩。但是您可以構建一個較小的表(假設有1000個大表)并對其進行1000次采樣,以進行一百萬次試用。


這是完成所有這些工作的一些代碼。它不太優雅(1000 是硬編碼的),但它在我的舊筆記本電腦上不到一秒鐘就生成了 1000 次模擬。通過例如將BinomialSampler的構造從循環中移出,或者通過使用二進制搜索而不是線性掃描來查找表索引,可以很容易地進一步優化。


package main


import (

    "fmt"

    "math"

    "math/rand"

)


type BinomialSampler []float64


func (bs BinomialSampler) Sample() int {

    r := rand.Float64()

    for i := 0; i < len(bs); i++ {

        if bs[i] >= r {

            return i

        }

    }

    return len(bs)

}


func NewBinomialSampler(N int, p float64) BinomialSampler {

    r := BinomialSampler(make([]float64, N+1))

    T := 0.0

    choice := 1.0

    for i := 0; i <= N; i++ {

        T += choice * math.Pow(p, float64(i)) * math.Pow(1-p, float64(N-i))

        r[i] = T

        choice *= float64(N-i) / float64(i+1)

    }

    return r

}


func WowSample(N int, p float64) int {

    if N%1000 != 0 {

        panic("N must be a multiple of 1000")

    }

    bs := NewBinomialSampler(1000, p)

    r := 0

    for i := 0; i < N; i += 1000 {

        r += bs.Sample()

    }

    return r

}


func main() {

    for i := 0; i < 1000; i++ {

        fmt.Println(WowSample(1000000, 0.0003))

    }

}


查看完整回答
反對 回復 2021-05-31
  • 3 回答
  • 0 關注
  • 265 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號