Beiträge anzeigen

Diese Sektion erlaubt es dir alle Beiträge dieses Mitglieds zu sehen. Beachte, dass du nur solche Beiträge sehen kannst, zu denen du auch Zugriffsrechte hast.


Nachrichten - FlashBurn

Seiten: 1 ... 40 41 [42] 43
821
Lowlevel-Coding / Re: spinlocks
« am: 16. April 2010, 18:44 »
Zitat von: erik
Sorry, für mein Delay, ich hab eine recht bewegte Woche hinter mir.
Kein Prob, habe die Woche auch nichts wirklich zustande gebracht ;)

Zitat von: erik
Der Vorteil ist das die ganze Zeitrechnerei nicht im User-Space mit relativen Zeiten sondern direkt im Kernel mit absoluten  Zeiten stattfindet, der Kernel errechnet den nächsten Trigger-Zeitpunkt nicht als Differenz sondern absolut aus dem Zeitpunkt der Event-Erstellung und der Frequenz bzw. Periodendauer. Da die Triggerfrequenz nicht unbedingt Teilergleich (nennt man das so, ich denke da gibt es nen besseren Begriff) mit der System-Timer-Frequenz ist kommt es zu einem gewissen Jitter wenn die Trigger-Zeitpunkte in das System-Zeit-Raster überführt werden müssen, aber wenn die Rechnung im Kernel ordentlich gecodet ist dann ergibt sich auch auf lange Sicht keine Abweichung. Wenn man hingegen immer die sleep-Funktion mit einer Differenz aufruft dann werden sich da unvermeidlich Fehler aufakkumulieren, außerdem kannst Du sleep nicht mit negativen Werten benutzen falls Dein Code mal ein klein wenig überzogen hat (dann wäre auch Dein neuer Start-Zeitpunkt falsch).
Nur mal als kurze Antwort, hast du schonmal versucht sowas zu schreiben (und dabei das restliche System nicht aus den Augen verloren)? Ich weiß das bei meinem Code eine Abweichung (welche auf nem alten PC mit nur nem PIT bei so 2µs pro Scheduler-Aufruf liegt) auftritt und die sich dann summiert. Bei nem Multi-CPU System liegt es am I/O-Aufkommen (und Spinlock Aufkommen) wie ungenau diese Zeit ist.

Zitat von: erik
Hä, was meinst Du? Kann es sein das Du "Schreibbefehle" und "schreibst" meinst?
Du meintest doch das es nicht schlecht wäre ne Art "Read-Combining" zu haben oder? Und da müsstest du ja auf die Daten warten, bis halt genug Lesebefehle zusammengekommen sind, oder? Obwohl mir gerade auffällt dass das dann beim Write-Combining das selbe Problem geben würde, aber es scheint ja zu funktionieren. Ich habe halt gerade ein Problem damit, was passiert wenn du nur ein Byte schreiben willst, die CPU kann ja nicht warten bis andere Schreibbefehle den Buffer voll gemacht haben, denn es kann ja sein, das andere CPUs auf diesen Wert zugreifen wollen (dann würde dir auch ne Spinlock zum Schützen von kritischen Bereichen nichts nutzen).

Ich mach dann mal nen Thread für Schedulingstrategien auf, müsste mich damit ja eh mal befassen.
822
Lowlevel-Coding / Re: spinlocks
« am: 12. April 2010, 17:36 »
Zitat von: erik
Das würde ich lieber mit einem periodischem Event machen. Da kannst Du Dich auch auf die Periodendauer verlassen, wenn Du selber immer mitrechnest akkumulieren sich Fehler auf. Wenn Du damit rechnest das 0 raus kommt musst Du auch damit rechnen das was negatives bei raus kommt. Auch da wäre der periodische Event die bessere Wahl, da könntest Du einzelne Ausreißer kompensieren falls Du im Durchschnitt immer deutlich unterm Zeitlimit bleibst.
Ich muss dir ganz ehrlich sagen, irgendwie versteh ich grad nur Bahnhof ;)

Also stell dir vor du schreibst nen Framelimiter. Da holst du dir die aktuelle Zeit, berechnest den Frame, holst dir die nun aktuelle Zeit, bildest die Differenz und wartest gegebenenfalls (was für ein Wort ;) ).

Zitat von: erik
Am besten noch in der libc.
Ich würde eher sagen, da auch ;) Denn du weißt nie was so ein Programmierer alles für Späße treibt.

Zitat von: erik
Das ist bei einem Scheduler aber wahrlich keine gute Variante.
Wieso? Ich denke mal du hast mich nicht richtig verstanden (da ich dir Infos vorenthalten habe).

Also mein Scheduler hat 32 Prioritätswarteschlangen (die eher wie ein Stack aufgebaut sind), eine Idlewarteschlange und (jedenfalls bald) eine Realtimewarteschlange. Jede Warteschlange ist wie gesagt eher wie ein Stack aufgebaut, da ich immer am Anfang der Liste/Queue adde.
Dann habe ich noch 2 Arrays, einmal ReadyQueueArray und einmal RunQueueArray (beide sind halt Arrays mit 32 Elementen, 1 Element für eine Priorität). Hat ein Thread seine Zeitscheibe verbraucht, dann kommt er in das RunQueueArray, ist das ReadyQueueArray leer, wird geschaut ob das RunQueueArray Elemente enthält, wenn ja werden die beiden Arrays vertauscht und wenn nicht laufen entweder die Threads aus der IdleQueue oder der IdleThread.

Die Idee ist, das es ein O(1) Scheduler ist (an den alten Linuxscheduler angelehnt) und das jeder Thread seiner Priorität entsprechend dran kommt. So vermeide ich nebenbei auch, das ein Thread theoretisch das ganze System lahmlegen kann (vorausgesetzt die Priorität ist hoch genug).

Du könntest jetzt sagen, dass das sowieso nicht passieren dürfte, da man die Priorität ja ändern sollte, mach ich auch, aber immer nur im Bereich -5/+5 der am Anfang festgelegten Priorität. So will ich vermeiden, das ein CPU intensiver Thread ganz bis nach unten durchgereicht wird.

(Vielleicht sollte man dazu mal nen extra Thread aufmachen, wo dann über Schedulingstrategien diskutiert wird.)

