Ja, Sie müssen mit einer Vorlaufzeit von ungefähr der Speicherlatenz vorausholen, damit es optimal ist. Ulrich Dreppers What Every Programmer Should Know About Memory spricht viel über Prefetching.
Dies zu erreichen wird für einen einzelnen Zugriff höchst nicht trivial sein. Zu früh und Ihre Daten werden möglicherweise vor den Insn, die Ihnen wichtig sind, geräumt. Zu spät und es könnte die Zugriffszeit etwas reduzieren. Das Tuning hängt von der Compiler-Version/-Optionen und von der Hardware ab, auf der Sie arbeiten. (Höhere Instruktionen pro Zyklus bedeutet, dass Sie früher im Voraus abrufen müssen. Höhere Speicherwartezeit bedeutet auch, dass Sie früher abrufen müssen).
Da Sie eine read-modify-write zu a
schreiben möchten, sollten Sie PREFETCHW
verwenden, wenn verfügbar. Die anderen Vorabrufbefehle werden nur zum Lesen vorgelesen, so dass der gelesene Teil eines RMW treffen könnte, aber ich denke, der Speicherabschnitt könnte durch die MOSI-Cache-Kohärenz verzögert werden, wodurch der Schreibbesitz der Cachezeile erhalten wird.
Wenn a
nicht atomar ist, können Sie auch einfach a
weit vor der Zeit laden und die Kopie in einem Register verwenden. Der Speicher zurück zum Global könnte in diesem Fall leicht verfehlen, was jedoch die Ausführung behindern könnte.
Sie werden es wahrscheinlich schwer haben, das mit einem Compiler zuverlässig zu tun, anstatt selbst zu schreiben. Jede der anderen Ideen erfordert auch die Überprüfung der Compiler-Ausgabe, um sicherzustellen, dass der Compiler das getan hat, was Sie hoffen.
Prefetch-Anweisungen müssen nicht unbedingt vorab eingelesen werden. Sie sind "Hinweise", die vermutlich ignoriert werden, wenn die Anzahl der ausstehenden Lasten in der Nähe von max ist (d. H. Fast unbelastete Puffer).
Eine weitere Option ist es zu laden (und nicht nur Prefetch) und dann mit einem CPUID
serialisiert. (Eine Last, die das Ergebnis wegwirft, ist wie ein Prefetch). Die Last müsste vor der Serialisierungsanweisung abschließen, und Anweisungen nach der Serialisierung insn können die Decodierung erst dann beginnen. Ich denke, dass ein Prefetch in den Ruhestand gehen kann, bevor die Daten ankommen, was normalerweise ein Vorteil ist, aber nicht in diesem Fall, in dem wir uns um eine Operation kümmern, die auf Kosten der Gesamtleistung geht.
Von insn ref Handbuch des Intel (siehe x86 Tag wiki) Eintrag für CPUID
:
Serialisierung Befehlsausführung garantiert, dass alle Änderungen auf Merker, Register und Speicher für frühere Anweisungen vor nächsten abgeschlossen sind Anweisung wird abgerufen und ausgeführt.
Ich denke, eine Sequenz wie dies ziemlich gut ist (aber immer noch garantiert nicht, alles in einem Präventiv Multi-Tasking-System):
add [mem], 0 # can't retire until the store completes, requiring that our core owns the cache line for writing
CPUID # later insns can't start until the prev add retires
add [mem], 2 # a += 2 Can't miss in cache unless an interrupt or the other hyper-thread evicts the cache line before this insn can execute
Hier verwenden wir add [mem], 0
als Schreib -prefetch, die sonst ein naher no-op ist. (Es ist ein nicht-atomare lesen-ändern-umschreiben). Ich bin mir nicht sicher, ob PREFETCHW
wirklich sicherstellen wird, dass die Cache-Zeile bereit ist, wenn Sie PREFETCHW
/CPUID
/add [mem], 2
tun. Das Insn wird mit bestellt. CPUID, aber das Handbuch sagt nicht, dass der Prefetch-Effekt bestellt wird.
Wenn a
volatile
ist, dann (void)a;
wird gcc oder Klirren erhalten, um eine Last insn zu emittieren. Ich nehme an, die meisten anderen Compiler (MSVC?) Sind gleich. Sie können wahrscheinlich (void) *(volatile something*)&a
verwenden, um einen Zeiger auf volatile
zu dereferenzieren und eine Last von der Adresse a
zu erzwingen.
Um Garantie, dass ein Speicherzugriff im Cache getroffen wird, dann würden Sie müssen in Echtzeit Priorität einen Kern festgesteckt zu laufen, die keine Interrupts empfangen. Abhängig vom Betriebssystem ist der Timer-Interrupt-Handler wahrscheinlich so leicht, dass die Wahrscheinlichkeit, dass Daten aus dem Cache entfernt werden, gering genug ist.
Wenn Ihr Prozess zwischen dem Ausführen eines Prefetch-Befehls und dem Ausführen des tatsächlichen Zugriffs verschoben wird, werden die Daten wahrscheinlich aus mindestens L1-Cache entfernt.
Es ist also unwahrscheinlich, dass Sie einen Angreifer besiegen können, der einen Timing-Angriff auf Ihren Code durchführt, es sei denn, es ist realistisch, in Echtzeit zu laufen. Ein Angreifer könnte viele Threads von speicherintensivem Code ausführen ...
Ich bin nicht sicher, was das tatsächlich erreichen würde. Sie ändern nur die Reihenfolge von fetch_into_cache (a), read_from_cache (a), write_to_cache (a) in prefetch_into_cache (a), read_from_cache (a), write_to_cache (a). Außer vielleicht jetzt mit einer großen Verzögerung zwischen dem Prefetch und dem Lesen. –
was genau wollen Sie erreichen? Timing-Attacken vermeiden? –