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

55
56
57
    // skip return instruction shape
    if(ins->name() != "@return")
        os << " -> " << ins->get_shape();
Paul's avatar
Paul committed
58
59
}

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

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

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

87
        print_func(ins, names);
Paul's avatar
Paul committed
88
89
90
    }
}

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

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

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

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

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

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

        ins_map[ins] = copy_ins;
    }
}

Paul's avatar
Paul committed
166
instruction_ref program::add_instruction(const operation& op, std::vector<instruction_ref> args)
Paul's avatar
Paul committed
167
{
Paul's avatar
Paul committed
168
    return insert_instruction(impl->instructions.end(), op, std::move(args));
Paul's avatar
Paul committed
169
}
Paul's avatar
Paul committed
170
171
172
instruction_ref program::insert_instruction(instruction_ref ins,
                                            const operation& op,
                                            std::vector<instruction_ref> args)
Paul's avatar
Paul committed
173
{
Paul's avatar
Paul committed
174
175
176
    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
177
    assert(not starts_with(op.name(), "@"));
Paul's avatar
Paul committed
178
    shape r     = compute_shape(op, args);
Paul's avatar
Paul committed
179
    auto result = impl->instructions.insert(ins, {op, r, std::move(args)});
Paul's avatar
Paul committed
180
    instruction::backreference(result);
Paul's avatar
Paul committed
181
    assert(result->valid(begin()));
Paul's avatar
Paul committed
182
    return result;
Paul's avatar
Paul committed
183
184
}

Paul's avatar
Paul committed
185
186
187
instruction_ref program::replace_instruction(instruction_ref ins,
                                             const operation& op,
                                             std::vector<instruction_ref> args)
Paul's avatar
Paul committed
188
189
190
191
{
    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
192
    assert(not starts_with(op.name(), "@"));
Paul's avatar
Paul committed
193

Paul's avatar
Paul committed
194
    shape r = compute_shape(op, args);
195
    instruction::replace(ins, op, r, std::move(args));
Paul's avatar
Paul committed
196
    assert(ins->valid(begin()));
Paul's avatar
Paul committed
197
198
199
    return ins;
}

Paul's avatar
Paul committed
200
instruction_ref program::replace_instruction(instruction_ref ins, instruction_ref rep)
Paul's avatar
Paul committed
201
{
Paul's avatar
Paul committed
202
203
204
    assert(has_instruction(ins));
    assert(has_instruction(rep));
    assert(ins != rep);
Shucai Xiao's avatar
Shucai Xiao committed
205

Shucai Xiao's avatar
Shucai Xiao committed
206
    if(ins == std::prev(this->end()))
Shucai Xiao's avatar
Shucai Xiao committed
207
    {
Shucai Xiao's avatar
Shucai Xiao committed
208
        return replace_instruction(ins, op::identity{}, rep);
Shucai Xiao's avatar
Shucai Xiao committed
209
210
    }

Paul's avatar
Paul committed
211
    // TODO: Should it be an error if the output is empty?
Paul's avatar
Paul committed
212
    if(ins->outputs().empty())
Paul's avatar
Paul committed
213
214
215
    {
        return rep;
    }
Paul's avatar
Paul committed
216
217
218
    // 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
219
    {
Paul's avatar
Paul committed
220
221
        // TODO: Check for possible cycles
        if(out != rep)
Paul's avatar
Paul committed
222
        {
Paul's avatar
Paul committed
223
            instruction::replace_argument(out, ins, rep);
Paul's avatar
Paul committed
224
        }
Paul's avatar
Paul committed
225
        assert(out->valid(begin()));
Paul's avatar
Paul committed
226
    }
Paul's avatar
Paul committed
227
    // Replacement should not be dead code unless its the last instruction
Paul's avatar
Paul committed
228
    assert(!rep->outputs().empty() or rep == std::prev(end()));
Paul's avatar
Paul committed
229
    // Output of the original instruction should only be the replacement or empty
Paul's avatar
Paul committed
230
231
232
    assert(ins->outputs().empty() or std::all_of(ins->outputs().begin(),
                                                 ins->outputs().end(),
                                                 [&](auto i) { return i == rep; }));
Paul's avatar
Paul committed
233
    assert(ins->valid(begin()));
Paul's avatar
Paul committed
234
    assert(rep->valid(begin()));
Paul's avatar
Paul committed
235
236
237
    return rep;
}

