Wenn eine Goroutine blockiert ist, fallen keine anderen Kosten an als:
- Speichernutzung
- langsamere Müllabfuhr
Die Kosten (in Bezug auf Speicher und durchschnittliche Zeit, um tatsächlich mit der Ausführung einer Goroutine zu beginnen) sind:
Go 1.6.2 (April 2016)
32-bit x86 CPU (A10-7850K 4GHz)
| Number of goroutines: 100000
| Per goroutine:
| Memory: 4536.84 bytes
| Time: 1.634248 µs
64-bit x86 CPU (A10-7850K 4GHz)
| Number of goroutines: 100000
| Per goroutine:
| Memory: 4707.92 bytes
| Time: 1.842097 µs
Go release.r60.3 (December 2011)
32-bit x86 CPU (1.6 GHz)
| Number of goroutines: 100000
| Per goroutine:
| Memory: 4243.45 bytes
| Time: 5.815950 µs
Auf einem Computer mit 4 GB installiertem Speicher wird die maximale Anzahl von Goroutinen auf etwas weniger als 1 Million begrenzt.
Quellcode (Sie müssen diesen nicht lesen, wenn Sie die oben angegebenen Zahlen bereits verstanden haben):
package main
import (
"flag"
"fmt"
"os"
"runtime"
"time"
)
var n = flag.Int("n", 1e5, "Number of goroutines to create")
var ch = make(chan byte)
var counter = 0
func f() {
counter++
<-ch
}
func main() {
flag.Parse()
if *n <= 0 {
fmt.Fprintf(os.Stderr, "invalid number of goroutines")
os.Exit(1)
}
runtime.GOMAXPROCS(1)
var m0 runtime.MemStats
runtime.ReadMemStats(&m0)
t0 := time.Now().UnixNano()
for i := 0; i < *n; i++ {
go f()
}
runtime.Gosched()
t1 := time.Now().UnixNano()
runtime.GC()
var m1 runtime.MemStats
runtime.ReadMemStats(&m1)
if counter != *n {
fmt.Fprintf(os.Stderr, "failed to begin execution of all goroutines")
os.Exit(1)
}
fmt.Printf("Number of goroutines: %d\n", *n)
fmt.Printf("Per goroutine:\n")
fmt.Printf(" Memory: %.2f bytes\n", float64(m1.Sys-m0.Sys)/float64(*n))
fmt.Printf(" Time: %f µs\n", float64(t1-t0)/float64(*n)/1e3)
}
n
. Hast du jedes Mal Glück? :)2758.41 bytes
pro Goroutine, läuft go 1.5.1.Hunderttausende pro Go FAQ: Warum Goroutinen statt Threads? ::
Der Test test / chan / goroutines.go erstellt 10.000 und könnte leicht mehr tun, ist jedoch so konzipiert, dass er schnell ausgeführt wird. Sie können die Nummer auf Ihrem System ändern, um zu experimentieren. Sie können problemlos Millionen ausführen, wenn genügend Speicher vorhanden ist, z. B. auf einem Server.
Um die maximale Anzahl von Goroutinen zu verstehen, beachten Sie, dass die Kosten pro Goroutine in erster Linie der Stapel sind. Nochmals per FAQ:
Bei einer Back-of-the-Envelope-Berechnung wird davon ausgegangen, dass jeder Goroutine eine 4-KiB- Seite für den Stapel zugewiesen ist (4 KiB ist eine ziemlich einheitliche Größe), plus ein wenig Overhead für einen Steuerblock (wie einen Thread- Steuerblock ) für die Laufzeit; Dies stimmt mit dem überein, was Sie beobachtet haben (2011 vor Go 1.0). Somit würden 100 Ki-Routinen ungefähr 400 MiB Speicher benötigen, und 1 Mi-Routinen würden ungefähr 4 GiB Speicher benötigen, was auf dem Desktop immer noch verwaltbar ist, ein bisschen viel für ein Telefon und auf einem Server sehr verwaltbar. In der Praxis hat der Startstapel eine Größe von einer halben Seite (2 KiB) bis zwei Seiten (8 KiB), was ungefähr korrekt ist.
Die Größe des Startstapels hat sich im Laufe der Zeit geändert. es begann bei 4 KiB (eine Seite), dann wurde in 1.2 auf 8 KiB (2 Seiten) erhöht, dann wurde in 1.4 auf 2 KiB (eine halbe Seite) verringert. Diese Änderungen waren auf segmentierte Stapel zurückzuführen, die Leistungsprobleme beim schnellen Hin- und Herwechseln zwischen Segmenten verursachten ("Hot-Stack-Split"), also zur Minderung erhöht (1.2) und dann verringert, wenn segmentierte Stapel durch zusammenhängende Stapel ersetzt wurden (1.4):
Go 1.2 Versionshinweise: Stapelgröße :
Go 1.4 Versionshinweise: Änderungen an der Laufzeit :
Der Speicher pro Goroutine ist größtenteils gestapelt, beginnt niedrig und wächst, sodass Sie kostengünstig viele Goroutinen haben können. Sie könnten einen kleineren Startstapel verwenden, aber dann müsste er früher wachsen (Speicherplatz auf Kosten der Zeit gewinnen), und die Vorteile nehmen ab, da der Steuerblock nicht schrumpft. Es ist möglich, den Stapel zu entfernen, zumindest wenn er ausgetauscht wird (z. B. alle Zuordnungen auf dem Heap vornehmen oder den Stapel auf dem Heap beim Kontextwechsel speichern), obwohl dies die Leistung beeinträchtigt und die Komplexität erhöht. Dies ist möglich (wie in Erlang) und bedeutet, dass Sie nur den Kontrollblock und den gespeicherten Kontext benötigen. Dies ermöglicht einen weiteren Faktor von 5 × –10 × für die Anzahl der Goroutinen, der jetzt durch die Größe des Kontrollblocks und die Größe der Goroutine auf dem Heap begrenzt ist -lokale Variablen. Dies ist jedoch nicht besonders nützlich, es sei denn, Sie benötigen Millionen winziger schlafender Goroutinen.
Da viele Goroutinen hauptsächlich für E / A-gebundene Aufgaben verwendet werden (konkret zur Verarbeitung blockierender Systemaufrufe, insbesondere von Netzwerk- oder Dateisystem-E / A), stoßen Sie viel häufiger auf Betriebssystembeschränkungen für andere Ressourcen, nämlich Netzwerksockets oder Dateihandles : Golang-Nüsse ›Die maximale Anzahl von Goroutinen und Dateideskriptoren? . Der übliche Weg, dies zu beheben, besteht darin, einen Pool der knappen Ressourcen zu erstellen oder einfach die Anzahl über ein Semaphor zu begrenzen . Siehe Beibehalten von Dateideskriptoren in Go und Begrenzen der Parallelität in Go .
quelle
Das hängt ganz von dem System ab, auf dem Sie laufen. Aber Goroutinen sind sehr leicht. Ein durchschnittlicher Prozess sollte keine Probleme mit 100.000 gleichzeitigen Routinen haben. Ob dies für Ihre Zielplattform gilt, können wir natürlich nicht beantworten, ohne zu wissen, um welche Plattform es sich handelt.
quelle
Um es zu paraphrasieren, es gibt Lügen, verdammte Lügen und Maßstäbe. Wie der Autor des Erlang-Benchmarks gestand,
Was ist Ihre Hardware, was ist Ihr Betriebssystem, wo ist Ihr Benchmark-Quellcode? Was versucht der Benchmark zu messen und zu beweisen / zu widerlegen?
quelle
Hier ist ein großartiger Artikel von Dave Cheney zu diesem Thema: http://dave.cheney.net/2013/06/02/why-is-a-goroutines-stack-infinite
quelle
debug.SetMaxStack
die "neue" Standard-Maximalstapelgröße pro Goroutine von 1 GB und 250 MB (auf 64-Bit- bzw. 32-Bit-Systemen) überschrieben. Dh Goroutine-Stapelgrößen waren seit Go1.2 nicht unendlich.Wenn die Anzahl der Goroutinen jemals zu einem Problem wird, können Sie sie für Ihr Programm leicht einschränken:
Siehe mr51m0n / gorc und dieses Beispiel .
quelle