Zitat von: erik
Für die Lock-Variablen ist IMHO richtiges non-cachable die beste Variante. Wenn Du richtig kopieren möchtest (als Benchmark) dann würde ich mal "Write-Combining" probieren, das dürfte zumindest den Schreib-Teil beschleunigen. Eigentlich schade das es in aktuellen CPUs fürs Lesen keine Entsprechung zum "Write-Combining" gibt.
Wo ist jetzt der Unterschied zw. non-cacheable und uncachceable (ich wollte das einfach über die Flags in der Pagetable machen)?
Wenn ich dich richtig verstehe, meinst du das man mehrere Lesebefehle koppelt und dann erst den Speicher liest? Ich meine das geht ja schlecht, wenn du nur 1byte liest und daraufhin dann viele Bytes schreibst, müsstest du ja ewig warten, oder habe ich da am Write-Combining was falsch verstanden?
823
Lowlevel-Coding / Re: spinlocks
« am: 12. April 2010, 14:01 »
Zitat von: erik
Hä, für was soll sowas gut sein? Damit lastest Du die CPU zu exakt 50% aus (die Pausenzeiten sind ja immer genau so lang wie die Arbeitszeiten). Also ich kann mir beim besten Willen nicht vorstellen wozu das nützlich sein soll, da solltest Du erst erklären was damit bezweckt werden soll.
Ups, Denkfehler ;) Ich meinte natürlich sowas, das er die Berechnung in einer bestimmten Zeit haben will und wenn noch Zeit über ist, dann macht er nen sleep(restlicheZeit) (wobei halt auch 0 raus kommen könnte).

Zitat von: erik
Bis jetzt hab ich das immer so erlebt das sleep(0) einfach nur die restliche Zeitscheibe frei gibt. Für Windows hab ich das gefunden: http://blog.m-ri.de/index.php/2010/01/23/was-denn-nun-switchtothread-sleep0-sleep1-2/, die 2 Kommentare stellen einiges klarer (vor allem der erste). Ein Sleep(0) ist natürlich nur dann sinnvoll wenn man davon aus gehen kann das es andere Threads gibt welche die CPU gerne nutzen würden, was beim warten auf einen mittelkurzen Lock natürlich gegeben ist.
Wie das mit sleep(0) bei POSIX ist weiß ich aber auch nicht genau. Ich hab mal für ein kleines RT-OS programmiert (das offiziell POSIX-Konform ist) und dort hat sleep(0) auch nur die aktuelle Zeitscheibe freigegeben und den Thread hinten an die entsprechende Scheduller-Liste angehängt (stand so in der Doku).
Es steht Dir natürlich frei das in Deinem OS fundamental anders zu implementieren, aber was versprichst Du Dir davon?
Ich kann das ganz leicht implementieren, indem ich bei sleep(0) einfach meinen Syscall zum abgeben aufrufe.
Der Grund warum das so bei meinem Scheduler (im Moment) nicht funktioniert ist, das ich immer am Anfang einer Liste adde (ist einfacher und geht schneller). Desweiteren kommt hinzu das ich mich noch nicht richtig mit scheduling Strategien auseinander gesetzt habe, aber soweit ich weiß ist meine niht ganz "normal". Da ich ne Ready- und ne Runqueue habe (wenn die Zeitscheibe eines Threads abgelaufen ist kommt er in die Runqueue, ist die Readyqueue leer, wird sie mit der Runqueue vertauscht, so dass alle bis auf die Idle-Threads min. einmal dran kommen).

Mal wieder back to topic, ich werd demnächst (sobald ich dazu komme) mal nen kleinen Benchmark machen, wie sich das Schreiben/Lesen auf uncachble Speicher auswirkt. Ich kann da leider nur testen, wie der best case aussieht, obwohl wohl eher der worst case (viele/alle CPUs wollen den Lock) interessanter wäre.
824
Lowlevel-Coding / Re: spinlocks
« am: 11. April 2010, 17:14 »
Zitat von: erik
Es gibt viele Befehle die nichts mit Rechnen oder Integern zu tun haben, z.B. die vielen MOV-Varianten inklusive der Speicherzugriffe.
Da kannst du aber davon ausgehen, das wenn du ne CPU von einem anderem Hersteller auf das selbe Board packst der Speicherzugriff (so fern er nicht vom Cache abgefangen wird) gleich lang dauern.
Zumal was macht man denn im Endeffekt, was wirklich Leistung kostet? Es läuft immer auf das Rechnen (ein Vergleich ist nichts anderes) mit Zahlen zurück. Du holst ne Zahl, bearbeitest (rechnest damit) sie und schreibst sie wieder irgendwo hin und diese Zahlen sind halt Integerzahlen (auch fixed Point Zahlen sind im Endeffekt Integerzahlen).

Zitat von: erik
War ja nur ein Vorschlag. Ich denke das man heutzutage nicht allzu viel verkehrt macht wenn man "nur" einen SMP fähigen Kernel hat.
Naja, mein Kernel soll ab Pentium aufwärts laufen und zu sagen ich hätte nur nen SMP Kernel ist nicht ganz zutreffend. Denn viele Sachen werden anders gemacht oder findest du erst gar nicht im Kernel, wenn er auf nem SMP System läuft.
Ich wollte meinen Kernel so klein wie möglich halten (also der Code und die Daten die nachher im Speicher liegen), aber die Komplexität nicht bis ins unendliche treiben und da muss man halt irgendwo Abstriche machen (deswegen Spinlocks die eher für SMP Systeme sind, da ist der Worst-Case niedriger als wenn ich Funktionen nutzen würde).

Zitat von: erik
Also bei sleep(0) solte das selbe passieren wie wenn der Timer-IRQ kommt, bei einem x86-SMP eben der IRQ vom Local-APIC-Timer.
Das ist halt interpretations Sache. Ich stelle mir das halt so vor, das Programm liest die aktuelle Zeit, rechnet ein wenig rum und liest dann wieder die aktuelle Zeit, bildet die Differenz und macht mit der Differenz nen "sleep(diff)". Wenn der Wert der sleep übergeben wurde "0" ist (aus welchem Grund auch immer) und der Thread theoretisch noch Zeit übrig hätte, lasse ich ihn sofort wieder laufen. Ich weiß jetzt allerdings nicht wie sleep mit einem Wert von "0" definiert ist, könnte mir aber vorstellen dass das nirgends richtig steht (jetzt mal auf POSIX bezogen, obwohl ich den Standard nicht sehr mag).
825
Lowlevel-Coding / Re: spinlocks
« am: 10. April 2010, 08:52 »
Zitat von: erik
Dann kompiliere den Kernel 2 mal (einmal für Single-CPU und ein mal für SMP) und lass den Boot-Loader den passenden nehmen. Die älteren NT-basierenden Windowsen haben das so gemacht (wie das in aktuellen Windowsen aussieht weiß ich nicht).
Ist nicht wirklich ne Option für mich (ist halt Einstellungssache ;) ). Problem ist speziell bei meinem Kernel, das er erst zur Laufzeit gelinkt wird und das würde heißen das ich nicht nur meinen Kernel 2mal bräcuhte sondern auch die "Module" die mindestens für den Kernel nötig sind (bei mir z.b. CPU, FPU, IRQ, PMM, Scheduler, Syscall und Timer) Das wird dann schon ein wenig umständlich.

Zitat von: erik
Echt, kann der K5 nicht so schnell Funktionen aufrufen?
Wenn sie erstmal im Cache liegen dann bestimmt, aber ich kann nicht immer davon ausgehen, das alles im Cache liegt.