Paul's avatar
Paul committed
238
instruction_ref program::remove_instruction(instruction_ref ins)
Paul's avatar
Paul committed
239
240
{
    assert(has_instruction(ins));
Paul's avatar
Paul committed
241
    assert(ins->outputs().empty());
Paul's avatar
Paul committed
242
243
244
245
    ins->clear_arguments();
    return impl->instructions.erase(ins);
}

246
247
instruction_ref program::remove_instructions(instruction_ref first, instruction_ref last)
{
Paul's avatar
Paul committed
248
249
    if(first == last)
        return first;
Paul's avatar
Paul committed
250
    // TODO: Check every element
251
    assert(has_instruction(first));
Paul's avatar
Paul committed
252
    std::for_each(first, last, [&](instruction& ins) { ins.clear_arguments(); });
Paul's avatar
Paul committed
253
    assert(std::all_of(first, last, [&](const instruction& ins) { return ins.outputs().empty(); }));
254
255
256
257
258
259
260
261
262
    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
263
instruction_ref program::add_literal(literal l)
Paul's avatar
Paul committed
264
{
Paul's avatar
Paul committed
265
266
267
268
    impl->instructions.emplace_front(std::move(l));
    return impl->instructions.begin();
}

Paul's avatar
Paul committed
269
instruction_ref program::add_outline(const shape& s)
Paul's avatar
Paul committed
270
271
272
{
    impl->instructions.push_front({builtin::outline{s}, s, {}});
    return impl->instructions.begin();
Paul's avatar
Paul committed
273
274
}

Paul's avatar
Paul committed
275
instruction_ref program::add_parameter(std::string name, shape s)
Paul's avatar
Paul committed
276
{
277
    assert(get_parameter_shape(name) == shape{});
Paul's avatar
Paul committed
278
    impl->instructions.push_front({builtin::param{std::move(name)}, std::move(s), {}});
Paul's avatar
Paul committed
279
280
281
    return impl->instructions.begin();
}

282
283
284
285
286
287
288
289
290
291
292
293
instruction_ref program::add_return(std::vector<instruction_ref> args)
{
    assert(std::all_of(
               args.begin(), args.end(), [&](instruction_ref x) { return has_instruction(x); }) &&
           "Argument is not an exisiting instruction");
    impl->instructions.push_back({builtin::returns{}, {}, args});
    auto result = std::prev(impl->instructions.end());
    instruction::backreference(result);
    assert(result->valid(begin()));
    return result;
}

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

mei-ye's avatar
mei-ye committed
313
314
315
316
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
317
            if(x.name() == "@param")
mei-ye's avatar
mei-ye committed
318
            {
Paul's avatar
Paul committed
319
                return any_cast<builtin::param>(x.get_operator()).parameter == name;
mei-ye's avatar
mei-ye committed
320
321
322
323
324
325
326
327
328
            }
            else
            {
                return false;
            }
        });
    if(ins != this->end())
        return ins;
    else
mei-ye's avatar
mei-ye committed
329
        return this->end();
mei-ye's avatar
mei-ye committed
330
331
}

