-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.cpp
More file actions
202 lines (159 loc) · 7.67 KB
/
main.cpp
File metadata and controls
202 lines (159 loc) · 7.67 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
// compile with g++ -std=c++11 -o main.exe main.cpp
#include <iomanip> // for std::setw
#include <ctime>
#include "timing.hpp"
const int secret_max = 10;
const long int size = 65536*2;
int playground [size];
const int reps = 50;
volatile int garbage_bag[RAND_MAX]; // collects outputs to make sure that they do not get optimized away
void flush();
int getMinIndex(float * array, int size);
int getMaxIndex(int * array, int size);
int getMostCommonValue(volatile int * array, int size);
int getNumberOfOccurrences(volatile int * guesses, int number);
void plotGuesses(volatile int * guesses);
int main(){
StartCounter();
garbage_bag[rand()] = rand();
std::cout << EndCounter() << " ignore this line" << std::endl; // reset counter. somehow necessary and for some reason needs to be printed to work ¯\_(ツ)_/¯
printf("\n\n");
printf("--------------------------------\n");
printf("START\n");
printf("--------------------------------\n");
printf("\n\n");
for(int i = 0; i < size; i++){playground[i] = rand();} // initialise playground array
std::cout << std::setprecision(3); // set output float precision
printf("-----------------------------------------------------\n");
std::cout << "Playing around with the cache" << std::endl << std::endl;
flush();
volatile int index = 50000;
std::cout << "First, I created a large array of size " << size << " and\nfilled it with random integers. Let's check out an\nelement somewhere in the middle of my big array and\nsee how long it takes:" << std::endl;
StartCounter();
std::cout << "array[" << index << "] = " << playground[index] << std::endl;
float time1 = EndCounter();
std::cout << "This took " << time1 << " time units" << std::endl << std::endl;
std::cout << "Now, we expect this part of the array to be cached.\nSo if we access it again it should be quicker:" << std::endl;
StartCounter();
std::cout << "array[" << index << "] = " << playground[index] << std::endl;
float time2 = EndCounter();
std::cout << "This took " << time2 << " time units" << std::endl;
printf("\nSo the difference is there but not always. Try re-\nrunning this a couple of times. I found that the\nsecond access is on average 3.5x faster. We can see\nthat the effects of caching are definitely noticebale\nbut quite hard to predict and reproduce. \n \n");
printf("-----------------------------------------------------\n");
std::cout << "Is this good enough to infer information by memory\naccess timing?" << std::endl << std::endl;
std::cout << "To answer this question, let's first create a simple\nsecret..." << std::endl;
srand (time(NULL)); // set new seed
volatile int secret = ((int)rand())%secret_max;
std::cout << secret << std::endl;
std::cout << "Created random secret number between 0 and " << secret_max << "." << std::endl << std::endl;
std::cout << "We should also make sure that nothing of interest is\ncached. To do that, we can simply do some calculations\nwith large arrays that will require lots of\ncache space..." << std::endl;
flush();
std::cout << "Flushed the cache.\n" << std::endl;
float times[secret_max];
volatile int guesses[reps];
volatile float time;
for (volatile int round = 0; round < reps; round++){
flush();
garbage_bag[rand()] = playground[(secret+1)*4096*2*2]; // playground[0] tends to always be cached
for (volatile int i = 0; i < secret_max; i++){
StartCounter();
garbage_bag[rand()] = playground[(i+1)*4096*2*2];
time = EndCounter();
times[i] = time; //times[i] += time;
}
guesses[round] = getMinIndex(times, secret_max);
if(round == 0){
std::cout << "First, we call array[(secret+1)*16384] (The +1 is\nthere because array[0] is almost always cached). We\nwill never look at secret directly but we can\nreconstruct it, because we know that it will be in\nthe cache." << std::endl;
std::cout << "In this simple example we know that secret is between\n0 and " << secret_max-1 << " so we can try all the possibilities:" << std::endl << std::endl;
for (int i = 0; i < secret_max; i++){
std::cout << "Accessing array[(" << i << "+1)*16384] took " << std::setw(12) << times[i] << " time units " << std::endl;
}
std::cout << std::endl << "A shorter time means that it was most likely in the\ncache and we expect array[(secret+1)*16384] to be\nin the cache. " << std::endl;
std::cout << "So the best guess for the secret value this round is: " << guesses[round] << std::endl << std::endl;
std::cout << "Now we can repeat this a couple more times to make it\nsignificant. " << std::endl;
std::cout << "Best guesses: " << guesses[round] << " ";
} else {
std::cout << guesses[round] << " "; // not printing the guess results in more optimisation and worse predictions
}
}
plotGuesses(guesses);
int prediction = getMostCommonValue(guesses, reps);
std::cout << std::endl << std::endl;
std::cout << "The best guess overall is : " << prediction << std::endl;
std::cout << "The real secret value is : " << secret << std::endl;
if(prediction == secret){
std::cout<<"Yay!" << std::endl << std::endl;
} else{
std::cout<<"Damn." << std::endl << std::endl;
}
std::cout << "The performance depends on the machine and other programs running concurrently." << std::endl;
printf("-----------------------------------------------------\n");
printf("\n\n");
printf("--------------------------------\n");
printf("DONE\n");
printf("--------------------------------\n");
volatile int sum = 0;
for (auto i : garbage_bag){
sum += i;
}
std::cout << sum << " ignore this line" << std::endl;
return 0;
}
void flush(){
// Does lots of stuff to "clear" the cache.
const int size = 65536*2*2;
volatile int trash [size];
trash[0] = 13;
for(volatile int j = 1; j < 20; j++){
for (volatile int i = 1; i < size; i++) {
trash[i] = (trash[i-1] * trash[i-1])%3400 + rand();
garbage_bag[rand()] = trash[size-1];
}
garbage_bag[rand()] = trash[size-1];
}
}
void plotGuesses(volatile int * guesses){ // plots a nice little histogram of the guesses
std::cout << std::endl;
std::cout << std::endl;
for (int i = 0; i < secret_max; i++){
std::cout << i << " ";
for (int j = 0; j < getNumberOfOccurrences(guesses, i); j++){
std::cout << "#";
}
std::cout << std::endl;
}
}
int getNumberOfOccurrences(volatile int * guesses, int number){
int counter = 0;
for (int i = 0; i < reps; i++){
if(guesses[i] == number){counter++;}
}
return counter;
}
int getMinIndex(float * array, int size){
// Returns the array index with the lowest value.
int min = 0;
for (int i = 0; i < size; i++) {
if(array[i] < array[min]){
min = i;
}
}
return min;
}
int getMaxIndex(int * array, int size){
// Returns the array index with the highest value
int max = 0;
for (int i = 0; i < size; i++) {
if(array[i] > array[max]){
max = i;
}
}
return max;
}
int getMostCommonValue(volatile int * array, int size){
// Returns the most common value in an array that only contains ints between 0 and secret_max
int counters[secret_max];
for(int i = 0; i < secret_max; i++){counters[i] = 0;} // initialise
for(int i = 0; i < size; i++){counters[array[i]]++;} // count
return getMaxIndex(counters, secret_max);
}