program.cpp 23.8 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

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

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

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

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

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

Paul's avatar
Paul committed
374
375
376
377
378
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
379
{
Paul's avatar
Paul committed
380
    assert(p.validate() == p.end());
381
    std::unordered_map<instruction_ref, argument> results;
Paul's avatar
Paul committed
382
    results.reserve(p.size() * 2);
Paul's avatar
Paul committed
383
384
    std::vector<argument> values;
    values.reserve(16);
385
    for(auto ins : iterator_for(p))
Paul's avatar
Paul committed
386
    {
387
388
        const auto& name = ins->name();
        if(name == "@literal")
Paul's avatar
Paul committed
389
        {
Paul's avatar
Paul committed
390
            results.emplace(ins, trace(ins, [&] { return ins->get_literal().get_argument(); }));
Paul's avatar
Paul committed
391
        }
392
        else if(name == "@param")
Paul's avatar
Paul committed
393
        {
Paul's avatar
Paul committed
394
395
396
397
398
            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);
399
                    auto param = params[param_name];
Paul's avatar
Paul committed
400
401
402
403
404
                    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
405
        }
406
        else if(name == "@outline")
Paul's avatar
Paul committed
407
        {
Paul's avatar
Paul committed
408
            results.emplace(ins, trace(ins, [&] { return argument{ins->get_shape(), nullptr}; }));
Paul's avatar
Paul committed
409
        }
Paul's avatar
Paul committed
410
411
        else
        {
Paul's avatar
Paul committed
412
            values.resize(ins->inputs().size());
Paul's avatar
Paul committed
413
414
415
416
417
            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
418
419
420
            results.emplace(ins, trace(ins, [&] {
                                return ins->get_operator().compute(ctx, ins->get_shape(), values);
                            }));
Paul's avatar
Paul committed
421
        }
422
        assert(results.find(ins) != results.end());
Paul's avatar
Paul committed
423
    }
424
    return results.at(std::prev(p.end()));
Paul's avatar
Paul committed
425
426
}

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

    auto trace_level = value_of(MIGRAPHX_TRACE_EVAL{});

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

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

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

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

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

    os << std::endl;
    os << "Summary:" << std::endl;
542
543
544
545
546
547
548
    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
549
    {
550
551
        auto&& name    = p.second;
        double avg     = p.first;
Paul's avatar
Paul committed
552
553
554
555
556
        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
557

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

Paul's avatar
Paul committed
567
568
void program::debug_print() const { std::cout << *this << std::endl; }
void program::debug_print(instruction_ref ins) const
Paul's avatar
Paul committed
569
{
Paul's avatar
Paul committed
570
571
572
573
574
575
576
577
578
579
    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
580
    std::stringstream ss;
581
    print_program(*this, [&](auto x, const auto& names) {
Paul's avatar
Paul committed
582
        if(x == ins)
Paul's avatar
Paul committed
583
584
585
586
587
588
        {
            print_instruction(std::cout, x, names);
            std::cout << std::endl;
        }
    });
}
Paul's avatar
Paul committed
589
void program::debug_print(const std::vector<instruction_ref>& inss) const
Paul's avatar
Paul committed
590
{
Paul's avatar
Paul committed
591
    for(auto ins : inss)
Paul's avatar
Paul committed
592
593
594
595
        debug_print(ins);
    std::cout << std::endl;
}

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

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

627
628
629
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
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
725
726
void program::dry_run(std::unordered_map<std::string, argument> params) const
{
Paul's avatar
Paul committed
727
    auto& ctx = this->impl->ctx;
Paul's avatar
Paul committed
728
    generic_eval(*this, ctx, std::move(params), [](auto&&...) { return argument{}; });
Paul's avatar
Paul committed
729
730
}

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

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

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

Paul's avatar
Paul committed
751
} // namespace MIGRAPHX_INLINE_NS
Paul's avatar
Paul committed
752
} // namespace migraphx