Zitat von: erik
Gerade Code der von vielen Stellen benötigt wird profitiert oft nicht vom inlinen sondern davon nur ein mal da zu sein und auch nur ein mal im Cache zu liegen, es sei den der zu inlinende Code ist kleiner als der Funktionsaufruf selber (also bei x86 kleiner als 5 Byte).
Rein von der Anzahl an Instruktionen ist der Code Single-CPU Code mit Funktionsaufruf und SMP-Code ohne, aber mit best Case (was auf Single-CPU Systemem ja immer zutrifft) gleich "lang". Was du sparst (was aber auf älteren System nicht wirklich schlimm zu sein scheint) ist der gelockte Speicherzugriff. Am teuersten ist das CLI auf den älteren CPUs. Ich habe meinen Code auch mal auf meinem Q9550 gebencht und der ist selbst auf Taktebene wesentlich schneller als der K5 (was ich vorallem auf das CLI zurückführe).

Zitat von: erik
Was hat damit eigentlich die Integerleistung zu tun?
Naja, alles was nicht FPU ist, ist halt Integerleistung. Ich hätte vielleicht sagen sollen das er ein verdammt gutes Verhältnis von Instruktionen pro Takt hat. Taktbereinigt zieht er halt auch viele spätere Generationen ab ;) (bei der Integerleistung, nicht bei der FPU).

Zitat von: erik
Wenn der momentane Lock-Besitzer diesen nur kurz benötigt aber dummerweise gerade in diesem kurzen Code-Abschnitt vom Scheduller unterbrochen wurde dann ist das eben blöd und kostet etwas Performance. Aber deswegen noch mehr Performance für nen aufwändigen OS-Kernel-Service zu beanspruchen (der meist gar nicht nötig wäre) ist IMHO auch nicht zielführend. Einen passenden Service vom OS-Kernel zu nutzen lohnt IMHO erst wenn der Lock/Semaphore/sonstwas für längere Zeit beansprucht wird, ein wartender Thread also einige male vom Scheduller aufgerufen würde.
Das kommt auf die Schedulingstrategie an und was bei Sleep(0) wirklich passiert. Bei meinem OS würde das mit dem Abgeben (ich habe da nen extra Syscall für, nutze also kein Sleep(0)) funktionieren, zumindestens noch. Wenn ich meinen Scheduler mal ändern sollte (was sehr wahrscheinlich ist), dann sieht es schon wieder ganz anders aus.
826
Lowlevel-Coding / Re: spinlocks
« am: 09. April 2010, 06:19 »
Zitat von: Svenska
Als Programmierer nicht, aber wenn du das Locking zweimal implementierst, einmal als simples CLI/STI für Uniprozessor und einmal komplizierter für SMP, kannst du dynamisch umschalten. Ist halt nur noch ein Abstraktionsgrad mehr, ob sich das lohnt, kann ich nicht einschätzen.
Das könnte ich machen, wenn ich den Code nicht inlinen würde ;)

Ich habe es auch mal gebenchmarkt (auf nem K5-100) und was ich durch nur die Ints ausmachen sparen würde, würde durch den Funktionsaufruf wieder drauf gehen (Prob ist nur der K5 hat natürlich ne verdammt gute Integerleistung).

Zitat von: Svenska
Wenn der Kernel läuft, sollte er wissen, auf wievielen CPUs er läuft.
Weiß meiner auch ;)
827
Lowlevel-Coding / Re: spinlocks
« am: 09. April 2010, 00:32 »
Zitat von: erik
Im User-Mode ist es auf einem Single-CPU-System IMHO Zeitverschwendung mehrmals hintereinander zu versuchen einen Lock zu bekommen, ein Versuch pro Zeitscheibe sollte reichen.
Du solltest vielleicht mal definieren was du unter einer Zeitscheibe verstehst. Also ich denke mal du meinst die Zeit die ein Thread vom Scheduler bekommt. So wie du es schilderst verstehe ich darunter das du nur einmal versuchen willst den Lock zu bekommen und dann abgibts, aber dann nimm bitte eine Semaphore! Denn wenn du beim nächsten mal wieder versuchst den Lock zu bekommen und ihn wieder nicht bekommst, kostet das nur unnötig Performance (TLB und so´n Zeug).

Ich kann so langsam nachvollziehen wieso du uncacheable besser findest. Ich habe gerade ne News auf Golem gelesen und fand die Idee ganz gut (ein oder mehrere Kerne nur für die Speicherverwaltung). Wenn man die Idee jetzt mal weiter denkt, dann macht es sich ganz gut, immer einen Kern für eine bestimme Aufgabe zu nehmen (nur was das OS so zur Verfügung stellt) und alle anderen Kerne können von den Programmen genutzt werden. Das sollte das Cacheproblem doch wesentlich verringern.

edit::

Zitat von: erik
Auf nem Single-CPU-System braucht man im Kernel keine Locks da reicht IRQs ausschalten auch. Im User-Mode ist es auf einem Single-CPU-System IMHO Zeitverschwendung mehrmals hintereinander zu versuchen einen Lock zu bekommen, ...
Mein Kernel läuft dummerweise auf Single und Multi-CPU Systemen. Auch kannst du als Programmierer ja nicht wissen ob dein Spinlockcode jetzt auf nem Single-CPU System läuft oder nicht.
828
Lowlevel-Coding / Re: spinlocks
« am: 07. April 2010, 22:58 »
Zitat von: erik
Die ersten beiden Varianten sind auf einem Single-CPU-System eher nicht geeignet.
Warum? Auf nem Single-CPU-System bekommst du ne Spinlock immer (ich gehe jetzt mal frech vom Kernel aus)! Denn wenn du schon ne Spinlock nutzt dann bitte auch die IRQs ausmachen. Wenn du das ganze für den Userspace betrachtest, dann würde ich es so lösen, das du ne bestimmte Anzahl an Versuchen unternimmst und dann an den Scheduler abgibts und beim nächsten Mal geht es wieder von vorne los.

Zitat von: erik
Was dann wieder pure Speicherverschwendung ist, außerdem weißt Du doch nicht zur Link-Zeit die Cache-Line-Größe des Zielrechners.
Also was ich so an Code vom Intel-Compiler gesehen haben, macht der sich um Speicherverschwendung nicht wirklich ne Rübe ;)
Intel scheint in seinem Optimierungsguide sowieso davon auszugehen, das du alles immer nur auf einer bestimmten CPU laufen lässt und andere sind egal. (gut für Compilerprogrammierer sind die Infos schon wertvoll)

