.
This commit is contained in:
parent
47c10976f0
commit
12b825a482
88
README.md
88
README.md
@ -2,9 +2,36 @@
|
|||||||
|
|
||||||
### Primitive
|
### Primitive
|
||||||
|
|
||||||
in `./src/lib.c` sind folgende Primitive:
|
|
||||||
|
|
||||||
|
in `./src/lib.c` sind folgende Primitive `flush`, `reload` und `time_maccess`:
|
||||||
|
|
||||||
|
`maccess` greift auf diese Speicheradresse `addr` zu.
|
||||||
|
```C
|
||||||
|
void maccess(void* addr)
|
||||||
|
{
|
||||||
|
asm volatile ("movq (%0), %%rax\n"
|
||||||
|
:
|
||||||
|
: "c" (addr)
|
||||||
|
: "rax");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
`flush` verdrängt die Speicheradresse `addr` aus dem Cache.
|
||||||
|
```C
|
||||||
|
void flush(void* addr)
|
||||||
|
{
|
||||||
|
asm volatile ("clflush 0(%0)\n"
|
||||||
|
:
|
||||||
|
: "c" (addr)
|
||||||
|
: "rax");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
`time_maccess` misst wie lange der Zugriff auf die Speicheradresse `addr` dauert.
|
`time_maccess` misst wie lange der Zugriff auf die Speicheradresse `addr` dauert.
|
||||||
|
|
||||||
|
Zum Messen der Dauer des Speicherzugriffs lesen wir vor und nach dem Zugriff die Zeit aus.
|
||||||
|
Um unsere Reihenfolge zu bewahren reicht das Ausschalten von Compiler Optimierungen ggfs. nicht aus. Um zu verhindern, dass die CPU die Instruktionen parallel oder in anderer Reihenfolge ausführt können sog. Fences verwendet werden:
|
||||||
|
|
||||||
```C
|
```C
|
||||||
size_t time_maccess(void (* addr)(void))
|
size_t time_maccess(void (* addr)(void))
|
||||||
{
|
{
|
||||||
@ -28,38 +55,20 @@ size_t time_maccess(void (* addr)(void))
|
|||||||
return delta;
|
return delta;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
`maccess` greift auf diese Speicheradresse `addr` zu.
|
|
||||||
```C
|
|
||||||
void maccess(void* addr)
|
|
||||||
{
|
|
||||||
asm volatile ("movq (%0), %%rax\n"
|
|
||||||
:
|
|
||||||
: "c" (addr)
|
|
||||||
: "rax");
|
|
||||||
}
|
|
||||||
```
|
|
||||||
`flush` verdrängt die Speicheradresse `addr` aus dem Cache.
|
|
||||||
```C
|
|
||||||
void flush(void* addr)
|
|
||||||
{
|
|
||||||
asm volatile ("clflush 0(%0)\n"
|
|
||||||
:
|
|
||||||
: "c" (addr)
|
|
||||||
: "rax");
|
|
||||||
}
|
|
||||||
```
|
|
||||||
### Block 1: Messen der Timing-Differenzen + Threshold bestimmen
|
### Block 1: Messen der Timing-Differenzen + Threshold bestimmen
|
||||||
|
|
||||||
##### Tips
|
##### Tips
|
||||||
- am besten kompiliert man ohne Compileroptimierungen (`-O0`):
|
- am besten kompiliert man ohne Compileroptimierungen (`-O0`):
|
||||||
```bash
|
```bash
|
||||||
gcc -O0 cache_test.c primitive.c -o cache_test
|
gcc -O0 cache_test.c lib.c -o cache_test
|
||||||
```
|
```
|
||||||
- CMake ist praktisch! siehe `./src/CMakeList.txt`. Verwenden durch `cmake .` und dann `make`.
|
- CMake ist praktisch! siehe `./src/CMakeList.txt`. Verwenden durch `cmake .` und dann `make`.
|
||||||
|
|
||||||
Muster: `./src/cache_test.c`
|
Muster: `./src/cache_test.c`
|
||||||
### Block 2: Signale über den Cache
|
### Block 2: Signale über den Cache
|
||||||
|
|
||||||
|
**Jetzt können wir den Cache doch mal als Medium verwenden. Der Sender kann `0` und `1` über den (ausbleibenden) Speicherzugriff kodieren. Der Empfänger interpretiert dann, je nach Zugriffszeit.**
|
||||||
|
|
||||||
`sharedlib.c` sieht so aus:
|
`sharedlib.c` sieht so aus:
|
||||||
|
|
||||||
```C
|
```C
|
||||||
@ -90,12 +99,14 @@ Damit kann dann auch gar nichts mehr schief gehen :)
|
|||||||
# sharedlib kompilieren
|
# sharedlib kompilieren
|
||||||
gcc -fPIC -shared -O0 -o libsharedlib.so sharedlib.c
|
gcc -fPIC -shared -O0 -o libsharedlib.so sharedlib.c
|
||||||
# sharedlib bei Sender und Empfänger linken
|
# sharedlib bei Sender und Empfänger linken
|
||||||
gcc -O0 sender.c primitive.c -L. -lsharedlib -o sender
|
gcc -O0 sender.c lib.c -L. -lsharedlib -o sender
|
||||||
gcc -O0 empfaenger.c primitive.c -L. -lsharedlib -o empfaenger
|
gcc -O0 empfaenger.c lib.c -L. -lsharedlib -o empfaenger
|
||||||
# ggfs. muss man noch den PATH exportieren
|
# ggfs. muss man noch den PATH exportieren
|
||||||
export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
|
export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
|
||||||
```
|
```
|
||||||
|
|
||||||
|
*(oder einfach `cmake` verwenden)*
|
||||||
|
|
||||||
dann kann man `function` verwenden:
|
dann kann man `function` verwenden:
|
||||||
|
|
||||||
```C
|
```C
|
||||||
@ -105,13 +116,32 @@ maccess((void *) function)
|
|||||||
```
|
```
|
||||||
##### Tips
|
##### Tips
|
||||||
|
|
||||||
- `taskset` zum pinnen der Programme auf einen bestimmten CPU-Kern.
|
- `taskset` zum kann pinnen der Programme auf einen bestimmten CPU-Kern benutzt werden. Das kann helfen.
|
||||||
- Verwendet man blockierenden Sleep um zu Takten, z.B. `clock_nanosleep`, dann sollte man unbedingt darauf achten nicht eine Periodendauer zu schlafen, sondern nur bis zum nächsten Zeitabschnitt. Einfacher geht es mit nicht-blockierendem Sleep, z.B. `ualarm`.
|
- Verwendet man blockierenden Sleep um zu Takten, z.B. `clock_nanosleep`, dann sollte man unbedingt darauf achten nicht eine Periodendauer zu schlafen, sondern nur bis zum nächsten Zeitabschnitt. Einfacher geht es mit nicht-blockierendem Sleep, z.B. `ualarm`.
|
||||||
- Der Zeitabstand von Flush zu Reload sollte MAXIMIERT werden! Dies ist das Zeitfenster, in welchem der Sender die Adresse(n) zurück in den Cache laden kann.
|
|
||||||
- (Der Sender kann das Flushen übernehmen)
|
|
||||||
- (Der Sender kann im gesamten Zeitabschnitt Flushen oder Laden, nicht nur einmal)
|
|
||||||
|
|
||||||
### Block 3: Pakete über den Cache als Medium
|
So kann man `ualarm` nutzen, um alle x µs einen `SIGARLM` auszulösen:
|
||||||
|
|
||||||
|
```C
|
||||||
|
int main(void){
|
||||||
|
...
|
||||||
|
signal(SIGALRM, signal_handler);
|
||||||
|
ualarm(sample_interval, sample_interval);
|
||||||
|
```
|
||||||
|
|
||||||
|
Bei jedem `SIGALRM` Signal wird `signal_handler` ausgeführt:
|
||||||
|
|
||||||
|
```c
|
||||||
|
void signal_handler(int signo) {
|
||||||
|
if (signo == SIGALRM) {
|
||||||
|
# CODE HERE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- Der Zeitabstand von Flush zu Reload sollte MAXIMIERT werden! Dies ist das Zeitfenster, in welchem der Sender die Adresse(n) zurück in den Cache laden kann.
|
||||||
|
- Der Sender kann nicht nur einmal Laden. Das kann man auch mehrfach in einem Zeitfenster machen, um sicher zu gehen, dass die Cacheline auch geladen ist, wenn der Empfänger ausließt.
|
||||||
|
|
||||||
|
### Block 3: Einen String über den Cache als Medium senden
|
||||||
|
|
||||||
- Kann man irgendwie mehr als ein Bit gleichzeitig senden? ;)
|
- Kann man irgendwie mehr als ein Bit gleichzeitig senden? ;)
|
||||||
|
|
||||||
|
|||||||
@ -18,4 +18,5 @@ add_executable(recv recv.c lib.c lib.h)
|
|||||||
target_link_libraries(recv sharedlib)
|
target_link_libraries(recv sharedlib)
|
||||||
|
|
||||||
# add the cache-test executable
|
# add the cache-test executable
|
||||||
add_executable(cache_test cache_test.c lib.c lib.h)
|
add_executable(cache_test cache_test.c lib.c lib.h)
|
||||||
|
target_link_libraries(cache_test sharedlib)
|
||||||
@ -1,13 +1,11 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include "lib.h"
|
||||||
|
#include "sharedlib.h"
|
||||||
|
|
||||||
#define NUM_ACCESSES 1000000
|
#define NUM_ACCESSES 1000000
|
||||||
|
|
||||||
extern void flush(void* addr);
|
|
||||||
extern void maccess(void* addr);
|
|
||||||
extern uint64_t time_maccess(void* addr);
|
|
||||||
|
|
||||||
void measure_multiple_accesses(void (*addr)(void), size_t num_accesses, size_t* hits, size_t* misses)
|
void measure_multiple_accesses(void (*addr)(void), size_t num_accesses, size_t* hits, size_t* misses)
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < num_accesses; i++)
|
for (size_t i = 0; i < num_accesses; i++)
|
||||||
@ -48,7 +46,7 @@ int main()
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
measure_multiple_accesses((void*) maccess, NUM_ACCESSES, hits, misses);
|
measure_multiple_accesses((void*) function, NUM_ACCESSES, hits, misses);
|
||||||
|
|
||||||
size_t threshold = find_crossover_point(hits, misses, NUM_ACCESSES);
|
size_t threshold = find_crossover_point(hits, misses, NUM_ACCESSES);
|
||||||
printf("Suggested threshold: %zu\n", threshold);
|
printf("Suggested threshold: %zu\n", threshold);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user