Paul's avatar
Paul committed
332
333
334
std::unordered_map<std::string, shape> program::get_parameter_shapes() const
{
    std::unordered_map<std::string, shape> result;
Paul's avatar
Paul committed
335
    for(auto&& ins : impl->instructions)
Paul's avatar
Paul committed
336
    {
Paul's avatar
Paul committed
337
        if(ins.name() == "@param")
Paul's avatar
Paul committed
338
        {
339
            auto&& name  = any_cast<builtin::param>(ins.get_operator()).parameter;
Paul's avatar
Paul committed
340
            result[name] = ins.get_shape();
Paul's avatar
Paul committed
341
342
343
344
345
        }
    }
    return result;
}

Paul's avatar
Paul committed
346
bool program::has_instruction(instruction_ref ins) const
Paul's avatar
Paul committed
347
{
Paul's avatar
Paul committed
348
349
350
351
    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
352
353
}

Paul's avatar
Paul committed
354
std::size_t program::size() const { return impl->instructions.size(); }
Paul's avatar
Paul committed
355
356
instruction_ref program::begin() const { return impl->instructions.begin(); }
instruction_ref program::end() const { return impl->instructions.end(); }
357

358
359
std::vector<shape> program::get_output_shapes() const
{
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
    auto last_ins = impl->instructions.back();
    if(last_ins.name() == "@return")
    {
        auto& output_ins = last_ins.inputs();
        std::vector<shape> output_shapes;
        std::transform(output_ins.begin(),
                       output_ins.end(),
                       std::back_inserter(output_shapes),
                       [](auto& ins) { return ins->get_shape(); });

        return output_shapes;
    }
    // The else branch is to provide backward compatibility
    else
    {
        return {last_ins.get_shape()};
    }
377
}
Paul's avatar
Paul committed
378

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

Paul's avatar
Paul committed
381
382
instruction_ref program::validate() const
{
Paul's avatar
Paul committed
383
384
    return std::find_if(impl->instructions.begin(),
                        impl->instructions.end(),
Paul's avatar
Paul committed
385
                        [&](const instruction& i) { return !i.valid(impl->instructions.begin()); });
Paul's avatar
Paul committed
386
387
}

388
void program::compile(const target& t, compile_options options)
Paul's avatar
Paul committed
389
{
Paul's avatar
Paul committed
390
    assert(this->validate() == impl->instructions.end());
mei-ye's avatar
mei-ye committed
391
    this->impl->ctx = t.get_context();
Paul's avatar
Paul committed
392
    if(enabled(MIGRAPHX_TRACE_COMPILE{}))
393
394
395
396
        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
397
    auto invalid = this->validate();
Paul's avatar
Paul committed
398
399
    if(invalid != impl->instructions.end())
    {
Paul's avatar
Paul committed
400
        auto index = std::distance(impl->instructions.begin(), invalid);
Paul's avatar
Paul committed
401
        MIGRAPHX_THROW("Invalid program from compilation at instruction " + std::to_string(index));
Paul's avatar
Paul committed
402
    }
Paul's avatar
Paul committed
403
404
405
406
407
408
409
410
411
    this->finalize();
}

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

Paul's avatar
Paul committed
414
template <class F>
415
416
417
418
std::vector<argument> generic_eval(const program& p,
                                   context& ctx,
                                   std::unordered_map<std::string, argument> params,
                                   F trace)
