"include/device.hpp" did not exist on "23c626a9411a0b828bbfba530ef105d1e8299cdd"
program.cpp 20.6 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;
}

Paul's avatar
Paul committed
627
628
void program::dry_run(std::unordered_map<std::string, argument> params) const
{
Paul's avatar
Paul committed
629
    auto& ctx = this->impl->ctx;
Paul's avatar
Paul committed
630
    generic_eval(*this, ctx, std::move(params), [](auto&&...) { return argument{}; });
Paul's avatar
Paul committed
631
632
}

Paul's avatar
Paul committed
633
634
void program::annotate(std::ostream& os, std::function<void(instruction_ref)> a) const
{
635
636
637
638
639
    print_program(*this, [&](auto ins, const auto& names) {
        print_instruction(os, ins, names);
        a(ins);
        os << std::endl;
    });
Paul's avatar
Paul committed
640
641
}

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

Paul's avatar
Paul committed
644
std::ostream& operator<<(std::ostream& os, const program& p)
Paul's avatar
Paul committed
645
{
646
647
648
649
    print_program(p, [&](auto ins, const auto& names) {
        print_instruction(os, ins, names);
        os << std::endl;
    });
Paul's avatar
Paul committed
650
    return os;
Paul's avatar
Paul committed
651
}
Paul's avatar
Paul committed
652

Paul's avatar
Paul committed
653
} // namespace MIGRAPHX_INLINE_NS
Paul's avatar
Paul committed
654
} // namespace migraphx