-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathff_mmap.cpp
More file actions
186 lines (156 loc) · 6.54 KB
/
Copy pathff_mmap.cpp
File metadata and controls
186 lines (156 loc) · 6.54 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
// FastFlow mmap mergesort with farm and feedback
// Overlap index build with sorting via a shared progress gate
// Emitter sends one BuildIndex task first and a worker builds while notifying progress
// Other workers sort leaves once their slice is indexed and merges proceed as children return
#include "utils.hpp"
#include <ff/ff.hpp>
#include <ff/farm.hpp>
using namespace ff;
// Shared state
static IndexRec* g_base = nullptr;
static std::string g_unsorted_file;
static std::size_t g_N = 0;
static int g_notify_every = 0; // we use opt.cutoff
static ProgressGate g_gate; // from utils.hpp
// Task model
struct Task {
enum Kind { Sort, Merge, BuildIndex } kind;
std::size_t left, mid, right;
Task* parent; // nullptr only for root
bool is_ready = false; // emitter uses this to detect "both children done"
};
struct Emitter; // fwd
static void build_tasks(std::size_t, std::size_t, Task*, std::size_t, Emitter*);
// Emitter
struct Emitter : ff_node_t<Task> {
Emitter(std::size_t N, std::size_t cutoff) : N(N), cutoff(cutoff) {}
Task* svc(Task* in) override {
if (in == nullptr) // FastFlow’s wake-up dummy
return GO_ON;
// We only ever get Merge tasks back here (parents). BuildIndex returns GO_ON from worker
if (!in->is_ready) { // first child finished
in->is_ready = true;
} else {
Task* parent = in->parent;
ff_send_out(in); // schedule the merge on workers
if (!parent) // root merge enqueued
return EOS; // close the stream
}
return GO_ON;
}
int svc_init() override {
// 0) Send the progressive index builder task FIRST (ensures one worker runs it)
auto* b = new Task{ Task::BuildIndex, 0, 0, 0, /*parent=*/nullptr, /*is_ready=*/false };
ff_send_out(b);
// 1) Build full mergesort task tree (leaves as Sort, internal nodes as Merge)
build_tasks(0, N - 1, /*parent=*/nullptr, cutoff, this); // root auto-freed later
return 0;
}
private:
std::size_t N;
std::size_t cutoff;
};
// Build full binary tree
static void build_tasks(std::size_t l, std::size_t r, Task* parent,
std::size_t cutoff, Emitter* emitter) {
const std::size_t span = r - l + 1;
if (span <= cutoff) {
// Leaf: sort directly (gated in worker before sort_records)
emitter->ff_send_out(new Task{ Task::Sort, l, 0, r, /*parent=*/parent, /*is_ready=*/false });
return;
}
std::size_t m = (l + r) / 2;
// Internal node: create a Merge task; it will be enqueued after both children return
Task* t = new Task{ Task::Merge, l, m, r, /*parent=*/parent, /*is_ready=*/false };
build_tasks(l, m, t, cutoff, emitter);
build_tasks(m+1, r, t, cutoff, emitter);
}
// Worker
struct Worker : ff_node_t<Task> {
Task* svc(Task* t) override {
switch (t->kind) {
case Task::BuildIndex: {
// Progressive index builder; notifies g_gate every g_notify_every elements (and at end)
build_index_mmap(g_unsorted_file, g_base, g_N, g_notify_every, &g_gate);
delete t;
return GO_ON; // nothing to return to emitter
}
case Task::Sort: {
// Wait until the slice [L..R] has been fully indexed, then sort in place
const std::size_t L = t->left, R = t->right;
g_gate.wait_until(R + 1);
sort_records(g_base + L, R - L + 1);
Task* parent = t->parent;
delete t;
return parent; // notify emitter that this child is done
}
case Task::Merge: {
merge_records(g_base, t->left, t->mid, t->right);
Task* parent = t->parent;
delete t;
return parent; // bubble up
}
}
// Should never get here
delete t;
return GO_ON;
}
};
// Main
int main(int argc, char** argv)
{
Params opt = parse_argv(argc, argv);
// Phase 1 - streaming generation
BENCH_START(generate_unsorted);
std::string unsorted_file = generate_unsorted_file_mmap(opt.n_records, opt.payload_max);
BENCH_STOP(generate_unsorted);
// Phase 2+3 - overlap index build + sort
BENCH_START(reading_and_sorting);
const int nthreads = opt.n_threads > 0 ? opt.n_threads : ff_numCores();
if (nthreads <= 1) {
// sequential fallback: build index normally, then std::sort (as before)
IndexRec* idx = build_index_mmap(unsorted_file, opt.n_records); // uses allocating overload
sort_records(idx, opt.n_records);
// stash into globals only so the rest of the file (Phase 4/5) remains identical
g_base = idx;
} else {
// Allocate index with malloc because rewrite_sorted_mmap() will free(idx)
IndexRec* idx = static_cast<IndexRec*>(std::malloc(opt.n_records * sizeof(IndexRec)));
if (!idx) { std::perror("malloc"); std::exit(1); }
// Set shared state for workers
g_base = idx;
g_unsorted_file = unsorted_file;
g_N = opt.n_records;
g_notify_every = opt.cutoff; // wake frequency
g_gate.reset();
// Farm: 1 emitter + (nthreads-1) workers
Emitter emitter(opt.n_records, opt.cutoff);
std::vector<ff_node*> workers;
for (int i = 0; i < nthreads - 1; ++i) workers.push_back(new Worker());
ff_farm farm;
farm.add_emitter(&emitter);
farm.add_workers(workers);
farm.remove_collector();
farm.wrap_around();
farm.set_scheduling_ondemand();
if (farm.run_and_wait_end() < 0) {
error("FastFlow execution failed\n");
return 1;
}
for (auto* w : workers) delete w;
}
BENCH_STOP(reading_and_sorting);
// Phase 4 - rewrite sorted file
BENCH_START(writing);
rewrite_sorted_mmap(unsorted_file, "files/sorted_"
+ std::to_string(opt.n_records) + "_"
+ std::to_string(opt.payload_max) + ".bin", g_base, opt.n_records);
BENCH_STOP(writing);
// Phase 5 - verify
BENCH_START(check_if_sorted);
check_if_sorted_mmap("files/sorted_"
+ std::to_string(opt.n_records) + "_"
+ std::to_string(opt.payload_max) + ".bin", opt.n_records);
BENCH_STOP(check_if_sorted);
return 0;
}