Du hattest mal was davon geschrieben, das AMD bei ihren aktuellen CPUs einen Teil des Level3 Caches dafür benutzt um die ganze Cachekohärenzprotokollsache zu minimieren. Dann frage ich mich schon was Intel mit ihren neuen Server-CPUs und bis zu 24MB Level3 Cache wollen, weil da müsste ja dann die Hölle los sein ;) Zumal diese CPUs gerade für viele Sockel-Mainboard ausgelegt sind.
829
Lowlevel-Coding / Re: spinlocks
« am: 07. April 2010, 09:26 »
Zitat von: erik
Es gibt keinen BUS mehr in einem modernen Multi-CPU-System! Oder meinst Du die Verbindung zwischen der CPU und den (eventuell mehreren) lokalen Speichermodulen? Das ist das einzigste was man noch als BUS bezeichnen könnte.
Ja, daran dachte ich. Das Problem mit dem Cachekohärenzprotokoll tritt ja "nur" auf, wenn der Lock freigegeben wurde ("nur" deshalb, weil dann ja die Cacheline mehrmals ungültig gemacht wird). Es kommt halt drauf an, wie lange man den Lock hat, bei 3-10 Assemblerinstruktionen kann es dann schon sein, das deine Variante vielleicht schneller ist, aber ich würde zumindest ne "pause" mit einsetzen. Denn gerade die neuen Core i-sowieso CPUs sollten wieder davon profitieren!

Zitat von: erik
Der Optimirungsguide ist sicher über mehrere CPU-Generationen gewachsen, ich glaube nicht das dort noch alles Up-To-Date ist.
Ach der wird eigentlich laufend aktualisiert und genau da ist ja das Problem. Am Anfang schreiben sie, du sollst deine Loops so weit es geht unrollen und später schreiben sie, dass auf der Core Architektur das Loop unrolling nicht übertrieben werden sollte, weil die CPUs damit nicht mehr zurecht kommen ;) Aber zum Thema Spinlocks sagen sie auch, das es im Cache liegen soll, aber eine Lock alleine in einer Cacheline! Naja ich denke wir einigen uns darauf, das jeder das nimmt was er für besser hält ;)

Zitat von: erik
Wenn Deine Queue eine einfach verkettete Liste ist in der es in jedem Element einen "Next"-Pointer gibt dann kannst Du ein neues Element bauen (dort ist der Next-Pointer noch NULL) und dieses mit einem cmpxchg auf den Next-Pointer des letzten Listenelement anhängen (dort ist der Next-Pointer auch noch NULL). Das heißt der cmpxchg prüft ob der Next-Pointer im letzten Element noch NULL war und schreibt dann den Pointer für Dein neues Element an diese Stelle und wenn das fehlschlägt (weil jemand anderes schneller war) dann probierst Du das bei dem neuen letzten Element eben erneut. Du versucht also nicht ständig den selben Next-Pointer zu bearbeiten sondern gehst nach jedem Fehlschlag weiter. Doof wäre nur wenn Deine Message-Queue in dieser Zeit leer werden sollte.
Genauso sieht meine Liste aus, aber wie ich es mir dachte, wird es wohl Probleme beim Löschen geben.
Ich habe ne doppelt verkettete Liste und speichere nur den Head ab. Der Pointer auf das letzte Element ist dann der Vorgänger vom Head, d.h. beim Löschen müsste ich 2 Pointer ändern und beim Einfügen müsste ich auch immer gucken ob der Head noch da ist. Also bleibe ich lieber bei ner Spinlock (ist ja auch nicht wirklich viel was hier passiert).

Ich bin mir nicht sicher ob es hierher passt, aber egal.
Was hälst du denn von Read-Write-Locks? Ich muss heute direkt mal nachgucken, ob es sowas auch als Semaphore gibt, habe ich nämlich noch nichts von gehört.
830
Lowlevel-Coding / Re: spinlocks
« am: 06. April 2010, 19:50 »
Zitat von: erik
Der Befehl wurde mit den ersten Pentiums eingeführt und anschließend in später erschienene 486 "zurückportiert", ebenso wie der cpuid-Befehl und der SMI-Mode (den gab es wimre aber nur in speziellen Embedded-Versionen des 486). Es gab sogar 386-Versionen mit cpuid.
Mit solchen Infos ist Intel immer sehr zurückhaltend :(

Zitat von: erik
Aber anstatt über die Auswirkungen von langsamen und schnellen Lock-Mechanismen zu diskutieren (ich gehe davon aus das dort keine nennenswerten Meinungsunterschiede bestehen) wüste ich gerne warum Du denkst (diesen Eindruck habe ich zumindest) das die gecachte Variante schneller ist. Du darfst dafür gerne das Basis-Szenario von http://lowlevel.brainsware.org/forum/index.php?topic=2468.msg27736#msg27736  verwenden.
Naja, wie es schon in dem alten Dokument stand, du liest beim Testen erstmal nur aus deinem Cache, der Memorybus wird "geschont", damit hat der Thread der den Lock hat effektiv mehr Bandbreite zur Verfügung. Wie das dann mit dem Cachekohärenzprotokolloverhead ist weiß ich nicht. Genauso haben alle anderen Threads mehr Bandbreite wenn du mit deinem Lock den Memorybus nicht zusätzlich belastest. Außerdem gehe ich mal davon aus, das Intel dieses Vefahren nicht im Optimierungsguide angeben würde, wenn es effektiv nichts bringt.

Ich wäre ja an einer Variante wie man eine Queue implementiert mit Hilfe des cmpxchg Befehls! Dann könnte ich vielleicht die Locks für meine Messagequeue entschärfen.
831
Lowlevel-Coding / Re: spinlocks
« am: 06. April 2010, 15:13 »
Zitat von: erik
Außerdem kann man einige Beispiele davon etwas entschärfen wenn z.B. bei einem Virtual-File-System nicht ein globaler Lock sondern für jede Datei ein eigener Lock vorhanden ist, das alle CPUs gleichzeitig auf die selbe Datei wollen ist nochmal deutlich unwahrscheinlicher. Nebst dessen das alle Deine Beispiele von der Sache her recht aufwendige Dinge sind wo selbst ein etwas langsamerer Lock-Vorgang nicht wirklich ins Gewicht fällt.
Mit dem Festplattentreiber hast du mich falsch verstanden! Mir ist schon klar, dass es unwahrscheinlich ist, das viele CPUs auf ein und die selbe Datei zugreifen, aber bei einer Festplatte im System ist es gar nicht so unwahrscheinlich das alle mit einmal auf diese eine Festplatte zugreifen wollen (wie gesagt, muss sowieso serialisiert werden, läuft also auf ne Queue hinaus, was nicht lange dauern sollte).
Wo ich dir allerdings wiederspreche (und da muss ich sogar mal meinem Prof zustimmen ;) ), dass ein langsamer Lock-Vorgang auf solche Prozesse keinen Einfluss hat, der ins Gewicht fällt. Denn du betrachtest jetzt immer nur den Lock-Vorgang an sich, aber was ist mit dem restlichem System? Das würde durchaus davon profitieren wenn das mit den Locks so schnell und effizient wie möglich passiert! Weil ja auch lesen/schreiben im uncacheable Memory Last auf dem Memorybus erzeugt und somit effektiv andere ausbremst.