Paul's avatar
Paul committed
419
{
Paul's avatar
Paul committed
420
    assert(p.validate() == p.end());
421
    std::unordered_map<instruction_ref, argument> results;
Paul's avatar
Paul committed
422
    results.reserve(p.size() * 2);
Paul's avatar
Paul committed
423
424
    std::vector<argument> values;
    values.reserve(16);
425
    for(auto ins : iterator_for(p))
Paul's avatar
Paul committed
426
    {
427
428
        const auto& name = ins->name();
        if(name == "@literal")
Paul's avatar
Paul committed
429
        {
Paul's avatar
Paul committed
430
            results.emplace(ins, trace(ins, [&] { return ins->get_literal().get_argument(); }));
Paul's avatar
Paul committed
431
        }
432
        else if(name == "@param")
Paul's avatar
Paul committed
433
        {
Paul's avatar
Paul committed
434
435
436
437
438
            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);
439
                    auto param = params[param_name];
Paul's avatar
Paul committed
440
441
442
443
444
                    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
445
        }
446
        else if(name == "@outline")
Paul's avatar
Paul committed
447
        {
Paul's avatar
Paul committed
448
            results.emplace(ins, trace(ins, [&] { return argument{ins->get_shape(), nullptr}; }));
Paul's avatar
Paul committed
449
        }
450
451
452
453
454
455
456
457
458
459
460
461
462
        else if(name == "@return")
        {
            std::vector<argument> prog_outputs;
            std::transform(ins->inputs().begin(),
                           ins->inputs().end(),
                           std::back_inserter(prog_outputs),
                           [&](instruction_ref i) {
                               assert(results.find(i) != results.end());
                               return results[i];
                           });

            return prog_outputs;
        }
Paul's avatar
Paul committed
463
464
        else
        {
Paul's avatar
Paul committed
465
            values.resize(ins->inputs().size());
Paul's avatar
Paul committed
466
467
468
469
470
            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
471
472
473
            results.emplace(ins, trace(ins, [&] {
                                return ins->get_operator().compute(ctx, ins->get_shape(), values);
                            }));
Paul's avatar
Paul committed
474
        }
475
        assert(results.find(ins) != results.end());
Paul's avatar
Paul committed
476
    }
477

478
    return {results.at(std::prev(p.end()))};
Paul's avatar
Paul committed
479
480
}

481
std::vector<argument> program::eval(parameter_map params) const
Paul's avatar
Paul committed
482
{
Paul's avatar
Paul committed
483
484
    auto& ctx = this->impl->ctx;
#ifndef NDEBUG
Paul's avatar
Paul committed
485
    auto sctx          = ctx;
Paul's avatar
Paul committed
486
487
488
    auto check_context = [&](auto f) {
        assert(is_shared(ctx, sctx));
        auto x = f();
Paul's avatar
Paul committed
489
        sctx   = ctx;
Paul's avatar
Paul committed
490
491
492
        return x;
    };
#else
Paul's avatar
Paul committed
493
    auto check_context = [](auto f) { return f(); };
Paul's avatar
Paul committed
494
#endif
Paul's avatar
Paul committed
495
496
497
498

    auto trace_level = value_of(MIGRAPHX_TRACE_EVAL{});

    if(trace_level > 0)
Paul's avatar
Paul committed
499
    {
Paul's avatar
Paul committed
500
        return generic_eval(*this, ctx, std::move(params), [&](auto& ins, auto f) {
Paul's avatar
Paul committed
501
            ctx.finish();
Paul's avatar
Paul committed
502
503
            std::cout << "Run instruction: ";
            this->debug_print(ins);
Paul's avatar
Paul committed
504
505
            auto result = check_context(f);
            ctx.finish();
Paul's avatar
Paul committed
506
            if(trace_level > 1 and ins->name().front() != '@' and ins->name() != "load")
Paul's avatar
Paul committed
507
508
                std::cout << "Ouput: " << result << std::endl;
            return result;
Paul's avatar
Paul committed
509
        });
Paul's avatar
Paul committed
510
511
512
513
    }
    else
    {
        return generic_eval(
Paul's avatar
Paul committed
514
            *this, ctx, std::move(params), [&](auto&, auto f) { return check_context(f); });
Paul's avatar
Paul committed
515
    }
Paul's avatar
Paul committed
516
517
}

Paul's avatar
Paul committed
518
519
520
double common_average(const std::vector<double>& v)
{
    std::size_t n = v.size() / 4;
Paul's avatar
Paul committed
521
522
    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
523
524
}

Paul's avatar
Paul committed
525
526
527
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
528
    auto& ctx          = this->impl->ctx;
Paul's avatar
Paul committed
529
530
    // Run once by itself
    eval(params);
