-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathownership.cc
177 lines (148 loc) · 5.61 KB
/
ownership.cc
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
#include <iostream>
#include <iomanip>
#include <cmath>
#include <locale>
using namespace std;
#include "llheap.h"
#include <stdint.h> // uintptr_t, UINTPTR_MAX
#include <unistd.h> // sleep
#include <string.h> // strcmp
#include <pthread.h>
#define str( s ) #s
#define xstr(s) str(s)
// HYPERAFF => use hyperthreads and fill in pairs of processors on a socket => 129,384, 129,385, ...
#define HYPERAFF
// LINEARAFF => do not use hyperthreading and fill cores on a socket => 129, 130, 131, 132, ...
//#define LINEARAFF
#include "affinity.h"
typedef size_t TYPE; // unsigned word-size
#define CACHE_ALIGN 128 // Intel recommendation
#define CALIGN __attribute__(( aligned(CACHE_ALIGN) ))
#define Fas( change, assn ) __atomic_exchange_n( (&(change)), (assn), __ATOMIC_SEQ_CST )
static __attribute__(( unused )) inline size_t cycleUp( size_t v, size_t n ) { return ( ((v) >= (n - 1)) ? 0 : (v + 1) ); }
static __attribute__(( unused )) inline size_t cycleDown( size_t v, size_t n ) { return ( ((v) <= 0) ? (n - 1) : (v - 1) ); }
template<typename T> T statistics( size_t N, T values[], double & avg, double & std, double & rstd ) {
T sum = 0;
for ( size_t r = 0; r < N; r += 1 ) {
sum += values[r];
} // for
avg = sum / N; // average
double sum2 = 0.0; // sum squared
for ( size_t r = 0; r < N; r += 1 ) { // sum squared differences from average
double diff = values[r] - avg;
sum2 += diff * diff;
} // for
std = sqrt( sum2 / N );
rstd = avg == 0.0 ? 0.0 : std / avg * 100;
return sum;
} // statisitics
enum { MaxThread = 256, MaxBatch = 500 }; // thread global
void * batches[MaxThread][MaxBatch]; // set to nullptr
struct Aligned { CALIGN void * * col; };
volatile Aligned allocations[MaxThread]; // set to nullptr
size_t times[MaxThread]; // set to zero
size_t Threads, Batch; // set in program main
volatile bool stop = false;
void * worker( void * arg ) {
size_t id = (size_t)arg;
size_t cnt = 0, a = 0;
Aligned batch = { batches[id] };
for ( ; ! stop; ) {
for ( ssize_t i = Batch - 1; i >= 0; i -= 1 ) { // allocations
batch.col[i] = malloc( i & 1 ? 42 : 192 );
} // for
Aligned obatch = batch;
while ( (batch.col = Fas( allocations[a].col, batch.col )) == obatch.col || batch.col == nullptr ) { // exchange
if ( stop ) goto fini;
a = cycleUp( a, Threads ); // try another batch
} // while
for ( size_t i = 0; i < Batch; i += 1 ) { // deallocations
free( batch.col[i] );
} // for
cnt += Batch;
a = cycleUp( a, Threads );
} // for
fini: ;
times[id] = cnt; // return throughput
return nullptr;
}; // worker
extern "C" size_t malloc_unfreed() { return Threads * 312/* pthreads */ + 16350/*locale*/; } // llheap only
int main( int argc, char * argv[] ) {
const char * lang = getenv( "LANG" ); // may cause memory leak
try {
locale loc( lang );
cout.imbue( loc ); // print numbers with separators (',')
} catch( runtime_error & ) {
cerr << "Invalid locale language name \"" << lang << "\"" << endl;
exit( EXIT_FAILURE );
} // try
enum {
Dduration = 30, // default duration (seconds)
Dthreads = 8, // default threads
Dbatch = 100, // default batch size
};
size_t Duration = Dduration;
Threads = Dthreads;
Batch = Dbatch;
switch ( argc ) {
case 4:
if ( strcmp( argv[3], "d" ) != 0 ) { // default ?
Batch = atoi( argv[3] ); // experiment duration
if ( (ssize_t)Batch < 1 || Batch > MaxBatch ) goto USAGE;
} // if
[[fallthrough]];
case 3:
if ( strcmp( argv[2], "d" ) != 0 ) { // default ?
Threads = atoi( argv[2] ); // experiment duration
if ( (ssize_t)Threads < 1 || Threads > MaxThread ) goto USAGE;
} // if
[[fallthrough]];
case 2:
if ( strcmp( argv[1], "d" ) != 0 ) { // default ?
Duration = atoi( argv[1] ); // experiment duration
if ( (ssize_t)Duration < 1 ) goto USAGE;
} // if
[[fallthrough]];
case 1: // defaults
break;
USAGE:
default:
cout << "Usage: " << argv[0] << " [ duration (> 0, seconds) | 'd' (default) " << Dduration
<< " [ threads (> 0 && <= " << MaxThread << ") | 'd' (default) " << Dthreads
<< " [ batches (> 0 && <= " << MaxBatch << ") | 'd' (default) " << Dbatch << "] ] ]"
<< endl;
exit( EXIT_FAILURE );
} // switch
cout << fixed << Duration << ' ' << Threads << ' ' << Batch << ' ' << flush;
pthread_t workers[Threads];
for ( size_t i = 0; i < Threads; i += 1 ) {
if ( pthread_create( &workers[i], NULL, worker, (void *)i ) < 0 ) abort();
affinity( workers[i], i );
} // for
sleep( Duration );
stop = true;
for ( unsigned int i = 0; i < Threads; i += 1 ) {
if ( pthread_join( workers[i], NULL ) < 0 ) abort();
} // for
for ( unsigned int i = 0; i < Threads; i += 1 ) { // free any outstanding allocations
if ( allocations[i].col != nullptr ) {
for ( unsigned int j = 0; j < Batch; j += 1 ) { // free any outstanding allocations
free( allocations[i].col[j] );
} // for
} // if
} // for
double avg, std, rstd;
decltype( +times[0] ) total = statistics( Threads, times, avg, std, rstd );
cout << fixed << total << setprecision(0) << ' ' << avg << ' ' << std << ' ' << setprecision(1) << rstd << "% ";
#if defined( HYPERAFF )
cout << "HYPERAFF affinity" << endl;
#elif defined( LINEARAFF )
cout << "LINEARAFF affinity" << endl;
#else
#error no affinity specified
#endif
} // main
// g++-10 -Wall -Wextra -g -O3 -D`hostname` ownership.cc libllheap.so -lpthread -Wl,-rpath=/u/pabuhr/heap -L/u/pabuhr/heap
// Local Variables: //
// compile-command: "g++-14 -Wall -Wextra -g -O3 ownership.cc libllheap-stats-debug.o -lpthread -D`hostname`" //
// End: //