Zitat von: erik
Wenn man sehr viele CPUs bedienen möchte muss man auch die SW-Architektur etwas anpassen. Da sollte es z.B. nicht unbedingt einen zentralen "App-Server" (was auch immer Du damit meinst) geben sondern dann sollten dessen aufgaben etwas dezentralisiert werden. Da gibt es natürlich auch gewisse Grenzen aber so weit es möglich ist sollte man eben schon die Gegebenheiten berücksichtigen.
Mit App-Server (kannst du meinet wegen auch Gui-Server nennen) meine ich etwas was sich um die grafische Darstellung auf dem Bildschirm kümmert und wenn man halt nen großen Monitor hat und 10 Anwendungen laufen gleichzeitig und wollen was neu Zeichnen (und müssen dafür ne Message an den App-Server schicken (ich habe mir schon was einfallen lassen, damit genau das nicht passiert)) dann kann es leider doch passieren das alle CPUs in dem Moment was in die gleiche Queue packen wollen.

Ich habe dein Link gelesen und so wie es da steht, müsste das alles atomisch in einem Zug ablaufen. Aber ich entsinne mich auch irgendwo gelesen zu haben, dass das so nicht funktioniert, sondern das du dann explizit "lock cmpxchg" nehmen musstest (da gibts nen Thread auf sandpile.org dazu). Auch gibt es den Befehl schon seit dem 486 (steht zumindest jedes Mal da, 486+).
832
Lowlevel-Coding / Re: spinlocks
« am: 06. April 2010, 14:25 »
Nur mal so eben dazwischen geschoben (lese noch den Link) bevor ich es wieder vergesse ;)

Du meintest ja das es ein SW Designfehler wäre, wenn z.b. 10 oder mehr CPUs gleichzeitig versuchen würde einen Lock zu bekommen. Ich nenne jetzt mal nur Bsp. die mir so spontan einfallen:

- Festplattentreiber (alle Threads die auf den CPUs laufen wollen gleichzeitig auf die Festplatte schreiben/lesen)
- Physikalischer Speichermanager (alle CPUs brauchen gleichzeit eine neue Page)
- Messagesystem (alle CPUs senden zur gleichen Zeit eine Message an z.B. den App-Server und die müssen ja der Reihe nach in die Queue gepackt werden)
- Netzwerk (alle CPUs senden/empfangen gleichzeitig)
- usw.

Also es gibt genug Fälle wo es passieren kann (auch wenn es mit steigender CPU Zahl immer unwahrscheinlicher wird) das alle CPUs,  dummerweise, die gleiche Lock haben wollen.

Meine Bsp. lassen sich alle (bei nem Mikrokernel) auf das Messagesystem reduzieren (also das nur der Lock für den Port (oder was vergleichbares) bekommen werden muss)! Bei nem monolithen Kernel, wo vieles im Kernel ist, wird es dann halt so laufen wie oben genannt.

Edit::

Schau dir mal den Optimierungsguide von Intel an, da steht auch was über Spinlocks (ansonsten ist es schön zu sehen, wie sie sich teils selbst wiedersprechen ;) ).
833
Lowlevel-Coding / Re: spinlocks
« am: 05. April 2010, 21:56 »
Zitat von: erik
Aber deswegen konsequent die Bitte zu ignorieren das zuvor geschriebene richtig zu lesen empfinde ich persönlich als unhöflich.
Sorry, wenn das so rüber gekommen ist, aber ich habe den Thread nun schon mehrmals gelesen, in der Hoffnung das ich wenn ich zu ende gelesen habe, nichts wieder vergessen habe ;)

Zitat von: erik
Ist eigentlich schon mal das Alter dieses Dokuments aufgefallen?
Ja, aber ich denke an der Grundaussage hat sich nichts geändert, außer das die Cachekohärenz besser (schneller) sein sollte.

Zitat von: erik
Nein! Wenn der ungelockte Lesevorgang den Status "Frei" ergeben hat muss dann für die echte atomische Operation noch mal gelesen werden und zwar gelockt. Es ist extrem wichtig das zwischen dem Lesen und dem zugehörigen Schreiben kein anderer gelockter Zugriff auf die Lock-Variable passieren kann, weder Lesen noch Schreiben (warum das so ist habe ich in diesem Thread nun schon mehrfach geschrieben, daher auch die Bitte diesen Thread richtig zu lesen). Bereits mit dem gelockten Lese-Zugriff geht bei den anderen CPUs die Cache-Line verloren. Ein Zustand "gültig aber vorübergehend gesperrt" habe ich bei Cache-Lines noch nicht gesehen (was nicht heißen soll das sowas unmöglich ist).
Ich meinte ja mit dem Schreiben des neuen Wertes den xchg Befehl, hätte ich vielleicht deutlicher sagen sollen!

Zitat von: erik
Deswegen hab ich ja vorgeschlagen das Lock-Variablen immer in non-cachable Speicher liegen sollen. Damit entfällt das ganze Cache-Kohärenz-Protokoll. Wenn 2 CPUs per xchg versuchen das Lock zu bekommen so sind dafür ganz exakt 4 Messages erforderlich. Wenn beide CPUs mit der ungelockten Vor-Schleife das Frei erkennen und dann beide einen gelockten Zugriffsversuch unternehmen dürfte das Cache-Kohärenz-Protokoll ein vielfaches des Traffics erzeugen. Das Cache-Kohärenz-Protokoll erzeugt umso mehr Traffic je mehr CPUs beteiligt sind, so das ich persönlich die konsequente Vermeidung des ganzen Aufwands bevorzuge. Für Locks die für mehr als 10 Assemblerbefehle behalten werden sollen ist natürlich der pause-Befehl ein gute Methode den Traffic zu reduzieren, aber auf Kosten der Reaktionsgeschwindigkeit. Für noch längere Locking-Zeiten sollte man dann OS-Verwaltete Locks benutzen, das OS kann den wartenden Thread schlafen legen (bei mehren wartenden Threads kommen die in eine geordnete Queue/FIFO) und diesen aufwecken sobald der Lock freigegeben wurde. Damit hat man eine fertretbar kurze Reaktionszeit und erzeugt keine unnütze Last egal wie lange der Lock belegt bleibt. Es gilt also weiterhin das man sich zuerst die Benutzung des Locks genau ansehen muss um dann die richtige Methode zu wählen.
Gut, dann würde mich aber interessieren wie du es bewerkstelligen willst, das die Locks in einem uncacheable Bereich liegen. Denn du musst dann ne ganze Page als uncacheable markieren und das für einen Lock? Zumal ich der Meinung bin, Caches gibt es nicht umsonst. Der Zugriff auf den RAM ist wirklich langsam! Ich glaube auch nicht das der Traffic für das Cachekohärenzprotokoll so schlimm ist, das er mehr braucht als nen Speicherzugriff (sollte max so lange dauern wie ein Cachezugriff)!
Genauso wird oft gesagt, das nicht 2 Locks in einer Cacheline liegen soll, aber wie soll man das bewerkstelligen?! Ich meine, die Cachelines sind nicht immer gleich groß und dann sollte auch nichts anderes in der Cacheline liegen und ich rechne nur mal hoch, in meinem Kernel sollten so ca. 50 allgemeine Locks und dann noch Locks für bestimmte Datenstrukturen (Threads, Tasks, Ports, ...) und dann jedes mal 64byte (als Bsp.)? Finde ich ein wenig arg viel!
Was du mit der Queue ansprichst ist dann schon eher ne Semaphore und zum Thema wann eine Spinlock und wann eine Semaphore einsetzen, habe ich mal ein paar Benchmarks gemacht. Ich habe dann für mich festgelegt, alles was kürzer als 2-3 Contextwechsel ist, kann man ruhig mit ner Spinlock schützen und das ist nicht gerade kurz (du darfst da dann auch noch die TLB Flushes mit einrechnen)!

