Hallo,
Beispiel: Der Ansatz des Giant Lock funktioniert prima, ist einfach und ist verdammt schnell für Uniprozessorsysteme (da sind die Locks immer frei). Auch bei 2..4 CPUs ist er gut
Du meinst also der Giant Lock (vorallem wenn ich da zwangsläufig an Linux = monolith denke) ist auch noch bei 2 bis 4 CPUs gut? Das kann ich einfach nicht glauben.
Dann lässt du es halt bleiben.
Allerdings sollten wir erstmal klären, was genau meinst du mit Giant Lock? Einen Lock der im Endeffekt den gesamten Kernel schützt?
Exakt: Nur ein Thread im System kann gleichzeitig im Kernel aktiv sein.
Würde ich den bei mir verwenden, würde schon das Initialisieren des Kernels linear pro CPU ansteigen und bei nem monolithen dürfte es noch schlechter sein als bei nem MikroKernel (zwecks das die ganzen Subsysteme außerhalb des Kernels liegen und erstmal nicht direkt davon betroffen sind).
Richtig, aber da verwechselst du wieder Skalierbarkeit ("lineares Zeitverhalten") mit Performance ("absolute Dauer"). Wenn deine Initialisierung 1s @ 1CPU dauert und 2s @ 64CPU, dann spielt das effektiv keine Rolle.
Angenommen, Threads verbringen nur einen sehr geringen Teil ihrer Zeit im Kernel und den Großteil ihrer Zeit rechnen oder schlafen sie. Für n CPUs hast du maximal n aktive Threads. Die Kollisionswahrscheinlichkeit, dass zwei Threads gleichzeitig in den Kernel wollen, ist für n=1 gleich Null, bei unendlich vielen CPUs gleich Eins, dazwischen dürfte es ein logarithmisches Verhalten haben. Das ändert sich grundsätzlich auch bei feineren Locks nicht (Skalierbarkeit bleibt schlecht, Performance wird wesentlich verbessert).
Ich vermute, dass lockfreie Algorithmen aufwändiger und damit langsamer sind. Wenn also (Kollisionswahrscheinlichkeit * durchschnittliche Wartezeit) kleiner ist als der Overhead durch einen lockfreien Algorithmus, dann solltest du Locks implementieren, sonst nicht. Daher meine unbewiesene Behauptung, dass für n=2..4 Locks das Optimum sind (und auch Giant Locks nicht unbedingt das schlechteste). während großem n jegliche Locks eher schädlich sind.
Und schon bist du wieder bei Kompromisslösungen.
(Es lässt sich übrigens für jeden Algorithmus ein Worst Case konstruieren, der ihn schlecht aussehen lässt.)
Im Kernel (ausgehend davon, das während man ein Lock hält die Ints aus sind) kann man ruhig Locks nehmen, da die meisten Probleme (außer Deadlock) dort nicht existieren.
Nur, weil ein Algorithmus ein Problem löst, welches in einem gewissen Kontext nicht existiert, ist er noch lange nicht schlecht.
Aber ja, im Kernel kann man Locks benutzen - tun die meisten Betriebssysteme ja auch.
Das sollte wahrscheinlich sogar das performanteste sein (und auch der Stromverbrauch, da man beim spinnen "pause" nutzen kann) und im UserSpace setzt man am besten auf Lock-Free (sollte Performance mäßig und auch Problem mäßig, vorrausgesetzt die Algos sind in Ordnung, die bessere Variante sein) mit der Einschränkung nur da wo paralleler Zugriff auch Sinn macht (z.B. Dateisystem).
Das eine hat mit dem anderen nichts zu tun: Code ist Code, ob der jetzt im Kernel oder im Userspace rumliegt, ist für die Performance von Algorithmen relativ egal. Locks skalieren halt scheiße, das betrifft die Kernel-Interna selbst aber genauso wie die Brücke zwischen Userspace und Kernel. (Wenn 20k Threads gleichzeitig READ aufrufen, ist ein Lock für die benutzten Datenstrukturen schlecht, auch im Kernel.) Im eigentlichen Userspace kann dir das als OS-Entwickler übrigens egal sein, weil ob die einzelnen Apache-Threads nun lockfrei auf den Apache-Kern zugreifen oder nicht, ist nicht dein Problem.
Es hängt halt grundsätzlich vom Einzelfall ab und von den Kosten, die entstehen. Wenn man mit dem Worst Case rechnet, dann sind Locks böse (Deadlock), aber normalerweise geht man vom Average Case aus und versucht, den zu optimieren. Und für wenig gleichzeitige Zugriffe sind Locks halt recht effizient.
Denn es gibt auch viele Bsp. wo paralleler Zugriff einfach kein Sinn macht und man alles eh serialisieren muss (z.B. Laufwerke).
Laufwerke sind dank NCQ und modernen COW-Dateisystemen durchaus parallel ansprechbar. Serialisieren tut dann erst der Controller. Paralleler Zugriff auf eine serielle Schnittstelle ist da schon schlimmer.
Gruß,
Svenska