J'ai un processus en déplacement. Voici un exemple de comptage de lignes dans du texte, bien que la question soit censée être beaucoup plus générale que cet exemple particulier:
func lineCount(s string) int {
count := 0
for _, c := range s {
if c == '\n' {
count++
}
}
return count
}
D'accord, pas mal, mais c'est trop lent, alors rendons-le simultanément:
func newLine(r rune, c chan<- struct{}, wg sync.WaitGroup) {
if r == '\n' {
c <- struct{}
}
wc.Done()
}
func sumLines(c <-chan struct{}, result chan<- int) {
count := 0
for _ := range c {
count++
}
result <- count
}
func lineCount(s string) int {
c := make(chan struct{})
var wg sync.WaitGroup
for _, r := range s {
wg.Add(1)
go newLine(r, c, wg)
}
result := make(chan int)
go sumLines(c, result)
wg.Wait()
close(c)
return <-result
}
Mieux, parce que maintenant, nous utilisons tous nos cœurs, mais soyons honnêtes, un goroutine par lettre est probablement surchargé et nous ajoutons probablement Beaucoup de frais généraux entre le nombre horrible de gorouts et le verrouillage/déverrouillage du groupe d'attente. Faisons mieux:
func newLine(s string, c chan<- int, wg sync.WaitGroup) {
count := 0
for _, r := range s {
if r == '\n' {
count++
}
}
c <- count
wc.Done()
}
func sumLines(c <-chan int, result chan<- int) {
count := 0
for miniCount := range c {
count += miniCount
}
result <- count
}
func lineCount(s string) int {
c := make(chan int)
var wg sync.WaitGroup
for i := 0; i < len(s)/MAGIC_NUMBER; i++ {
wg.Add(1)
go newLine(s[i*MAGIC_NUMBER : (i+1)*MAGIC_NUMBER], c, wg)
}
result := make(chan int)
go sumLines(c, result)
wg.Wait()
close(c)
return <-result
}
Alors maintenant, nous divisons notre chaîne uniformément (sauf la dernière partie) en goroutines. J'ai 8 noyaux, alors j'ai déjà eu une raison de définir MAGIC_NUMBER
à plus de 8 ans? Encore une fois, alors que j'écrivais cette question avec l'exemple de comptage des lignes dans le texte, la question est réellement dirigée vers toute situation dans laquelle le problème peut être tranché et de couper en dés cause, et c'est vraiment le programmeur de décider du nombre de tranches aller pour.
Pour une tâche simple, comme comptant de nouvelles lignes, il va être assez difficile de faire mieux qu'un simple nombre de filetés à un seul seuil, votre goulot d'étranglement va lire la chaîne de disque ou de réseau, qui est une opération de série de toute façon et un seul fil va déjà être nettement plus rapide que l'IO correspondant. Pour le cas plus général, je suggérerais de lire sur Map-Réduire Modèle de programmation.
Comme la réponse de Philip Kendall suggère cependant, IO tâche liée est l'endroit où vous bénéficierez d'avoir exécuté plus de threads que de nœuds, si vous avez une tâche liée à la CPU, vous bénéficierez de beaucoup de Splitez le travail plus que vous n'avez de noyaux de travail.
Oui. Exemple: NVIDIA recommande d'environ 3 fois le nombre d'alus depuis la commutation de contexte un éclair rapide mais la mémoire est extrêmement lente par comparaison. En particulier, vous pourriez envisager l'accès à la mémoire GPU comme des E/S. Comme d'autres personnes ont dit, vous voulez que vous utilisiez "juste" toutes vos ressources au fur et à mesure de leur disponibilité et que la distribution des consommateurs dépend ensuite de la configuration matérielle et de la nature du problème résolu. Le solde est généralement médié par un système d'exploitation et ses coûts de fonctionnement intérieur également et doivent être pris en compte. Par exemple, pour certaines applications RT Versions de Linux sont nécessaires car les machines de préemption standard ne sont pas appropriées pour RT Applications.