"src/include/blockwise_2d_tensor_op.hpp" did not exist on "5e77650415119376712fbe7aefb2f3922af021db"
program.cpp 13.7 KB
Newer Older
Paul's avatar
Paul committed
1
2
3
#include <migraph/program.hpp>
#include <migraph/stringutils.hpp>
#include <migraph/instruction.hpp>
Paul's avatar
Paul committed
4
#include <migraph/env.hpp>
Paul's avatar
Paul committed
5
#include <migraph/time.hpp>
6
#include <migraph/iterator_for.hpp>
Paul's avatar
Paul committed
7
#include <iostream>
Paul's avatar
Paul committed
8
#include <sstream>
Paul's avatar
Paul committed
9
#include <algorithm>
Paul's avatar
Paul committed
10
#include <utility>
Paul's avatar
Paul committed
11

Paul's avatar
Paul committed
12
namespace migraph {
Paul's avatar
Paul committed
13

Paul's avatar
Paul committed
14
15
MIGRAPH_DECLARE_ENV_VAR(MIGRAPH_TRACE_COMPILE)

Paul's avatar
Paul committed
16
17
18
19
struct program_impl
{
    // A list is used to keep references to an instruction stable
    std::list<instruction> instructions;
Paul's avatar
Paul committed
20
    context ctx;
Paul's avatar
Paul committed
21
22
};

23
const operation& get_operation(instruction_ref ins) { return ins->get_operator(); }
Paul's avatar
Paul committed
24

Paul's avatar
Paul committed
25
template <class F>
Paul's avatar
Paul committed
26
27
static void print_program(std::ostream& os, const program& p, F annonate)
{
28
    std::unordered_map<instruction_ref, std::string> names;
Paul's avatar
Paul committed
29
30
    int count = 0;

31
    for(auto ins : iterator_for(p))
Paul's avatar
Paul committed
32
33
    {
        std::string var_name = "@" + std::to_string(count);
Paul's avatar
Paul committed
34
        if(ins->name() == "@param")
Paul's avatar
Paul committed
35
        {
36
            var_name = any_cast<builtin::param>(ins->get_operator()).parameter;
Paul's avatar
Paul committed
37
38
39
40
        }

        os << var_name << " = ";

41
        os << ins->get_operator();
Paul's avatar
Paul committed
42

Paul's avatar
Paul committed
43
        if(ins->name() == "@literal")
Paul's avatar
Paul committed
44
        {
Paul's avatar
Paul committed
45
            if(ins->get_literal().get_shape().elements() > 10)
Paul's avatar
Paul committed
46
47
                os << "{ ... }";
            else
Paul's avatar
Paul committed
48
                os << "{" << ins->get_literal() << "}";
Paul's avatar
Paul committed
49
50
        }

Paul's avatar
Paul committed
51
        if(!ins->inputs().empty())
Paul's avatar
Paul committed
52
53
        {
            char delim = '(';
Paul's avatar
Paul committed
54
            for(auto&& arg : ins->inputs())
Paul's avatar
Paul committed
55
56
            {
                assert(p.has_instruction(arg) && "Instruction not found");
57
                os << delim << names.at(arg);
Paul's avatar
Paul committed
58
59
60
61
62
                delim = ',';
            }
            os << ")";
        }

Paul's avatar
Paul committed
63
        os << " -> " << ins->get_shape();
Paul's avatar
Paul committed
64

Paul's avatar
Paul committed
65
        annonate(ins, names);
Paul's avatar
Paul committed
66
67
68

        os << std::endl;

69
        names.emplace(ins, var_name);
Paul's avatar
Paul committed
70
71
72
73
        count++;
    }
}

Paul's avatar
Paul committed
74
program::program() : impl(std::make_unique<program_impl>()) {}
Paul's avatar
Paul committed
75

Paul's avatar
Paul committed
76
program::program(program&&) noexcept = default;
Paul's avatar
Paul committed
77
78
program& program::operator=(program&&) noexcept = default;
program::~program() noexcept                    = default;
Paul's avatar
Paul committed
79

Paul's avatar
Paul committed
80
instruction_ref program::add_instruction(const operation& op, std::vector<instruction_ref> args)
Paul's avatar
Paul committed
81
{
Paul's avatar
Paul committed
82
    return insert_instruction(impl->instructions.end(), op, std::move(args));
Paul's avatar
Paul committed
83
}
Paul's avatar
Paul committed
84
85
86
instruction_ref program::insert_instruction(instruction_ref ins,
                                            const operation& op,
                                            std::vector<instruction_ref> args)
Paul's avatar
Paul committed
87
{
Paul's avatar
Paul committed
88
89
90
    assert(std::all_of(
               args.begin(), args.end(), [&](instruction_ref x) { return has_instruction(x); }) &&
           "Argument is not an exisiting instruction");
Paul's avatar
Paul committed
91
    assert(not starts_with(op.name(), "@"));
Paul's avatar
Paul committed
92
93
    // TODO: Use move
    shape r     = compute_shape(op, args);
Paul's avatar
Paul committed
94
    auto result = impl->instructions.insert(ins, {op, r, std::move(args)});
Paul's avatar
Paul committed
95
    instruction::backreference(result);
Paul's avatar
Paul committed
96
    // assert(result->inputs() == args);
Paul's avatar
Paul committed
97
    assert(result->valid(begin()));
Paul's avatar
Paul committed
98
    return result;
Paul's avatar
Paul committed
99
100
}

Paul's avatar
Paul committed
101
102
103
instruction_ref program::replace_instruction(instruction_ref ins,
                                             const operation& op,
                                             std::vector<instruction_ref> args)
Paul's avatar
Paul committed
104
105
106
107
{
    assert(std::all_of(
               args.begin(), args.end(), [&](instruction_ref x) { return has_instruction(x); }) &&
           "Argument is not an exisiting instruction");
Paul's avatar
Paul committed
108
    assert(not starts_with(op.name(), "@"));
Paul's avatar
Paul committed
109

Paul's avatar
Paul committed
110
    shape r = compute_shape(op, args);
111
    instruction::replace(ins, op, r, std::move(args));
Paul's avatar
Paul committed
112
    assert(ins->valid(begin()));
Paul's avatar
Paul committed
113
114
115
    return ins;
}

Paul's avatar
Paul committed
116
instruction_ref program::replace_instruction(instruction_ref ins, instruction_ref rep)
Paul's avatar
Paul committed
117
{
Paul's avatar
Paul committed
118
119
120
121
    assert(has_instruction(ins));
    assert(has_instruction(rep));
    assert(ins != rep);
    // TODO: Should it be an error if the output is empty?
Paul's avatar
Paul committed
122
    if(ins->outputs().empty())
Paul's avatar
Paul committed
123
124
125
    {
        return rep;
    }
Paul's avatar
Paul committed
126
    for(auto&& out : ins->outputs())
Paul's avatar
Paul committed
127
    {
Paul's avatar
Paul committed
128
129
        // TODO: Check for possible cycles
        if(out != rep)
Paul's avatar
Paul committed
130
        {
Paul's avatar
Paul committed
131
            instruction::replace_argument(out, ins, rep);
Paul's avatar
Paul committed
132
        }
Paul's avatar
Paul committed
133
        assert(out->valid(begin()));
Paul's avatar
Paul committed
134
    }
Paul's avatar
Paul committed
135
    // Replacement should not be dead code unless its the last instruction
Paul's avatar
Paul committed
136
    assert(!rep->outputs().empty() or rep == std::prev(end()));
Paul's avatar
Paul committed
137
    assert(ins->valid(begin()));
Paul's avatar
Paul committed
138
    assert(rep->valid(begin()));
Paul's avatar
Paul committed
139
140
141
    return rep;
}

Paul's avatar
Paul committed
142
instruction_ref program::remove_instruction(instruction_ref ins)
Paul's avatar
Paul committed
143
144
{
    assert(has_instruction(ins));
Paul's avatar
Paul committed
145
    assert(ins->outputs().empty());
Paul's avatar
Paul committed
146
147
148
149
    ins->clear_arguments();
    return impl->instructions.erase(ins);
}

150
151
instruction_ref program::remove_instructions(instruction_ref first, instruction_ref last)
{
Paul's avatar
Paul committed
152
153
    if(first == last)
        return first;
Paul's avatar
Paul committed
154
    // TODO: Check every element
155
    assert(has_instruction(first));
Paul's avatar
Paul committed
156
    std::for_each(first, last, [&](instruction& ins) { ins.clear_arguments(); });
Paul's avatar
Paul committed
157
    assert(std::all_of(first, last, [&](instruction& ins) { return ins.outputs().empty(); }));
158
159
160
161
162
163
164
165
166
    return impl->instructions.erase(first, last);
}

instruction_ref program::move_instruction(instruction_ref src, instruction_ref dst)
{
    impl->instructions.splice(dst, impl->instructions, src);
    return src;
}

Paul's avatar
Paul committed
167
instruction_ref program::add_literal(literal l)
Paul's avatar
Paul committed
168
{
Paul's avatar
Paul committed
169
170
171
172
    impl->instructions.emplace_front(std::move(l));
    return impl->instructions.begin();
}

Paul's avatar
Paul committed
173
instruction_ref program::add_outline(const shape& s)
Paul's avatar
Paul committed
174
175
176
{
    impl->instructions.push_front({builtin::outline{s}, s, {}});
    return impl->instructions.begin();
Paul's avatar
Paul committed
177
178
}

Paul's avatar
Paul committed
179
instruction_ref program::add_parameter(std::string name, shape s)
Paul's avatar
Paul committed
180
{
181
    assert(get_parameter_shape(name) == shape{});
Paul's avatar
Paul committed
182
    impl->instructions.push_front({builtin::param{std::move(name)}, std::move(s), {}});
Paul's avatar
Paul committed
183
184
185
    return impl->instructions.begin();
}

Paul's avatar
Paul committed
186
shape program::get_parameter_shape(std::string name) const
Paul's avatar
Paul committed
187
188
{
    auto ins = std::find_if(
Paul's avatar
Paul committed
189
        impl->instructions.begin(), impl->instructions.end(), [&](const instruction& x) {
Paul's avatar
Paul committed
190
            if(x.name() == "@param")
Paul's avatar
Paul committed
191
            {
192
                return any_cast<builtin::param>(x.get_operator()).parameter == name;
Paul's avatar
Paul committed
193
194
195
196
197
198
199
            }
            else
            {
                return false;
            }
        });
    if(ins != this->end())
Paul's avatar
Paul committed
200
        return ins->get_shape();
Paul's avatar
Paul committed
201
202
    else
        return {};
Paul's avatar
Paul committed
203
204
}

Paul's avatar
Paul committed
205
206
207
std::unordered_map<std::string, shape> program::get_parameter_shapes() const
{
    std::unordered_map<std::string, shape> result;
Paul's avatar
Paul committed
208
    for(auto&& ins : impl->instructions)
Paul's avatar
Paul committed
209
    {
Paul's avatar
Paul committed
210
        if(ins.name() == "@param")
Paul's avatar
Paul committed
211
        {
212
            auto&& name  = any_cast<builtin::param>(ins.get_operator()).parameter;
Paul's avatar
Paul committed
213
            result[name] = ins.get_shape();
Paul's avatar
Paul committed
214
215
216
217
218
        }
    }
    return result;
}

Paul's avatar
Paul committed
219
bool program::has_instruction(instruction_ref ins) const
Paul's avatar
Paul committed
220
{
Paul's avatar
Paul committed
221
222
223
224
    return std::find_if(
               impl->instructions.begin(), impl->instructions.end(), [&](const instruction& x) {
                   return std::addressof(*ins) == std::addressof(x);
               }) != impl->instructions.end();
Paul's avatar
Paul committed
225
226
}

Paul's avatar
Paul committed
227
std::size_t program::size() const { return impl->instructions.size(); }
Paul's avatar
Paul committed
228
229
instruction_ref program::begin() const { return impl->instructions.begin(); }
instruction_ref program::end() const { return impl->instructions.end(); }
230

Paul's avatar
Paul committed
231
shape program::get_shape() const { return impl->instructions.back().get_shape(); }
Paul's avatar
Paul committed
232

Paul's avatar
Paul committed
233
234
instruction_ref program::validate() const
{
Paul's avatar
Paul committed
235
236
    return std::find_if(impl->instructions.begin(),
                        impl->instructions.end(),
Paul's avatar
Paul committed
237
                        [&](const instruction& i) { return !i.valid(impl->instructions.begin()); });
Paul's avatar
Paul committed
238
239
}

Paul's avatar
Paul committed
240
void program::compile(const target& t, tracer trace)
Paul's avatar
Paul committed
241
{
Paul's avatar
Paul committed
242
    assert(this->validate() == impl->instructions.end());
Paul's avatar
Paul committed
243
    this->impl->ctx = t.get_context();
Paul's avatar
Paul committed
244
245
246
247
    if(not trace.enabled() and enabled(MIGRAPH_TRACE_COMPILE{}))
        trace = tracer{std::cout};
    trace(*this);
    trace();
Paul's avatar
Paul committed
248
    for(auto&& p : t.get_passes(this->impl->ctx))
Paul's avatar
Paul committed
249
    {
Paul's avatar
Paul committed
250
        trace("Pass: ", p.name());
Paul's avatar
Paul committed
251
        p.apply(*this);
Paul's avatar
Paul committed
252
        trace(*this);
Paul's avatar
Paul committed
253
#ifndef NDEBUG
Paul's avatar
Paul committed
254
        trace("Validate ...");
Paul's avatar
Paul committed
255
        auto invalid = this->validate();
Paul's avatar
Paul committed
256
257
        if(invalid != impl->instructions.end())
        {
Paul's avatar
Paul committed
258
            auto index = std::distance(impl->instructions.begin(), invalid);
Paul's avatar
Paul committed
259
            MIGRAPH_THROW(p.name() + " pass produces invalid program at instruction " +
Paul's avatar
Paul committed
260
                          std::to_string(index) + ": " + invalid->name());
Paul's avatar
Paul committed
261
        }
Paul's avatar
Paul committed
262
        trace();
Paul's avatar
Paul committed
263
264
#endif
    }
Paul's avatar
Paul committed
265
    auto invalid = this->validate();
Paul's avatar
Paul committed
266
267
    if(invalid != impl->instructions.end())
    {
Paul's avatar
Paul committed
268
269
270
        auto index = std::distance(impl->instructions.begin(), invalid);
        MIGRAPH_THROW("Invalid program from compilation at instruction " + std::to_string(index));
    }
Paul's avatar
Paul committed
271
272
}

Paul's avatar
Paul committed
273
274
275
276
277
template <class F>
argument generic_eval(const program& p,
                      context& ctx,
                      std::unordered_map<std::string, argument> params,
                      F trace)
Paul's avatar
Paul committed
278
{
Paul's avatar
Paul committed
279
    assert(p.validate() == p.end());
280
    std::unordered_map<instruction_ref, argument> results;
Paul's avatar
Paul committed
281
    results.reserve(p.size() * 2);
Paul's avatar
Paul committed
282
283
    std::vector<argument> values;
    values.reserve(16);
284
    for(auto ins : iterator_for(p))
Paul's avatar
Paul committed
285
    {
Paul's avatar
Paul committed
286
        if(ins->name() == "@literal")
Paul's avatar
Paul committed
287
        {
Paul's avatar
Paul committed
288
            results.emplace(ins, trace(ins, [&] { return ins->get_literal().get_argument(); }));
Paul's avatar
Paul committed
289
        }
Paul's avatar
Paul committed
290
        else if(ins->name() == "@param")
Paul's avatar
Paul committed
291
        {
Paul's avatar
Paul committed
292
            results.emplace(ins, trace(ins, [&] {
293
                                return params.at(any_cast<builtin::param>(ins->get_operator()).parameter);
Paul's avatar
Paul committed
294
                            }));
Paul's avatar
Paul committed
295
        }
Paul's avatar
Paul committed
296
        else if(ins->name() == "@outline")
Paul's avatar
Paul committed
297
        {
Paul's avatar
Paul committed
298
            results.emplace(ins, trace(ins, [&] { return argument{ins->get_shape(), nullptr}; }));
Paul's avatar
Paul committed
299
        }
Paul's avatar
Paul committed
300
301
        else
        {
Paul's avatar
Paul committed
302
            values.resize(ins->inputs().size());
Paul's avatar
Paul committed
303
304
305
306
307
308
            std::transform(
                ins->inputs().begin(), ins->inputs().end(), values.begin(), [&](instruction_ref i) {
                    assert(results.find(i) != results.end());
                    return results[i];
                });
            results.emplace(
309
                ins, trace(ins, [&] { return ins->get_operator().compute(ctx, ins->get_shape(), values); }));
Paul's avatar
Paul committed
310
        }
311
        assert(results.find(ins) != results.end());
Paul's avatar
Paul committed
312
    }
313
    return results.at(std::prev(p.end()));
Paul's avatar
Paul committed
314
315
}

Paul's avatar
Paul committed
316
317
argument program::eval(std::unordered_map<std::string, argument> params) const
{
Paul's avatar
Paul committed
318
319
    return generic_eval(
        *this, this->impl->ctx, std::move(params), [](auto&, auto f) { return f(); });
Paul's avatar
Paul committed
320
321
}

Paul's avatar
Paul committed
322
323
324
double common_average(const std::vector<double>& v)
{
    std::size_t n = v.size() / 4;
Paul's avatar
Paul committed
325
326
    double total  = std::accumulate(v.begin() + n, v.end() - n, 0.0);
    return total / std::distance(v.begin() + n, v.end() - n);
Paul's avatar
Paul committed
327
328
}

Paul's avatar
Paul committed
329
330
331
void program::perf_report(std::ostream& os, std::size_t n, parameter_map params) const
{
    using milliseconds = std::chrono::duration<double, std::milli>;
Paul's avatar
Paul committed
332
    auto& ctx          = this->impl->ctx;
Paul's avatar
Paul committed
333
334
    // Run once by itself
    eval(params);
Paul's avatar
Paul committed
335
    ctx.finish();
Paul's avatar
Paul committed
336
    // Run and time entire program
Paul's avatar
Paul committed
337
338
    std::vector<double> total_vec;
    total_vec.reserve(n);
Paul's avatar
Paul committed
339
    for(std::size_t i = 0; i < n; i++)
Paul's avatar
Paul committed
340
    {
Paul's avatar
Paul committed
341
342
343
344
        total_vec.push_back(time<milliseconds>([&] {
            eval(params);
            ctx.finish();
        }));
Paul's avatar
Paul committed
345
    }
Paul's avatar
Paul committed
346
347
    std::sort(total_vec.begin(), total_vec.end());
    std::unordered_map<instruction_ref, std::vector<double>> ins_vec;
Paul's avatar
Paul committed
348
    // Fill the map
Paul's avatar
Paul committed
349
    generic_eval(*this, ctx, params, [&](auto ins, auto) {
Paul's avatar
Paul committed
350
        ins_vec[ins].reserve(n);
Paul's avatar
Paul committed
351
352
        return argument{};
    });
Paul's avatar
Paul committed
353
    // Run and time each instruction
Paul's avatar
Paul committed
354
    for(std::size_t i = 0; i < n; i++)
Paul's avatar
Paul committed
355
    {
Paul's avatar
Paul committed
356
        generic_eval(*this, ctx, params, [&](auto ins, auto f) {
357
            argument result;
Paul's avatar
Paul committed
358
359
360
361
            ins_vec[ins].push_back(time<milliseconds>([&] {
                result = f();
                ctx.finish();
            }));
362
            return result;
Paul's avatar
Paul committed
363
364
        });
    }
Paul's avatar
Paul committed
365
366
    for(auto&& p : ins_vec)
        std::sort(p.second.begin(), p.second.end());
Paul's avatar
Paul committed
367
    // Run and time implicit overhead
Paul's avatar
Paul committed
368
369
    std::vector<double> overhead_vec;
    overhead_vec.reserve(n);
Paul's avatar
Paul committed
370
    for(std::size_t i = 0; i < n; i++)
Paul's avatar
Paul committed
371
    {
Paul's avatar
Paul committed
372
373
        overhead_vec.push_back(time<milliseconds>(
            [&] { generic_eval(*this, ctx, params, [](auto...) { return argument{}; }); }));
Paul's avatar
Paul committed
374
375
    }

Paul's avatar
Paul committed
376
    double total_time             = common_average(total_vec);
Paul's avatar
Paul committed
377
    double rate                   = std::ceil(1000.0 / total_time);
Paul's avatar
Paul committed
378
    double overhead_time          = common_average(overhead_vec);
Paul's avatar
Paul committed
379
    double overhead_percent       = overhead_time * 100.0 / total_time;
Paul's avatar
Paul committed
380
    double total_instruction_time = 0.0;
Paul's avatar
Paul committed
381
    std::unordered_map<std::string, double> op_times;
Paul's avatar
Paul committed
382
    for(auto&& p : ins_vec)
Paul's avatar
Paul committed
383
384
    {
        double avg = common_average(p.second);
Paul's avatar
Paul committed
385
        op_times[p.first->name()] += avg;
Paul's avatar
Paul committed
386
387
        total_instruction_time += avg;
    }
Paul's avatar
Paul committed
388
389
    double calculate_overhead_time    = total_time - total_instruction_time;
    double calculate_overhead_percent = calculate_overhead_time * 100.0 / total_time;
Paul's avatar
Paul committed
390

Paul's avatar
Paul committed
391
392
393
394
395
    print_program(os, *this, [&](auto ins, auto&&) {
        double avg     = common_average(ins_vec[ins]);
        double percent = std::ceil(100.0 * avg / total_instruction_time);
        os << ": " << avg << "ms, " << percent << "%";
    });
Paul's avatar
Paul committed
396
397
398

    os << std::endl;
    os << "Summary:" << std::endl;
Paul's avatar
Paul committed
399
    for(auto&& p : op_times)
Paul's avatar
Paul committed
400
    {
Paul's avatar
Paul committed
401
402
        auto&& name    = p.first;
        double avg     = p.second;
Paul's avatar
Paul committed
403
404
405
406
407
        double percent = std::ceil(100.0 * avg / total_instruction_time);
        os << name << ": " << avg << "ms, " << percent << "%" << std::endl;
    }

    os << std::endl;
Paul's avatar
Paul committed
408

Paul's avatar
Paul committed
409
    os << "Rate: " << rate << "/sec" << std::endl;
Paul's avatar
Paul committed
410
411
    os << "Total time: " << total_time << "ms" << std::endl;
    os << "Total instructions time: " << total_instruction_time << "ms" << std::endl;
Paul's avatar
Paul committed
412
413
414
415
    os << "Overhead time: " << overhead_time << "ms"
       << ", " << calculate_overhead_time << "ms" << std::endl;
    os << "Overhead: " << std::round(overhead_percent) << "%"
       << ", " << std::round(calculate_overhead_percent) << "%" << std::endl;
Paul's avatar
Paul committed
416
417
}

Paul's avatar
Paul committed
418
bool operator==(const program& x, const program& y) { return to_string(x) == to_string(y); }
Paul's avatar
Paul committed
419

Paul's avatar
Paul committed
420
std::ostream& operator<<(std::ostream& os, const program& p)
Paul's avatar
Paul committed
421
{
Paul's avatar
Paul committed
422
    print_program(os, p, [](auto&&...) {});
Paul's avatar
Paul committed
423
    return os;
Paul's avatar
Paul committed
424
}
Paul Fultz II's avatar
Paul Fultz II committed
425

Paul's avatar
Paul committed
426
} // namespace migraph