A CPU Bound műveletek végrehajtása során nyilvánvaló, hogy ha párhuzamosságot teszünk (több gorutin hozzáadásával), akkor a teljesítmény javul. De hogyan működik ez az i/o kötött műveletekben? Dolgozzunk ezen.
Webhelyek feltérképezése
A webhelyek feltérképezése i/o kötött művelet, mert többnyire a hálózati és az i/o műveletekre várunk. Nem használjuk ki a szálakat, gorutinjaink az idő nagy részében várakoznak.
package iobench import ( "net/http" "sync" ) func generateLinks(url string) []string { urls := make([]string, 10) for i := 0; i < 10; i++ { links[i] = url } return urls } func syncCrawl(urls []string) { for _, url := range urls { http.Get(url) } } func concurrentCrawl(urls []string) { var wg sync.WaitGroup wg.Add(len(urls)) for _, url := range urls { go func(url string) { defer wg.Done() http.Get(url) }(url) } wg.Wait() }
A syncCrawl funkcióban szinkronizálási hívást végzünk, így sorrendben fogjuk feltérképezni a webhelyeket.
A concurrentCrawl funkcióban aszinkronhívást végzünk, így a webhelyeket egyidejűleg térképezzük fel a párhuzamosból. Ne feledje, hogy a párhuzamos és párhuzamos programok különböznek egymástól. A párhuzamos programok soron kívül futnak be, többnyire várakoznak, így nem használják aktívan a hardverszálakat. A párhuzamos programok szintén nem futnak be, de párhuzamosan. Gondolj csak arra, hogy te vagy az egyetlen, aki tésztát készít, felforralod a vizet, egyidejűleg működik, és veszel tésztát, közben szószt készítesz, felforr a víz, erre vársz. Más esetben gondolja úgy, hogy 2 fő, és szószt készít, az egyik salátát, a másik pedig tésztát készít.
Benchmarking
Határozzuk meg a kódot. Az i/o működés szimulálására teszek időt.Alvó üzemmódban, mert a valós rendszerben az i/o műveletekre várunk.
package iobench import ( "net/http" "net/http/httptest" "testing" "time" ) func BenchmarkSyncRead(b *testing.B) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { time.Sleep(time.Millisecond) w.WriteHeader(200) })) defer srv.Close() urls := generateLinks(srv.URL) b.ResetTimer() for i := 0; i < b.N; i++ { syncCrawl(urls) } } func BenchmarkConcurrentRead(b *testing.B) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { time.Sleep(time.Millisecond) w.WriteHeader(200) })) defer srv.Close() urls := generateLinks(srv.URL) b.ResetTimer() for i := 0; i < b.N; i++ { concurrentCrawl(urls) } }
Eredmények
Ha korlátozom a cpu-t eggyel, akkor egyszálú környezetben futok. Lásd ezt a párhuzamosságot. Nincs paralizmus, de még mindig kihasználom a koncrreunitást. Amíg egy gorutin a hálózati műveletekre vár, a go futási idő kontextusban váltja a gorutinokat.
További cpu hozzáadása nem segít
Nem használunk aktívan cpu-t. Még ha 5-re állítom is a cpu-t, akkor sem látunk különbséget. Mert a cpu semmiben nem tud segíteni. ezt be tudom bizonyítani.
Lásd az eredmény szinte nincs különbség. Ha CPU-hoz kötött műveleteket végeznék, láthatná a különbséget.