Paul's avatar
Paul committed
531
    ctx.finish();
Paul's avatar
Paul committed
532
    // Run and time entire program
Paul's avatar
Paul committed
533
534
    std::vector<double> total_vec;
    total_vec.reserve(n);
Paul's avatar
Paul committed
535
    for(std::size_t i = 0; i < n; i++)
Paul's avatar
Paul committed
536
    {
Paul's avatar
Paul committed
537
538
539
540
        total_vec.push_back(time<milliseconds>([&] {
            eval(params);
            ctx.finish();
        }));
Paul's avatar
Paul committed
541
    }
Paul's avatar
Paul committed
542
543
    std::sort(total_vec.begin(), total_vec.end());
    std::unordered_map<instruction_ref, std::vector<double>> ins_vec;
Paul's avatar
Paul committed
544
    // Fill the map
Paul's avatar
Paul committed
545
    generic_eval(*this, ctx, params, [&](auto ins, auto) {
Paul's avatar
Paul committed
546
        ins_vec[ins].reserve(n);
Paul's avatar
Paul committed
547
548
        return argument{};
    });
Paul's avatar
Paul committed
549
    // Run and time each instruction
Paul's avatar
Paul committed
550
    for(std::size_t i = 0; i < n; i++)
Paul's avatar
Paul committed
551
    {
Paul's avatar
Paul committed
552
        generic_eval(*this, ctx, params, [&](auto ins, auto f) {
553
            argument result;
Paul's avatar
Paul committed
554
555
556
557
            ins_vec[ins].push_back(time<milliseconds>([&] {
                result = f();
                ctx.finish();
            }));
558
            return result;
Paul's avatar
Paul committed
559
560
        });
    }
Paul's avatar
Paul committed
561
562
    for(auto&& p : ins_vec)
        std::sort(p.second.begin(), p.second.end());
Paul's avatar
Paul committed
563
    // Run and time implicit overhead
Paul's avatar
Paul committed
564
565
    std::vector<double> overhead_vec;
    overhead_vec.reserve(n);
Paul's avatar
Paul committed
566
    for(std::size_t i = 0; i < n; i++)
Paul's avatar
Paul committed
567
    {
Paul's avatar
Paul committed
568
        overhead_vec.push_back(time<milliseconds>([&] { dry_run(params); }));
Paul's avatar
Paul committed
569
570
    }

Paul's avatar
Paul committed
571
    double total_time             = common_average(total_vec);
Paul's avatar
Paul committed
572
    double rate                   = 1000.0 / total_time;
Paul's avatar
Paul committed
573
    double overhead_time          = common_average(overhead_vec);
Paul's avatar
Paul committed
574
    double overhead_percent       = overhead_time * 100.0 / total_time;
Paul's avatar
Paul committed
575
    double total_instruction_time = 0.0;
Paul's avatar
Paul committed
576
    std::unordered_map<std::string, double> op_times;
Paul's avatar
Paul committed
577
    for(auto&& p : ins_vec)
Paul's avatar
Paul committed
578
579
    {
        double avg = common_average(p.second);
Paul's avatar
Paul committed
580
        op_times[p.first->name()] += avg;
Paul's avatar
Paul committed
581
582
        total_instruction_time += avg;
    }
Paul's avatar
Paul committed
583
584
    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
585

586
    print_program(*this, [&](auto ins, const auto& names) {
Khalique's avatar
Khalique committed
587
        print_instruction(std::cout, ins, names);
588
589
590
591
592

        // skip return instruction
        if(ins->name() == "@return")
            return;

Paul's avatar
Paul committed
593
594
595
        double avg     = common_average(ins_vec[ins]);
        double percent = std::ceil(100.0 * avg / total_instruction_time);
        os << ": " << avg << "ms, " << percent << "%";
596
        os << std::endl;
Paul's avatar
Paul committed
597
    });
Paul's avatar
Paul committed
598
599
600

    os << std::endl;
    os << "Summary:" << std::endl;