Zitat von: erik
Dann müsste dieser Befehl aber 2 Lesezugriffe aussenden, den ersten ungelockt zum vorher nachschauen und (falls überhaupt erforderlich) den zweiten dann gelockt für die atomische Operation. Das ist natürlich möglich, deckt sich aber nicht mit der von Intel beschriebenen Funktionsweise.
Ich würde sagen das es genau so funktioniert, im Endeffekt ist es ein cmp und ein xchg Befehl kombiniert in einem Opcode, damit der Decoder weniger machen muss (und es auch einfacher für wird). Anders macht es auch kein Sinn für mich.
834
Lowlevel-Coding / Re: spinlocks
« am: 03. April 2010, 09:09 »
Ich muss mich mal korrigieren, ich meinte die ganze Zeit die Test-And-Test-And-Set Variante!

Da ich gerade (und heute allgemein) nicht alzu viel Zeit habe, will ich nur mal kurz aus nem Paper zitieren und den Link zu dem Paper posten:

Zitat
While the lock is busy, spinning is done in the cache without consuming bus or networks cycles. When the lock is released, each copy is updated to the new value (distributed-write) or invalidated, causing a cache read miss that obtains the new value. The waiting processor sees the change in state and performs a test-and-set; if someone acquired the lock in the interim, the processor can resume spinning in its cache.

Der Link dafür ist: http://www.cs.washington.edu/homes/tom/pubs/spinlock.pdf

Das ergibt erstaunlicher Weise sogar viel Sinn ;) Zusammengefasst heißt das, dass du wenn du nur liest und guckst ob der Wert sich verändert hat, das ganze in deinem Cache machen kannst und so den Memorybus nicht belastest, erst wenn der Lock freigegeben wurde, wird der neue Wert geschrieben und die CPU muss ihn sich holen. D.h. das warten auf den Lock findet immer im Cache statt! Aber wenn du jedesmal schreibst müssen die anderen CPUs ihre Caches invalidieren und es muss in den Speicher zurück geschrieben werden (was heißt das du mit jeder xchg Instruktion auf den Memorybus zugreifst und die Caches der anderen CPUs invalidierst). Ich weiß nun nicht in wie weit die Caches moderner CPUs optimiert sind, das sie erkennen das der neue geschriebene Wert dem alten entspricht und somit keine Änderung stattgefunden hat.

edit::

Jetzt wird mir auch so langsam klar was es mit dem cmpxchg auf sich hat. Im Endeffekt dürfte das genau diese Test-And-Test-And-Set Variante in einem Befehl (ohne schleife halt) sein.
835
Lowlevel-Coding / Re: spinlocks
« am: 02. April 2010, 23:14 »
Zitat von: erik
Da irrst Du seit deutlich über 5 Jahren. Der Hyper-Transport von AMD und QPI von Intel haben mehr mit Ethernet gemeinsam als mit nem klassischen CPU-Bus. Die sind nur stärker vernetzt als Ethernet, also es gibt oft viele Wege zum Ziel was man bei Ethernet eher vermeidet.
Ich weis nicht was du mit NUMA meinst, aber ich verstehe darunter das jede CPU ihren eigenen Speicher hat auf den Sie schnell zugreifen kann. Will sie auf Speicher zugreifen der nicht in ihrer "Nähe" liegt läuft das über den Speicherkontroller einer anderen CPU (also der CPU wo der Speicher in der "Nähe" liegt) und dieses Konzept haben wir noch nicht im Desktopbereich. Das wird bisher ausschließlich bei mehr Sockel Systemen angewand.

Zitat von: erik
Was genau meist Du mit "Test-And-Set Variante"? Den BTS-Befehl oder irgendeine Vorgehensweise?
Also damit meine ich das man erst testet ob der Lock frei ist und erst dann versucht ihn zu bekommen. Das testen läuft dabei ohne lock Präfix ab und belastet den Memorybus also nicht so sehr wie einen Test mit lock Präfix. Wie gesagt, man nutzt diese Variante um die Zugriffe mit lock Präfix so weit es geht zu minimieren! Ich schreib einfach mal pseudo Code:
VERSUCH:
ist der Lock frei?
ja -> gehe zu Label FREI;;
nein -> mache eine Pause und gehe dann wieder zu Label VERSUCH;;
FREI:
versuche den Lock zu bekommen;;
habe ich den Lock bekommen?
ja -> verlasse diesen Codeblock;;
nein -> gehe wieder zum Label VERSUCH;;
Du musst dir halt einfach vorstellen, der Thread der den Lock gerade hat, will halt oft aus dem Speicher lesen und dann auch mal schreiben. Ist der Memorybus gelockt, kann er beides in dem Moment nicht machen und muss warten -> d.h. er braucht effektiv mehr Zeit als er brauchen würde, wenn der Memorybus nicht gelockt ist. Das ist aber nicht die einzige Auswirkung, denn damit hälst du auch alle anderen CPUs auf, die gerade auf den Speicher zugreifen wollen, d.h. im Endeffekt "verlangsamst" du das ganze System mit dem ständigen locken des Memorybuses!

Das Problem mit dem genauen erklären ist, das ich mir leider auch nicht Vorstellen kann wie das im Speicher alles parallel ablaufen soll, aber ich kann mir folgendes vorstellen:

Also der xchg Befehl liest erstmal den aktuellen Wert aus dem Speicher (das braucht schonmal seine Zeit), hat er das getan, schreibt er den neuen Wert in den Speicher (was wieder seine Zeit braucht) und genau dazwischen kann es passieren das eine andere CPU (Thread) schneller war und schon nen anderen Wert geschrieben hat (weil das Lesen und Schreiben halt doch ne Weile dauert) und damit würden dann 2 CPUs den Lock haben. Ich versuche das ganze mal noch mehr zu veranschaulichen:

CPU1 liest Wert -> CPU2 liest Wert -> CPU1 schreibt Wert -> CPU2 schreibt Wert

Und jetzt mal mit Lock:

CPU1 sendet LOCK -> CPU1 liest Wert -> CPU1 schreibt Wert -> CPU1 löscht LOCK -> erst jetzt können wieder andere CPUs auf den Speicher zugreifen

