Abgesehen von den in den anderen Antworten genannten Punkten (schwer zu beweisen, dass Operationen unabhängig sind und Programmierer seriell denken), muss ein dritter Faktor berücksichtigt werden: die Kosten für die Parallelisierung.
Die Wahrheit ist, dass Thread-Parallelität mit sehr erheblichen Kosten verbunden ist:
Thread-Erstellung ist sehr teuer: Für den Kernel ist das Starten eines Threads ungefähr das gleiche wie das Starten eines Prozesses. Ich bin mir nicht sicher über die genauen Kosten, aber ich glaube, dass sie in der Größenordnung von zehn Mikrosekunden liegen.
Thread-Kommunikation über Mutexe ist teuer: In der Regel erfordert dies einen Systemaufruf auf jeder Seite, wobei möglicherweise ein Thread in den Ruhezustand versetzt und wieder aktiviert wird. Dies führt zu Latenz sowie zu Cold Caches und gelöschten TLBs. Im Durchschnitt kostet das Aufnehmen und Freigeben eines Mutex ungefähr eine Mikrosekunde.
So weit, ist es gut. Warum ist dies ein Problem für die implizite Parallelität? Weil implizite Parallelität am einfachsten im Kleinen zu beweisen ist. Es ist eine Sache zu beweisen, dass zwei Iterationen einer einfachen Schleife unabhängig voneinander sind, es ist eine ganz andere Sache zu beweisen, dass das Drucken von etwas stdout
und das Senden einer Abfrage an eine Datenbank unabhängig voneinander sind und parallel ausgeführt werden können ( Der Datenbankprozess könnte sich auf der anderen Seite der Pipe befinden!).
Das heißt, die implizite Parallelität, die ein Computerprogramm nachweisen kann, ist wahrscheinlich nicht ausnutzbar, da die Kosten für die Parallelisierung höher sind als der Vorteil der Parallelverarbeitung. Andererseits ist die große Parallelität, die eine Anwendung wirklich beschleunigen kann, für einen Compiler nicht nachweisbar. Stellen Sie sich vor, wie viel Arbeit eine CPU in einer Mikrosekunde leisten kann. Wenn nun die Parallelisierung schneller sein soll als das serielle Programm, muss das parallele Programm in der Lage sein, alle CPUs für mehrere Mikrosekunden zwischen zwei Mutex-Aufrufen beschäftigt zu halten. Das erfordert eine wirklich grobkörnige Parallelität, die sich kaum automatisch beweisen lässt.
Schließlich keine Regel ohne Ausnahme: Die Ausnutzung impliziter Parallelität funktioniert dort, wo keine Threads beteiligt sind, was bei der Vektorisierung des Codes der Fall ist (unter Verwendung von SIMD-Befehlssätzen wie AVX, Altivec usw.). Das funktioniert in der Tat am besten für die Parallelität im kleinen Maßstab, die relativ einfach zu beweisen ist.