601
602
603
604
605
606
607
    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
608
    {
609
610
        auto&& name    = p.second;
        double avg     = p.first;
Paul's avatar
Paul committed
611
612
613
614
615
        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
616

Paul's avatar
Paul committed
617
    os << "Rate: " << rate << "/sec" << std::endl;
Paul's avatar
Paul committed
618
619
    os << "Total time: " << total_time << "ms" << std::endl;
    os << "Total instructions time: " << total_instruction_time << "ms" << std::endl;
Paul's avatar
Paul committed
620
621
622
623
    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
624
625
}

Paul's avatar
Paul committed
626
627
void program::debug_print() const { std::cout << *this << std::endl; }
void program::debug_print(instruction_ref ins) const
Paul's avatar
Paul committed
628
{
Paul's avatar
Paul committed
629
630
631
632
633
634
635
636
637
638
    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
639
    std::stringstream ss;
640
    print_program(*this, [&](auto x, const auto& names) {
Paul's avatar
Paul committed
641
        if(x == ins)
Paul's avatar
Paul committed
642
643
644
645
646
647
        {
            print_instruction(std::cout, x, names);
            std::cout << std::endl;
        }
    });
}
Paul's avatar
Paul committed
648
void program::debug_print(const std::vector<instruction_ref>& inss) const
Paul's avatar
Paul committed
649
{
Paul's avatar
Paul committed
650
    for(auto ins : inss)
Paul's avatar
Paul committed
651
652
653
654
        debug_print(ins);
    std::cout << std::endl;
}

Khalique's avatar
Khalique committed
655
static std::string enclose_name(const std::string& name)
656
{
Khalique's avatar
Khalique committed
657
    return '"' + replace_string(name, "\"", "\\\"") + '"';
658
659
}

660
void program::print_graph(std::ostream& os, bool brief) const
661
662
663
{
    os << "digraph {" << std::endl;
    os << "\trankdir=LR;" << std::endl;
664
    print_program(*this, [&](auto ins, const auto& names) {
665
666
667
668
669
670
671
        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;
672
673
674
675
676
        if(!ins->inputs().empty())
        {
            for(auto&& arg : ins->inputs())
            {
                os << "\t" << enclose_name(names.at(arg)) << " -> " << enclose_name(names.at(ins));
677
678
679
                if(not brief)
                    os << "[label=" << enclose_name(to_string(ins->get_shape())) << "]";
                os << ";" << std::endl;
680
681
682
            }
        }
    });
683
684
685
    os << "}" << std::endl;
}

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
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
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
784
785
void program::dry_run(std::unordered_map<std::string, argument> params) const
{
Paul's avatar
Paul committed
786
    auto& ctx = this->impl->ctx;
Paul's avatar
Paul committed
787
    generic_eval(*this, ctx, std::move(params), [](auto&&...) { return argument{}; });
Paul's avatar
Paul committed
788
789
}

Paul's avatar
Paul committed
790
791
void program::annotate(std::ostream& os, std::function<void(instruction_ref)> a) const
{
792
793
794
795
796
    print_program(*this, [&](auto ins, const auto& names) {
        print_instruction(os, ins, names);
        a(ins);
        os << std::endl;
    });
Paul's avatar
Paul committed
797
798
}

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

Paul's avatar
Paul committed
801
std::ostream& operator<<(std::ostream& os, const program& p)
Paul's avatar
Paul committed
802
{
803
804
805
806
    print_program(p, [&](auto ins, const auto& names) {
        print_instruction(os, ins, names);
        os << std::endl;
    });
Paul's avatar
Paul committed
807
    return os;
Paul's avatar
Paul committed
808
}
Paul's avatar
Paul committed
809

Paul's avatar
Paul committed
810
} // namespace MIGRAPHX_INLINE_NS
Paul's avatar
Paul committed
811
} // namespace migraphx