Soweit wurde es ja auch beschrieben, was aber bisher nicht wirklich genannt wurde ist, das in der Zeit wo der LOCK aufrecht erhalten wurden viele weitere Speicherzugriffe hätten standfinden können! Diese unterbindest du in dem Moment mit dem LOCK und bremst das System aus.

Das Problem ist im Endeffekt das der RAM, auch wenn er wesentlich schneller als z.B. die HDD ist, immernoch ziemlich langsam ist. Ansonsten bräuchte man ja auch nicht die schnellen Level1 und Level2 Caches (oder jetzt sogar Level3 Caches). In der Zeit wo die CPU etwas aus dem RAM läd hätte sie auch schon viele andere Dinge machen können. Was ich jetzt nicht weiß, wie schnell die Cache Synchronisierung läuft (unter den einzelnen CPUs), dürfte aber auch mehr Zeit in Anspruch nehmen als eine Instruction auszuführen.

Was ich aber ehrlich zugeben muss, das mit dem Cachetrashing habe ich noch nicht verstanden, also was ihr damit meint und warum das schlecht ist ;)
836
Lowlevel-Coding / Re: spinlocks
« am: 02. April 2010, 21:07 »
Zitat von: erik
Bist Du Dir sicher das Du richtig geschaut hast?
Das macht dann aber keinen besonders vertrauenerweckenden Eindruck, ich hätte den Linux-Leuten mehr zugetraut. Zwischen dem gelocktem sub und dem anschließendem Nachprüfen (ein normaler Speicherzugriff nehme ich an) könnte ja noch ein anderer Thread (oder gar der Scheduller-IRQ) dazwischen funken.
Da sollte man dann wissen was die CPU Befehle so alles bewirken. Denn wenn du ne mathematische Operation durchfürhst werden auch immer die Flags aktualisiert und da kannst du dann sehen ob der neue Wert >= 0 ist.

Zitat von: erik
Den Hinnweis auf http://lowlevel.brainsware.org/forum/index.php?topic=2468.msg27736#msg27736  hatte ich schon gegeben? Ich will echt nicht behaupten das der Schreiber des Beitrags dort die absolute Wahrheit geschrieben hat aber es könnte schon recht dicht dran sein.
Ich habe das ganze jetzt nur mal überflogen. Problem ist er geht von NUMA aus, was wir aufm Desktop nunmal noch nicht haben. Dann macht er es sich mit dem Lock ganz schön einfach . Der Punkt ist doch das viele Sachen bei nem Multi CPU System gleichzeitig ablaufen und halt nicht seriell.

Um es mal ganz logisch zu erklären, wieso musst du denn das lock Präfix verwenden? Weil es halt passieren kann das 2 CPUs den selben Wert ändern (gleichzeitig) und dann ja beide den Lock bekommen würden. Lockst du aber den Memorybus, werden alle Lese- und Schreiboperationen gestoppt bis der Memorybus nicht mehr gelockt ist (und das kann halt ziemlich "lange" dauern). Denn wenn es nicht sehr kostspielig wäre, wieso sind dann nicht alle Speicherzugriffe so wie mit dem lock Präfix.

Ich habe leider jetzt auch keine Quelle zur Hand wo man mal ganz genau beschrieben sieht was bei so nem lock Präfix nun passiert.

Aber man wird nicht umsonst diese Test-And-Set Variante entwickelt haben, damit das lock Präfix nicht alzu häufig auf den Memorybus angewendet wird.
837
Lowlevel-Coding / Re: spinlocks
« am: 02. April 2010, 10:30 »
Mir ist noch was eingefallen bezüglich Semaphore und "lock add/sub", Linux implementiert so seine Userspace Semaphore (oder Futex heißt das da glaub ich). Das läuft dann ungefähr so wie ich es beschrieben habe, nur das er halt nen "lock sub [eax],1" macht und wenn es kleiner 0 ist macht er nen Syscall in den Kernel und packt sich in die Warteschleife.

Zitat von: bluecode
Eine einfach verkettete Liste lässt sich damit prima ohne Locks implementieren.
Ich weiß jetzt nicht genau wie das funktionieren würde, aber ich könnte mir vorstellen dass das nicht einfach ist und das es bei bestimmten Fällen mit einer Spinlock schneller gehen würde. Ich denke da speziell ans Entfernen.

Edit::

Noch mal zu dem lock Präfix. Ich hatte irgendwo mal gelesen, das dadurch der Memorybus für bis zu 100Takte für Lesen und Schreiben gesperrt ist und dadurch kann es sehr wohl passieren das ein anderer Thread der den Spinlock hat schneller fertig wird, wenn man den Memorybus nicht ständig lockt!
838
Lowlevel-Coding / Re: spinlocks
« am: 01. April 2010, 22:35 »
Zitat von: erik
Wenn man ne richtige Datenstruktur schützt muss man immer zusätzlich einen Lock oder eine Semaphore vorsehen.
Der Meinung bin ich ja auch, nur als ich mich über den cmpxchg Befehl informieren wollte, wurde mir mal so ein Bsp. an den Kopf geknallt, das es doch besser ist diesen zu verwenden, wenn man nur eine Variable schützen möchte, als die Variable mit einem Lock zu schützen. Das leuchtet mir ja auch ein, aber warum sollte ich nur ne Variable ändern wollen die geschützt werden muss??

Zitat von: erik
Da bin ich mir nicht mehr sicher. Zuerst war ich von den ganzen PUSHs und POPs irritiert und dann von ":[output] "=q"(spinlock->flags)" (deshalb dachte ich das bei "testl $1,(%%eax)\n\t" irgendwas in Abhängigkeit von den Flags aus dem Struct gemacht wird). Nach jetzt noch mal einigen Minuten Konzentration bin ich (fast) geneigt Dir recht zu geben.
Falls du dich besser fühlst, ich hasse die AT&T Syntax auch, aber GAS/GCC will du nunmal haben (ja ich weiß da gibts so´n switch der zur Intelsyntax wechselt).

Also die ganzen Push´s und Pop´s sind für die Flags, wenn du im Kernel nen Spinlock nutzt und die Ints ausschaltest und dann bei der Freigabe wieder anmachst, kann es sein das du das gar nicht "durftest", das sie bereits aus waren bevor du versucht hast den Lock zu bekommen.

Also als erstes speichere ich die Flags "pushfl", dann deaktiviere ich die Ints und dann gehe ich in eine Schleife wo ich gucke ob der Lock frei ist, ist das nicht der Fall aktiviere ich die ints wieder (obwohl das so nicht stimmt, ich setzte sie auf den Wert der vorher drin stand, deswegen auch "popfl; pushfl") und mache ne "pause" ;) Ist der Lock frei, wird versucht über "lock bts ..." den Lock zu bekommen, hat das nicht geklappt, geht der ganze Spaß wieder von vorne los.

