program.cpp 23.9 KB
Newer Older
Paul's avatar
Paul committed
1
2
3
#include <migraphx/program.hpp>
#include <migraphx/stringutils.hpp>
#include <migraphx/instruction.hpp>
4
#include <migraphx/op/identity.hpp>
Paul's avatar
Paul committed
5
#include <migraphx/target.hpp>
Paul's avatar
Paul committed
6
7
8
9
#include <migraphx/env.hpp>
#include <migraphx/ranges.hpp>
#include <migraphx/time.hpp>
#include <migraphx/iterator_for.hpp>
10
#include <migraphx/pass_manager.hpp>
Paul's avatar
Paul committed
11
#include <iostream>
Paul's avatar
Paul committed
12
#include <sstream>
Paul's avatar
Paul committed
13
#include <algorithm>
Paul's avatar
Paul committed
14
#include <utility>
Paul's avatar
Paul committed
15

Paul's avatar
Paul committed
16
namespace migraphx {
Paul's avatar
Paul committed
17
inline namespace MIGRAPHX_INLINE_NS {
Paul's avatar
Paul committed
18

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

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

Paul's avatar
Paul committed
28
29
30
static void print_instruction(std::ostream& os,
                              instruction_ref ins,
                              const std::unordered_map<instruction_ref, std::string>& names)
Paul's avatar
Paul committed
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
{
    os << names.at(ins) << " = ";

    os << ins->get_operator();

    if(ins->name() == "@literal")
    {
        if(ins->get_literal().get_shape().elements() > 10)
            os << "{ ... }";
        else
            os << "{" << ins->get_literal() << "}";
    }

    if(!ins->inputs().empty())
    {
        char delim = '(';
        for(auto&& arg : ins->inputs())
        {
            os << delim << names.at(arg);
            delim = ',';
        }
        os << ")";
    }
Paul's avatar
Paul committed
54

Paul's avatar
Paul committed
55
56
57
    os << " -> " << ins->get_shape();
}

Paul's avatar
Paul committed
58
template <class F>
Khalique's avatar
Khalique committed
59
static void print_program(const program& p, F print_func)
Paul's avatar
Paul committed
60
{
61
    std::unordered_map<instruction_ref, std::string> names;
Paul's avatar
Paul committed
62
63
    int count = 0;

64
    for(auto ins : iterator_for(p))
Paul's avatar
Paul committed
65
    {
Paul's avatar
Paul committed
66
        std::string var_name;
Paul's avatar
Paul committed
67
        if(ins->name() == "@param")
Paul's avatar
Paul committed
68
        {
69
            var_name = any_cast<builtin::param>(ins->get_operator()).parameter;
Paul's avatar
Paul committed
70
        }
Paul's avatar
Paul committed
71
72
73
74
75
        else
        {
            var_name = "@" + std::to_string(count);
            count++;
        }
Paul's avatar
Paul committed
76
        names.emplace(ins, var_name);
Paul's avatar
Paul committed
77

Paul's avatar
Paul committed
78
79
        // TODO: Use all_of
        for(auto&& arg : ins->inputs())
Paul's avatar
Paul committed
80
        {
Paul's avatar
Paul committed
81
82
            assert(p.has_instruction(arg) && "Instruction not found");
            (void)arg;
Paul's avatar
Paul committed
83
84
        }

85
        print_func(ins, names);
Paul's avatar
Paul committed
86
87
88
    }
}

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

Paul's avatar
Paul committed
91
program::program(program&&) noexcept = default;
Shucai Xiao's avatar
Shucai Xiao committed
92
program::~program() noexcept         = default;
Paul's avatar
Paul committed
93

94
// copy constructor
Shucai Xiao's avatar
Shucai Xiao committed
95
program::program(const program& p) { assign(p); }
96
97

// copy assignment operator
Shucai Xiao's avatar
Shucai Xiao committed
98
program& program::operator=(program p)
99
{
Shucai Xiao's avatar
Shucai Xiao committed
100
    std::swap(p.impl, this->impl);
101
102
103
    return *this;
}

Shucai Xiao's avatar
Shucai Xiao committed
104
void program::assign(const program& p)
105
106
{
    // clean the current program
Shucai Xiao's avatar
Shucai Xiao committed
107
    if(!impl)
108
109
110
    {
        impl = std::make_unique<program_impl>();
    }
Shucai Xiao's avatar
Shucai Xiao committed
111
    else if(!impl->instructions.empty())
112
    {
Shucai Xiao's avatar
Shucai Xiao committed
113
        impl->instructions.clear();
114
    }
Shucai Xiao's avatar
Shucai Xiao committed
115
    impl->ctx = p.impl->ctx;
116
117

    std::unordered_map<instruction_ref, instruction_ref> ins_map;
Shucai Xiao's avatar
Shucai Xiao committed
118
    for(auto ins : iterator_for(p))
119
120
    {
        instruction_ref copy_ins{};
Shucai Xiao's avatar
Shucai Xiao committed
121
        if(ins->name() == "@literal")
122
        {
Shucai Xiao's avatar
Shucai Xiao committed
123
            auto l   = ins->get_literal();
124
125
            copy_ins = impl->instructions.insert(impl->instructions.end(), instruction{l});
        }
Shucai Xiao's avatar
Shucai Xiao committed
126
        else if(ins->name() == "@param")
127
        {
Shucai Xiao's avatar
Shucai Xiao committed
128
129
            auto&& name = any_cast<builtin::param>(ins->get_operator()).parameter;
            auto s      = ins->get_shape();
Shucai Xiao's avatar
Shucai Xiao committed
130
131
            copy_ins    = impl->instructions.insert(impl->instructions.end(),
                                                 {builtin::param{name}, std::move(s), {}});
132
        }
Shucai Xiao's avatar
Shucai Xiao committed
133
        else if(ins->name() == "@outline")
134
135
        {
            auto s = ins->get_shape();
Shucai Xiao's avatar
Shucai Xiao committed
136
137
            copy_ins =
                impl->instructions.insert(impl->instructions.end(), {builtin::outline{s}, s, {}});
138
139
140
141
142
143
        }
        else
        {
            // retrieve its mapped input
            auto inputs = ins->inputs();
            // ensure all inputs have its corresponding copy instructions
Shucai Xiao's avatar
Shucai Xiao committed
144
145
            assert(std::all_of(
                inputs.begin(), inputs.end(), [&](auto i) { return ins_map.count(i) > 0; }));
146
            std::vector<instruction_ref> copy_inputs(inputs.size());
Shucai Xiao's avatar
Shucai Xiao committed
147
148
149
            std::transform(inputs.begin(), inputs.end(), copy_inputs.begin(), [&](auto i) {
                return ins_map[i];
            });
150
151
152
153
154
155
156
            copy_ins = add_instruction(ins->get_operator(), copy_inputs);
        }

        ins_map[ins] = copy_ins;
    }
}

Paul's avatar
Paul committed
157
instruction_ref program::add_instruction(const operation& op, std::vector<instruction_ref> args)
Paul's avatar
Paul committed
158
{
Paul's avatar
Paul committed
159
    return insert_instruction(impl->instructions.end(), op, std::move(args));
Paul's avatar
Paul committed
160
}
Paul's avatar
Paul committed
161
162
163
instruction_ref program::insert_instruction(instruction_ref ins,
                                            const operation& op,
                                            std::vector<instruction_ref> args)
Paul's avatar
Paul committed
164
{
Paul's avatar
Paul committed
165
166
167
    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
168
    assert(not starts_with(op.name(), "@"));
Paul's avatar
Paul committed
169
    shape r     = compute_shape(op, args);
Paul's avatar
Paul committed
170
    auto result = impl->instructions.insert(ins, {op, r, std::move(args)});
Paul's avatar
Paul committed
171
    instruction::backreference(result);
Paul's avatar
Paul committed
172
    assert(result->valid(begin()));
Paul's avatar
Paul committed
173
    return result;
Paul's avatar
Paul committed
174
175
}

Paul's avatar
Paul committed
176
177
178
instruction_ref program::replace_instruction(instruction_ref ins,
                                             const operation& op,
                                             std::vector<instruction_ref> args)
Paul's avatar
Paul committed
179
180
181
182
{
    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
183
    assert(not starts_with(op.name(), "@"));
Paul's avatar
Paul committed
184

Paul's avatar
Paul committed
185
    shape r = compute_shape(op, args);
186
    instruction::replace(ins, op, r, std::move(args));
Paul's avatar
Paul committed
187
    assert(ins->valid(begin()));
Paul's avatar
Paul committed
188
189
190
    return ins;
}

Paul's avatar
Paul committed
191
instruction_ref program::replace_instruction(instruction_ref ins, instruction_ref rep)
Paul's avatar
Paul committed
192
{
Paul's avatar
Paul committed
193
194
195
    assert(has_instruction(ins));
    assert(has_instruction(rep));
    assert(ins != rep);
Shucai Xiao's avatar
Shucai Xiao committed
196

Shucai Xiao's avatar
Shucai Xiao committed
197
    if(ins == std::prev(this->end()))
Shucai Xiao's avatar
Shucai Xiao committed
198
    {
Shucai Xiao's avatar
Shucai Xiao committed
199
        return replace_instruction(ins, op::identity{}, rep);
Shucai Xiao's avatar
Shucai Xiao committed
200
201
    }

Paul's avatar
Paul committed
202
    // TODO: Should it be an error if the output is empty?
Paul's avatar
Paul committed
203
    if(ins->outputs().empty())
Paul's avatar
Paul committed
204
205
206
    {
        return rep;
    }
Paul's avatar
Paul committed
207
208
209
    // Make a copy of outputs which can be changed when calling replace_argument
    auto outputs = ins->outputs();
    for(auto out : outputs)
Paul's avatar
Paul committed
210
    {
Paul's avatar
Paul committed
211
212
        // TODO: Check for possible cycles
        if(out != rep)
Paul's avatar
Paul committed
213
        {
Paul's avatar
Paul committed
214
            instruction::replace_argument(out, ins, rep);
Paul's avatar
Paul committed
215
        }
Paul's avatar
Paul committed
216
        assert(out->valid(begin()));
Paul's avatar
Paul committed
217
    }
Paul's avatar
Paul committed
218
    // Replacement should not be dead code unless its the last instruction
Paul's avatar
Paul committed
219
    assert(!rep->outputs().empty() or rep == std::prev(end()));
Paul's avatar
Paul committed
220
    // Output of the original instruction should only be the replacement or empty
Paul's avatar
Paul committed
221
222
223
    assert(ins->outputs().empty() or std::all_of(ins->outputs().begin(),
                                                 ins->outputs().end(),
                                                 [&](auto i) { return i == rep; }));
Paul's avatar
Paul committed
224
    assert(ins->valid(begin()));
Paul's avatar
Paul committed
225
    assert(rep->valid(begin()));
Paul's avatar
Paul committed
226
227
228
    return rep;
}

Paul's avatar
Paul committed
229
instruction_ref program::remove_instruction(instruction_ref ins)
Paul's avatar
Paul committed
230
231
{
    assert(has_instruction(ins));
Paul's avatar
Paul committed
232
    assert(ins->outputs().empty());
Paul's avatar
Paul committed
233
234
235
236
    ins->clear_arguments();
    return impl->instructions.erase(ins);
}

237
238
instruction_ref program::remove_instructions(instruction_ref first, instruction_ref last)
{
Paul's avatar
Paul committed
239
240
    if(first == last)
        return first;
Paul's avatar
Paul committed
241
    // TODO: Check every element
242
    assert(has_instruction(first));
Paul's avatar
Paul committed
243
    std::for_each(first, last, [&](instruction& ins) { ins.clear_arguments(); });
Paul's avatar
Paul committed
244
    assert(std::all_of(first, last, [&](const instruction& ins) { return ins.outputs().empty(); }));
245
246
247
248
249
250
251
252
253
    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
254
instruction_ref program::add_literal(literal l)
Paul's avatar
Paul committed
255
{
Paul's avatar
Paul committed
256
257
258
259
    impl->instructions.emplace_front(std::move(l));
    return impl->instructions.begin();
}

Paul's avatar
Paul committed
260
instruction_ref program::add_outline(const shape& s)
Paul's avatar
Paul committed
261
262
263
{
    impl->instructions.push_front({builtin::outline{s}, s, {}});
    return impl->instructions.begin();
Paul's avatar
Paul committed
264
265
}

Paul's avatar
Paul committed
266
instruction_ref program::add_parameter(std::string name, shape s)
Paul's avatar
Paul committed
267
{
268
    assert(get_parameter_shape(name) == shape{});
Paul's avatar
Paul committed
269
    impl->instructions.push_front({builtin::param{std::move(name)}, std::move(s), {}});
Paul's avatar
Paul committed
270
271
272
    return impl->instructions.begin();
}

Paul's avatar
Paul committed
273
shape program::get_parameter_shape(std::string name) const
Paul's avatar
Paul committed
274
275
{
    auto ins = std::find_if(
Paul's avatar
Paul committed
276
        impl->instructions.begin(), impl->instructions.end(), [&](const instruction& x) {
Paul's avatar
Paul committed
277
            if(x.name() == "@param")
Paul's avatar
Paul committed
278
            {
279
                return any_cast<builtin::param>(x.get_operator()).parameter == name;
Paul's avatar
Paul committed
280
281
282
283
284
285
286
            }
            else
            {
                return false;
            }
        });
    if(ins != this->end())
Paul's avatar
Paul committed
287
        return ins->get_shape();
Paul's avatar
Paul committed
288
289
    else
        return {};
Paul's avatar
Paul committed
290
291
}

mei-ye's avatar
mei-ye committed
292
293
294
295
instruction_ref program::get_parameter(std::string name) const
{
    auto ins = std::find_if(
        impl->instructions.begin(), impl->instructions.end(), [&](const instruction& x) {
Paul's avatar
Paul committed
296
            if(x.name() == "@param")
mei-ye's avatar
mei-ye committed
297
            {
Paul's avatar
Paul committed
298
                return any_cast<builtin::param>(x.get_operator()).parameter == name;
mei-ye's avatar
mei-ye committed
299
300
301
302
303
304
305
306
307
            }
            else
            {
                return false;
            }
        });
    if(ins != this->end())
        return ins;
    else
mei-ye's avatar
mei-ye committed
308
        return this->end();
mei-ye's avatar
mei-ye committed
309
310
}

Paul's avatar
Paul committed
311
312
313
std::unordered_map<std::string, shape> program::get_parameter_shapes() const
{
    std::unordered_map<std::string, shape> result;
Paul's avatar
Paul committed
314
    for(auto&& ins : impl->instructions)
Paul's avatar
Paul committed
315
    {
Paul's avatar
Paul committed
316
        if(ins.name() == "@param")
Paul's avatar
Paul committed
317
        {
318
            auto&& name  = any_cast<builtin::param>(ins.get_operator()).parameter;
Paul's avatar
Paul committed
319
            result[name] = ins.get_shape();
Paul's avatar
Paul committed
320
321
322
323
324
        }
    }
    return result;
}

Paul's avatar
Paul committed
325
bool program::has_instruction(instruction_ref ins) const
Paul's avatar
Paul committed
326
{
Paul's avatar
Paul committed
327
328
329
330
    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
331
332
}

Paul's avatar
Paul committed
333
std::size_t program::size() const { return impl->instructions.size(); }
Paul's avatar
Paul committed
334
335
instruction_ref program::begin() const { return impl->instructions.begin(); }
instruction_ref program::end() const { return impl->instructions.end(); }
336

337
338
339
340
std::vector<shape> program::get_output_shapes() const
{
    return {impl->instructions.back().get_shape()};
}
Paul's avatar
Paul committed
341

Paul's avatar
Paul committed
342
343
context& program::get_context() const { return impl->ctx; }

Paul's avatar
Paul committed
344
345
instruction_ref program::validate() const
{
Paul's avatar
Paul committed
346
347
    return std::find_if(impl->instructions.begin(),
                        impl->instructions.end(),
Paul's avatar
Paul committed
348
                        [&](const instruction& i) { return !i.valid(impl->instructions.begin()); });
Paul's avatar
Paul committed
349
350
}

351
void program::compile(const target& t, compile_options options)
Paul's avatar
Paul committed
352
{
Paul's avatar
Paul committed
353
    assert(this->validate() == impl->instructions.end());
mei-ye's avatar
mei-ye committed
354
    this->impl->ctx = t.get_context();
Paul's avatar
Paul committed
355
    if(enabled(MIGRAPHX_TRACE_COMPILE{}))
356
357
358
359
        options.trace = tracer{std::cout};
    options.trace(*this);
    options.trace();
    run_passes(*this, t.get_passes(this->impl->ctx, options), options.trace);
Paul's avatar
Paul committed
360
    auto invalid = this->validate();
Paul's avatar
Paul committed
361
362
    if(invalid != impl->instructions.end())
    {
Paul's avatar
Paul committed
363
        auto index = std::distance(impl->instructions.begin(), invalid);
Paul's avatar
Paul committed
364
        MIGRAPHX_THROW("Invalid program from compilation at instruction " + std::to_string(index));
Paul's avatar
Paul committed
365
    }
Paul's avatar
Paul committed
366
367
368
369
370
371
372
373
374
    this->finalize();
}

void program::finalize()
{
    for(auto ins : iterator_for(*this))
    {
        ins->finalize(this->impl->ctx);
    }
Paul's avatar
Paul committed
375
376
}

Paul's avatar
Paul committed
377
template <class F>
378
379
380
381
std::vector<argument> generic_eval(const program& p,
                                   context& ctx,
                                   std::unordered_map<std::string, argument> params,
                                   F trace)
Paul's avatar
Paul committed
382
{
Paul's avatar
Paul committed
383
    assert(p.validate() == p.end());
384
    std::unordered_map<instruction_ref, argument> results;
Paul's avatar
Paul committed
385
    results.reserve(p.size() * 2);
Paul's avatar
Paul committed
386
387
    std::vector<argument> values;
    values.reserve(16);
388
    for(auto ins : iterator_for(p))
Paul's avatar
Paul committed
389
    {
390
391
        const auto& name = ins->name();
        if(name == "@literal")
Paul's avatar
Paul committed
392
        {
Paul's avatar
Paul committed
393
            results.emplace(ins, trace(ins, [&] { return ins->get_literal().get_argument(); }));
Paul's avatar
Paul committed
394
        }
395
        else if(name == "@param")
Paul's avatar
Paul committed
396
        {
Paul's avatar
Paul committed
397
398
399
400
401
            results.emplace(
                ins, trace(ins, [&] {
                    auto param_name = any_cast<builtin::param>(ins->get_operator()).parameter;
                    if(not contains(params, param_name))
                        MIGRAPHX_THROW("Parameter not found: " + param_name);
402
                    auto param = params[param_name];
Paul's avatar
Paul committed
403
404
405
406
407
                    if(param.get_shape() != ins->get_shape())
                        MIGRAPHX_THROW("Incorrect shape {" + to_string(param.get_shape()) +
                                       "} for parameter: " + param_name);
                    return param;
                }));
Paul's avatar
Paul committed
408
        }
409
        else if(name == "@outline")
Paul's avatar
Paul committed
410
        {
Paul's avatar
Paul committed
411
            results.emplace(ins, trace(ins, [&] { return argument{ins->get_shape(), nullptr}; }));
Paul's avatar
Paul committed
412
        }
Paul's avatar
Paul committed
413
414
        else
        {
Paul's avatar
Paul committed
415
            values.resize(ins->inputs().size());
Paul's avatar
Paul committed
416
417
418
419
420
            std::transform(
                ins->inputs().begin(), ins->inputs().end(), values.begin(), [&](instruction_ref i) {
                    assert(results.find(i) != results.end());
                    return results[i];
                });
Paul's avatar
Paul committed
421
422
423
            results.emplace(ins, trace(ins, [&] {
                                return ins->get_operator().compute(ctx, ins->get_shape(), values);
                            }));
Paul's avatar
Paul committed
424
        }
425
        assert(results.find(ins) != results.end());
Paul's avatar
Paul committed
426
    }
427
    return {results.at(std::prev(p.end()))};
Paul's avatar
Paul committed
428
429
}

430
std::vector<argument> program::eval(std::unordered_map<std::string, argument> params) const
Paul's avatar
Paul committed
431
{
Paul's avatar
Paul committed
432
433
    auto& ctx = this->impl->ctx;
#ifndef NDEBUG
Paul's avatar
Paul committed
434
    auto sctx          = ctx;
Paul's avatar
Paul committed
435
436
437
    auto check_context = [&](auto f) {
        assert(is_shared(ctx, sctx));
        auto x = f();
Paul's avatar
Paul committed
438
        sctx   = ctx;
Paul's avatar
Paul committed
439
440
441
        return x;
    };
#else
Paul's avatar
Paul committed
442
    auto check_context = [](auto f) { return f(); };
Paul's avatar
Paul committed
443
#endif
Paul's avatar
Paul committed
444
445
446
447

    auto trace_level = value_of(MIGRAPHX_TRACE_EVAL{});

    if(trace_level > 0)
Paul's avatar
Paul committed
448
    {
Paul's avatar
Paul committed
449
        return generic_eval(*this, ctx, std::move(params), [&](auto& ins, auto f) {
Paul's avatar
Paul committed
450
            ctx.finish();
Paul's avatar
Paul committed
451
452
            std::cout << "Run instruction: ";
            this->debug_print(ins);
Paul's avatar
Paul committed
453
454
            auto result = check_context(f);
            ctx.finish();
Paul's avatar
Paul committed
455
            if(trace_level > 1 and ins->name().front() != '@' and ins->name() != "load")
Paul's avatar
Paul committed
456
457
                std::cout << "Ouput: " << result << std::endl;
            return result;
Paul's avatar
Paul committed
458
        });
Paul's avatar
Paul committed
459
460
461
462
    }
    else
    {
        return generic_eval(
Paul's avatar
Paul committed
463
            *this, ctx, std::move(params), [&](auto&, auto f) { return check_context(f); });
Paul's avatar
Paul committed
464
    }
Paul's avatar
Paul committed
465
466
}

Paul's avatar
Paul committed
467
468
469
double common_average(const std::vector<double>& v)
{
    std::size_t n = v.size() / 4;
Paul's avatar
Paul committed
470
471
    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
472
473
}

Paul's avatar
Paul committed
474
475
476
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
477
    auto& ctx          = this->impl->ctx;
Paul's avatar
Paul committed
478
479
    // Run once by itself
    eval(params);
Paul's avatar
Paul committed
480
    ctx.finish();
Paul's avatar
Paul committed
481
    // Run and time entire program
Paul's avatar
Paul committed
482
483
    std::vector<double> total_vec;
    total_vec.reserve(n);
Paul's avatar
Paul committed
484
    for(std::size_t i = 0; i < n; i++)
Paul's avatar
Paul committed
485
    {
Paul's avatar
Paul committed
486
487
488
489
        total_vec.push_back(time<milliseconds>([&] {
            eval(params);
            ctx.finish();
        }));
Paul's avatar
Paul committed
490
    }
Paul's avatar
Paul committed
491
492
    std::sort(total_vec.begin(), total_vec.end());
    std::unordered_map<instruction_ref, std::vector<double>> ins_vec;
Paul's avatar
Paul committed
493
    // Fill the map
Paul's avatar
Paul committed
494
    generic_eval(*this, ctx, params, [&](auto ins, auto) {
Paul's avatar
Paul committed
495
        ins_vec[ins].reserve(n);
Paul's avatar
Paul committed
496
497
        return argument{};
    });
Paul's avatar
Paul committed
498
    // Run and time each instruction
Paul's avatar
Paul committed
499
    for(std::size_t i = 0; i < n; i++)
Paul's avatar
Paul committed
500
    {
Paul's avatar
Paul committed
501
        generic_eval(*this, ctx, params, [&](auto ins, auto f) {
502
            argument result;
Paul's avatar
Paul committed
503
504
505
506
            ins_vec[ins].push_back(time<milliseconds>([&] {
                result = f();
                ctx.finish();
            }));
507
            return result;
Paul's avatar
Paul committed
508
509
        });
    }
Paul's avatar
Paul committed
510
511
    for(auto&& p : ins_vec)
        std::sort(p.second.begin(), p.second.end());
Paul's avatar
Paul committed
512
    // Run and time implicit overhead
Paul's avatar
Paul committed
513
514
    std::vector<double> overhead_vec;
    overhead_vec.reserve(n);
Paul's avatar
Paul committed
515
    for(std::size_t i = 0; i < n; i++)
Paul's avatar
Paul committed
516
    {
Paul's avatar
Paul committed
517
        overhead_vec.push_back(time<milliseconds>([&] { dry_run(params); }));
Paul's avatar
Paul committed
518
519
    }

Paul's avatar
Paul committed
520
    double total_time             = common_average(total_vec);
Paul's avatar
Paul committed
521
    double rate                   = 1000.0 / total_time;
Paul's avatar
Paul committed
522
    double overhead_time          = common_average(overhead_vec);
Paul's avatar
Paul committed
523
    double overhead_percent       = overhead_time * 100.0 / total_time;
Paul's avatar
Paul committed
524
    double total_instruction_time = 0.0;
Paul's avatar
Paul committed
525
    std::unordered_map<std::string, double> op_times;
Paul's avatar
Paul committed
526
    for(auto&& p : ins_vec)
Paul's avatar
Paul committed
527
528
    {
        double avg = common_average(p.second);
Paul's avatar
Paul committed
529
        op_times[p.first->name()] += avg;
Paul's avatar
Paul committed
530
531
        total_instruction_time += avg;
    }
Paul's avatar
Paul committed
532
533
    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
534

535
    print_program(*this, [&](auto ins, const auto& names) {
Khalique's avatar
Khalique committed
536
        print_instruction(std::cout, ins, names);
Paul's avatar
Paul committed
537
538
539
        double avg     = common_average(ins_vec[ins]);
        double percent = std::ceil(100.0 * avg / total_instruction_time);
        os << ": " << avg << "ms, " << percent << "%";
540
        os << std::endl;
Paul's avatar
Paul committed
541
    });
Paul's avatar
Paul committed
542
543
544

    os << std::endl;
    os << "Summary:" << std::endl;
545
546
547
548
549
550
551
    std::vector<std::pair<double, std::string>> op_times_sorted;
    std::transform(op_times.begin(),
                   op_times.end(),
                   std::back_inserter(op_times_sorted),
                   [](auto p) { return std::make_pair(p.second, p.first); });
    std::sort(op_times_sorted.begin(), op_times_sorted.end(), std::greater<>{});
    for(auto&& p : op_times_sorted)
Paul's avatar
Paul committed
552
    {
553
554
        auto&& name    = p.second;
        double avg     = p.first;
Paul's avatar
Paul committed
555
556
557
558
559
        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
560

Paul's avatar
Paul committed
561
    os << "Rate: " << rate << "/sec" << std::endl;
Paul's avatar
Paul committed
562
563
    os << "Total time: " << total_time << "ms" << std::endl;
    os << "Total instructions time: " << total_instruction_time << "ms" << std::endl;
Paul's avatar
Paul committed
564
565
566
567
    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
568
569
}

Paul's avatar
Paul committed
570
571
void program::debug_print() const { std::cout << *this << std::endl; }
void program::debug_print(instruction_ref ins) const
Paul's avatar
Paul committed
572
{
Paul's avatar
Paul committed
573
574
575
576
577
578
579
580
581
582
    if(ins == this->end())
    {
        std::cout << "End instruction" << std::endl;
        return;
    }
    if(not has_instruction(ins))
    {
        std::cout << "Instruction not part of program" << std::endl;
        return;
    }
Paul's avatar
Paul committed
583
    std::stringstream ss;
584
    print_program(*this, [&](auto x, const auto& names) {
Paul's avatar
Paul committed
585
        if(x == ins)
Paul's avatar
Paul committed
586
587
588
589
590
591
        {
            print_instruction(std::cout, x, names);
            std::cout << std::endl;
        }
    });
}
Paul's avatar
Paul committed
592
void program::debug_print(const std::vector<instruction_ref>& inss) const
Paul's avatar
Paul committed
593
{
Paul's avatar
Paul committed
594
    for(auto ins : inss)
Paul's avatar
Paul committed
595
596
597
598
        debug_print(ins);
    std::cout << std::endl;
}

Khalique's avatar
Khalique committed
599
static std::string enclose_name(const std::string& name)
600
{
Khalique's avatar
Khalique committed
601
    return '"' + replace_string(name, "\"", "\\\"") + '"';
602
603
}

604
void program::print_graph(std::ostream& os, bool brief) const
605
606
607
{
    os << "digraph {" << std::endl;
    os << "\trankdir=LR;" << std::endl;
608
    print_program(*this, [&](auto ins, const auto& names) {
609
610
611
612
613
614
615
        std::string label;
        if(brief)
            label = ins->name();
        else
            label = to_string(ins->get_operator());
        os << "\t" << enclose_name(names.at(ins)) << "[label=" << enclose_name(label) << "]";
        os << ";" << std::endl;
616
617
618
619
620
        if(!ins->inputs().empty())
        {
            for(auto&& arg : ins->inputs())
            {
                os << "\t" << enclose_name(names.at(arg)) << " -> " << enclose_name(names.at(ins));
621
622
623
                if(not brief)
                    os << "[label=" << enclose_name(to_string(ins->get_shape())) << "]";
                os << ";" << std::endl;
624
625
626
            }
        }
    });
627
628
629
    os << "}" << std::endl;
}

630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
static std::string cpp_var_name(const std::string& name)
{
    return "m" + replace_string(name, "@", "x");
}

static std::string cpp_op_var(const std::string& name, instruction_ref ins)
{
    return replace_string(name, "@", ins->name());
}

static void print_op_attributes(std::ostream& os, const std::string& name, const operation& op)
{
    std::string x = to_string(op);
    if(contains(x, "["))
    {
        auto start                 = x.find('[');
        auto end                   = x.find(']');
        std::string attribute_text = x.substr(start + 1, end - start - 1);
        std::vector<std::string> attributes;
        for(auto&& attribute : split_string(attribute_text, ','))
        {
            if(contains(attribute, '='))
                attributes.push_back(attribute);
            else
                attributes.back() += "," + attribute;
        }
        for(auto&& attribute : attributes)
        {
            auto p     = split_string(attribute, '=');
            auto key   = p.front();
            auto value = p.back();
            if(contains({"bn_mode", "padding_mode"}, key))
                continue;
            if(key == "mode")
                value = enclose_name(trim(value));
            os << name << "." << key << " = " << value << ";" << std::endl;
        }
    }
}

static void print_cpp_shape(std::ostream& os, const migraphx::shape& s)
{
    os << "migraphx::shape{migraphx::shape::" << s.type_string();
    os << ", {" << to_string_range(s.lens()) << "}";
    if(not s.standard())
        os << ", {" << to_string_range(s.strides()) << "}";
    os << "}";
}

void program::print_cpp(std::ostream& os) const
{
    os << "migraphx::program p;" << std::endl;
    // cppcheck-suppress variableScope
    unsigned long seed = 0;
    print_program(*this, [&](auto ins, const auto& names) {
        auto op = cpp_op_var(names.at(ins), ins);
        if(ins->name().front() != '@')
        {
            os << "migraphx::op::" << ins->name() << " " << op << ";" << std::endl;
            print_op_attributes(os, op, ins->get_operator());
        }
        os << "auto " << cpp_var_name(names.at(ins)) << " = ";
        if(ins->name() == "@literal")
        {
            os << "p.add_literal(";
            bool use_abs = false;
            ins->get_literal().visit([&](auto v) {
                use_abs = std::none_of(v.begin(), v.end(), [](auto x) { return x < 0; });
            });
            if(use_abs)
                os << "migraphx::abs(";
            os << "migraphx::generate_literal(";
            print_cpp_shape(os, ins->get_shape());
            os << ", " << seed << ")";
            if(use_abs)
                os << ")";
            os << ");" << std::endl;
            seed++;
        }
        else if(ins->name() == "@param")
        {
            std::string name = any_cast<builtin::param>(ins->get_operator()).parameter;
            os << "p.add_parameter(" << enclose_name(name) << ",";
            print_cpp_shape(os, ins->get_shape());
            os << ");" << std::endl;
        }
        else
        {
            os << "p.add_instruction(" << op;
            for(auto input : ins->inputs())
            {
                os << ", " << cpp_var_name(names.at(input));
            }
            os << ");" << std::endl;
        }
    });
}

Paul's avatar
Paul committed
728
729
void program::dry_run(std::unordered_map<std::string, argument> params) const
{
Paul's avatar
Paul committed
730
    auto& ctx = this->impl->ctx;
Paul's avatar
Paul committed
731
    generic_eval(*this, ctx, std::move(params), [](auto&&...) { return argument{}; });
Paul's avatar
Paul committed
732
733
}

Paul's avatar
Paul committed
734
735
void program::annotate(std::ostream& os, std::function<void(instruction_ref)> a) const
{
736
737
738
739
740
    print_program(*this, [&](auto ins, const auto& names) {
        print_instruction(os, ins, names);
        a(ins);
        os << std::endl;
    });
Paul's avatar
Paul committed
741
742
}

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

Paul's avatar
Paul committed
745
std::ostream& operator<<(std::ostream& os, const program& p)
Paul's avatar
Paul committed
746
{
747
748
749
750
    print_program(p, [&](auto ins, const auto& names) {
        print_instruction(os, ins, names);
        os << std::endl;
    });
Paul's avatar
Paul committed
751
    return os;
Paul's avatar
Paul committed
752
}
Paul's avatar
Paul committed
753

Paul's avatar
Paul committed
754
} // namespace MIGRAPHX_INLINE_NS
Paul's avatar
Paul committed
755
} // namespace migraphx