":[output] "=q"(spinlock->flags)" heißt dass das Register "[output]" (anstelle von %0 oder %1, weil ich es lesbarer finde) eins von allen 6 (deswegen auch "q") sein kann.
In EAX steht die Adresse des Locks und mit "testl $1,(%%eax)" teste ich ob der Lock frei ist oder nicht.

Zitat von: erik
Ganz unabhängig davon finde ich so eine vorgelagerte Schleife irgendwie sinnlos, klar wird beim warten möglicherweise etwas weniger Traffic auf dem CPU-Netzwerk erzeugt aber deshalb gibt doch der momentane Besitzer den Lock nicht schneller frei, oder irre ich da auch schon wieder?
Ist erstmal richig, aber ... Das mit dem Lock kommt noch und durch die "pause" wird die CPU entlastet, verbraucht weniger Strom (bin mir net sicher ob das so stimmt), legt wirklich ne kleine Pause ein und belastet damit nicht den Speicherbus und falls du auf ner CPU mit Hyperthreading läufst, kann so der andere logische Kern die Einheiten besser nutzen (stell dir es wie ein ganz kurzes "hlt" vor).

Zitat von: erik
Gerade bei einer Semaphore musst Du wissen wie viele schon drin sind und da reicht ein einfacher add nicht, auch nicht mit lock-Präfix.
Da hast du natürlich recht, ich weiß auch nicht woran ich da dachte. Das einzige was mir spontan einfällt ist, wenn du das mit dem counter einer Semaphore machst (also das gelockete add) dann könntest du etwas sowas machen:
lock sub [eax],1
jae .locked
;versuche den spinlock für den Code zu bekommen
;füge den aktuellen thread der Queue hinzu und warte bis du aufgeweckt wirst
.locked:
;du darfst weiter machen
Ich bin mir jetzt aber nicht sicher ob das auch wirklich funktionieren würde. Was ich damit meine ist, das du nur versuchen musst den Spinlock der Semaphore zu bekommen, wenn du sowieso warten musst, ansonsten kannst du ja auch ganz einfach weiter machen. Beim Release dann das ganze umgekehrt, du machst nen gelocktes add und wenn es kleiner als 0 (also negativ) ist, versuchst du den Spinlock zu bekommen und weckst den nächsten Thread auf. So kann es im optimalen Fall passieren das du den Spinlock gar nicht bekommen musst. Wäre also nochmal nen Stück schneller/besser. Aber wie gesagt, ist jetzt ne spontane Idee (um mein Bsp. nicht als sinnlos dastehen zu lassen ;) ) und müsste halt ausprobiert werden.

Zitat von: erik
Was genau meinst Du mit diesem Satz? Für Deinen BTS-Befehl benutzt Du doch auch das LOCK-Präfix.
Der Punkt ist, das ich den Speicherbus nur locke, wenn ich vermute das der Lock frei ist. Du hingegen lockst den immer, bei jedem Zugriff. Soweit wie ich es verstanden habe, geht es darum, das alle anderen Zugriffe warten müssen bis du fertig bist und da man ja alles möglichst parallel macht bremst das halt tierisch die Performance. Weil ansonsten ja auch parallel aus dem Speicher gelesen werden kann, aber wenn du den Bus lockst ist das nicht mehr möglich. Ich hoffe ich war einigmaßen verständlich!

Ich meine die ganze Situation ist sowieso nur für SMP Systeme interessant, weil du auf nem 1 CPU System den Lock immer bekommst. Auf nem SMP System arbeiten alle CPUs gleichzeitig und greifen auch gleichzeitig auf den Speicher zu (und jetzt kommt der Buslock), es sei denn du lockst denn Bus, dann müssen alle auf dich warten.
839
Lowlevel-Coding / Re: spinlocks
« am: 01. April 2010, 21:33 »
Zitat von: bluecode
Genau das macht doch sein Code, oder sehe ich das falsch?
Richtig ;)

@erik

Ich meine Code wie diesen:
mov eax,1
mov edx,addrLock
.loop:
xchg [edx],eax
test eax,eax
jnz .loop
Dieser Code lockt den Bus bei jedem Schleifendurchlauf (und dürfte so ungefähr dem entsprechen was ich irgendwo am Anfang mal gelesen habe). Das kann ganz schnell zu einem Performace Problem werden. Deswegen ja auch die Test-And-Set Variante.

@all

Was mich mal interessieren würde, wozu ist der cmpxchg Befehl eigentlich gut (oder besser)? Ich meine mich würde mal ein Bsp. interessieren wo man durch ihn wirklich einen Gewinn hat. Als Bsp. wird oft genannt, wenn man eine Variable ändern möchte und diese Variable dann durch einen Lock geschützt werden müsste. Das sehe ich auch soweit ein, es macht mehr Sinn diesen  Befehl (oder halt cmpxchg8/16) einzusetzen, da man dann den Lock nicht benötigt. Was ich aber nicht weiß ist ein Bsp. wo ich einfach ne Variable ändern möchte. Mir fällt da spontan nur die Semaphore ein, aber da tut es auch ein "lock add [eax],1" oder "lock sub [eax],1", aber es muss ja auch einen Grund dafür geben das Intel die anderen beiden Befehle (8 und 16 von cmpxchg) eingeführt hat.

Wenn ich etwas mit einem Lock schütze dann ist das meistens ne Datenstruktur und da reicht es z.B. nicht wenn ich einfach nur z.b. die Wurzel eines Baumes ändere, wenn dann möchte ich den ganzen Baum irgendwie ändern (ist ein blödes Bsp.).
840
OS-Design / Re: Sysenter vs. Interrupts
« am: 01. April 2010, 19:53 »
Zitat von: XanClic
Ich weiß nicht, was das Problem mit 64-Bit-Werten ist. Ich kann doch eax und ebx nutzen? Ich persönlich würde das sowieso in einen Assemblerstub auslagern, der dann ja ebx sichern kann und am Ende ebx nach edx schiebt.
Da gebe ich dir recht. Im Moment habe ich meine Syscalls aber soweit optimiert das ich nicht in einen Stub zurückkehre sondern direkt an die Instruktion die nach dem Call kommt. Aber ich habe schon geplant dies zu ändern, da ich mir damit einige Fehler in den Kernel holen kann und das möchte ich vermeiden. Ich meine ganz konkret, das er als Rücksprung Adresse ne falsche angibt und ich dann ne Exception im Kernel bekomme, was weniger schön. Springe ich zu nem Stub zurück und führe dort dann ein ret aus, dann wird die Exception im User-Mode geworfen was weniger problematisch ist und ich könnte gleichzeitig die von die beschriebene Methode zur Rückgabe von 64bit Werten nutzen.

So aber wie würdet ihr das Problem mit dem User-Stack lösen. Ich meine das der Aufrufer ne falsche Adresse im ESP stehen hat oder er einfach sysenter ohne den vom Betriebssystem bereitgestellten Stub nutzt?
Seiten: 1 ... 40 41 [42] 43

Einloggen