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
#include <migraphx/env.hpp>
#include <migraphx/ranges.hpp>
#include <migraphx/time.hpp>
9
#include <migraphx/pass_manager.hpp>
10
#include <migraphx/register_target.hpp>
Shucai Xiao's avatar
Shucai Xiao committed
11
#include <migraphx/iterator_for.hpp>
12
#include <migraphx/algorithm.hpp>
13
#include <migraphx/output_iterator.hpp>
Shucai Xiao's avatar
Shucai Xiao committed
14
#include <migraphx/make_op.hpp>
Paul's avatar
Paul committed
15
#include <iostream>
Paul's avatar
Paul committed
16
#include <sstream>
Paul's avatar
Paul committed
17
#include <algorithm>
18
#include <set>
Paul's avatar
Paul committed
19
#include <utility>
20

21
#include <unordered_set>
Shucai Xiao's avatar
Shucai Xiao committed
22
23
#include <map>
#include <cassert>
Paul's avatar
Paul committed
24

Paul's avatar
Paul committed
25
namespace migraphx {
Paul's avatar
Paul committed
26
inline namespace MIGRAPHX_INLINE_NS {
Paul's avatar
Paul committed
27

Paul's avatar
Paul committed
28
29
struct program_impl
{
Shucai Xiao's avatar
Shucai Xiao committed
30
    // A map is used to keep references to modules of the program
31
    std::unordered_map<std::string, module> modules;
Paul's avatar
Paul committed
32
    context ctx;
33
    std::string target_name;
Paul's avatar
Paul committed
34
35
};

36
program::program() : impl(std::make_unique<program_impl>()) { this->create_module("main"); }
Paul's avatar
Paul committed
37

Paul's avatar
Paul committed
38
program::program(program&&) noexcept = default;
Shucai Xiao's avatar
Shucai Xiao committed
39
program::~program() noexcept         = default;
Paul's avatar
Paul committed
40

41
// copy constructor
Shucai Xiao's avatar
Shucai Xiao committed
42
program::program(const program& p) { assign(p); }
43
44

// copy assignment operator
Shucai Xiao's avatar
Shucai Xiao committed
45
program& program::operator=(program p)
46
{
Shucai Xiao's avatar
Shucai Xiao committed
47
    std::swap(p.impl, this->impl);
48
49
50
    return *this;
}

Shucai Xiao's avatar
Shucai Xiao committed
51
void program::assign(const program& p)
52
{
Shucai Xiao's avatar
Shucai Xiao committed
53
    if(!impl)
54
55
56
    {
        impl = std::make_unique<program_impl>();
    }
Shucai Xiao's avatar
Shucai Xiao committed
57
    else if(!impl->modules.empty())
58
    {
Shucai Xiao's avatar
Shucai Xiao committed
59
        impl->modules.clear();
60
    }
Shucai Xiao's avatar
Shucai Xiao committed
61

62
    impl->ctx         = p.impl->ctx;
Shucai Xiao's avatar
Shucai Xiao committed
63
64
    impl->target_name = p.impl->target_name;
    impl->modules     = p.impl->modules;
Shucai Xiao's avatar
Shucai Xiao committed
65
66
67
68

    // build a map from old ins to new ins
    // Build a map from old module to new module
    std::unordered_map<module_ref, module_ref> mod_map;
Paul's avatar
Paul committed
69
70
71
72
73
    std::transform(
        impl->modules.begin(),
        impl->modules.end(),
        std::inserter(mod_map, mod_map.begin()),
        [&](auto&& xp) { return std::make_pair(&p.impl->modules.at(xp.first), &xp.second); });
Shucai Xiao's avatar
Shucai Xiao committed
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89

    std::unordered_map<instruction_ref, instruction_ref> ins_map;
    for(auto&& pp : mod_map)
    {
        auto old_ins = iterator_for(*pp.first);
        auto new_ins = iterator_for(*pp.second);
        std::transform(old_ins.begin(),
                       old_ins.end(),
                       new_ins.begin(),
                       std::inserter(ins_map, ins_map.begin()),
                       [](auto x, auto y) { return std::make_pair(x, y); });
    }

    // Update all references from all modules
    for(auto&& mp : impl->modules)
    {
90
        for(auto ins : iterator_for(mp.second))
Shucai Xiao's avatar
Shucai Xiao committed
91
92
            instruction::replace_refs(ins, ins_map, mod_map);
    }
93
94
}

Paul's avatar
Paul committed
95
shape program::get_parameter_shape(std::string name) const
Paul's avatar
Paul committed
96
{
Shucai Xiao's avatar
Shucai Xiao committed
97
98
    const auto* mm = this->get_main_module();
    return mm->get_parameter_shape(std::move(name));
Paul's avatar
Paul committed
99
100
}

101
102
std::vector<std::string> program::get_parameter_names() const
{
Shucai Xiao's avatar
Shucai Xiao committed
103
104
    const auto* mm = this->get_main_module();
    return mm->get_parameter_names();
105
106
}

mei-ye's avatar
mei-ye committed
107
108
instruction_ref program::get_parameter(std::string name) const
{
Shucai Xiao's avatar
Shucai Xiao committed
109
110
    const auto* mm = this->get_main_module();
    return mm->get_parameter(std::move(name));
mei-ye's avatar
mei-ye committed
111
112
}

Paul's avatar
Paul committed
113
114
std::unordered_map<std::string, shape> program::get_parameter_shapes() const
{
Shucai Xiao's avatar
Shucai Xiao committed
115
116
    const auto* mm = this->get_main_module();
    return mm->get_parameter_shapes();
Paul's avatar
Paul committed
117
118
}

Shucai Xiao's avatar
Shucai Xiao committed
119
std::size_t program::size() const { return impl->modules.size(); }
120

121
122
std::vector<shape> program::get_output_shapes() const
{
Shucai Xiao's avatar
Shucai Xiao committed
123
124
    const auto* mm = this->get_main_module();
    return mm->get_output_shapes();
125
}
Paul's avatar
Paul committed
126

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

Paul's avatar
Paul committed
129
130
instruction_ref program::validate() const
{
Shucai Xiao's avatar
Shucai Xiao committed
131
132
    const auto* mm = this->get_main_module();
    return mm->validate();
Paul's avatar
Paul committed
133
134
}

135
136
bool program::is_compiled() const { return not this->impl->target_name.empty(); }

137
void program::compile(const target& t, compile_options options)
Paul's avatar
Paul committed
138
{
139
140
141
    assert(not this->is_compiled());
    this->impl->target_name = t.name();
    this->impl->ctx         = t.get_context();
Paul's avatar
Paul committed
142
    if(enabled(MIGRAPHX_TRACE_COMPILE{}))
143
        options.trace = tracer{std::cout};
Shucai Xiao's avatar
Shucai Xiao committed
144

145
146
    options.trace(*this);
    options.trace();
Shucai Xiao's avatar
Shucai Xiao committed
147

Shucai Xiao's avatar
Shucai Xiao committed
148
    auto&& passes = t.get_passes(this->impl->ctx, options);
149
150
151
    run_passes(*this, passes, options.trace);

    auto mods = this->get_modules();
Shucai Xiao's avatar
Shucai Xiao committed
152

153
154
    // Validate and finalize
    for(const auto& mod : reverse(mods))
Paul's avatar
Paul committed
155
    {
Shucai Xiao's avatar
Shucai Xiao committed
156
157
158
159
160
161
        auto invalid = mod->validate();
        if(invalid != mod->end())
        {
            MIGRAPHX_THROW("Invalid module " + mod->name() + " from compilation at instruction " +
                           std::to_string(std::distance(mod->begin(), invalid)));
        }
162
163
164
165
166
167
168
        auto dangling = mod->find_dangling_reference();
        if(dangling != mod->end())
        {
            auto index = std::distance(mod->begin(), dangling);
            MIGRAPHX_THROW("Dangling reference in module " + mod->name() + " from instruction " +
                           std::to_string(index));
        }
Shucai Xiao's avatar
Shucai Xiao committed
169
        mod->finalize(this->impl->ctx);
Paul's avatar
Paul committed
170
    }
Paul's avatar
Paul committed
171
172
173
174
}

void program::finalize()
{
Shucai Xiao's avatar
Shucai Xiao committed
175
176
    auto* mm = this->get_main_module();
    mm->finalize(this->impl->ctx);
Paul's avatar
Paul committed
177
178
}

Paul's avatar
Paul committed
179
template <class F>
Shucai Xiao's avatar
Shucai Xiao committed
180
std::vector<argument> generic_eval(const module* mod,
181
182
                                   context& ctx,
                                   std::unordered_map<std::string, argument> params,
Shucai Xiao's avatar
Shucai Xiao committed
183
                                   std::unordered_map<instruction_ref, argument> results,
184
                                   F trace)
Paul's avatar
Paul committed
185
{
Shucai Xiao's avatar
Shucai Xiao committed
186
187
    assert(mod->validate() == mod->end());
    results.reserve(mod->size() * 2);
Paul's avatar
Paul committed
188
189
    std::vector<argument> values;
    values.reserve(16);
Shucai Xiao's avatar
Shucai Xiao committed
190
    for(auto ins : iterator_for(*mod))
Paul's avatar
Paul committed
191
    {
192
193
        const auto& name = ins->name();
        if(name == "@literal")
Paul's avatar
Paul committed
194
        {
Paul's avatar
Paul committed
195
            results.emplace(ins, trace(ins, [&] { return ins->get_literal().get_argument(); }));
Paul's avatar
Paul committed
196
        }
197
        else if(name == "@param")
Paul's avatar
Paul committed
198
        {
Paul's avatar
Paul committed
199
200
201
202
203
            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);
204
                    auto param = params[param_name];
Paul's avatar
Paul committed
205
206
207
208
209
                    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
210
        }
211
        else if(name == "@outline")
Paul's avatar
Paul committed
212
        {
Paul's avatar
Paul committed
213
            results.emplace(ins, trace(ins, [&] { return argument{ins->get_shape(), nullptr}; }));
Paul's avatar
Paul committed
214
        }
215
216
217
218
219
220
221
222
223
224
225
226
227
        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
228
229
        else
        {
Paul's avatar
Paul committed
230
            values.resize(ins->inputs().size());
Paul's avatar
Paul committed
231
232
233
234
235
            std::transform(
                ins->inputs().begin(), ins->inputs().end(), values.begin(), [&](instruction_ref i) {
                    assert(results.find(i) != results.end());
                    return results[i];
                });
Shucai Xiao's avatar
Shucai Xiao committed
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256

            const auto& mod_args = ins->module_inputs();
            auto module_eval     = [&](module_ref smod,
                                   const std::unordered_map<std::string, argument>& inputs) {
                return generic_eval(smod, ctx, inputs, results, trace);
            };

            if(not mod_args.empty())
            {
                results.emplace(ins, trace(ins, [&] {
                                    return ins->normalized_operator().compute(
                                        values, mod_args, module_eval);
                                }));
            }
            else
            {
                results.emplace(ins, trace(ins, [&] {
                                    return ins->normalized_operator().compute(
                                        ctx, ins->get_shape(), values);
                                }));
            }
Paul's avatar
Paul committed
257
        }
258
        assert(results.find(ins) != results.end());
Paul's avatar
Paul committed
259
    }
Shucai Xiao's avatar
Shucai Xiao committed
260
    return {results.at(std::prev(mod->end()))};
Paul's avatar
Paul committed
261
262
}

Shucai Xiao's avatar
Shucai Xiao committed
263
264
265
266
267
268
template <class F>
std::vector<argument> generic_eval(const program& p,
                                   context& ctx,
                                   std::unordered_map<std::string, argument> params,
                                   F trace)
{
Shucai Xiao's avatar
Shucai Xiao committed
269
270
    const module* mm = p.get_main_module();
    return generic_eval(mm, ctx, params, {}, trace);
Shucai Xiao's avatar
Shucai Xiao committed
271
272
}

273
std::vector<argument> program::eval(parameter_map params) const
Paul's avatar
Paul committed
274
{
Paul's avatar
Paul committed
275
276
    auto& ctx = this->impl->ctx;
#ifndef NDEBUG
Paul's avatar
Paul committed
277
    auto sctx          = ctx;
Paul's avatar
Paul committed
278
279
280
    auto check_context = [&](auto f) {
        assert(is_shared(ctx, sctx));
        auto x = f();
Paul's avatar
Paul committed
281
        sctx   = ctx;
Paul's avatar
Paul committed
282
283
284
        return x;
    };
#else
Paul's avatar
Paul committed
285
    auto check_context = [](auto f) { return f(); };
Paul's avatar
Paul committed
286
#endif
Paul's avatar
Paul committed
287
288
289
290

    auto trace_level = value_of(MIGRAPHX_TRACE_EVAL{});

    if(trace_level > 0)
Paul's avatar
Paul committed
291
    {
Paul's avatar
Paul committed
292
        return generic_eval(*this, ctx, std::move(params), [&](auto& ins, auto f) {
Paul's avatar
Paul committed
293
            ctx.finish();
Paul's avatar
Paul committed
294
295
            std::cout << "Run instruction: ";
            this->debug_print(ins);
Paul's avatar
Paul committed
296
297
            auto result = check_context(f);
            ctx.finish();
Paul's avatar
Paul committed
298
            if(trace_level > 1 and ins->name().front() != '@' and ins->name() != "load")
Paul's avatar
Paul committed
299
300
                std::cout << "Ouput: " << result << std::endl;
            return result;
Paul's avatar
Paul committed
301
        });
Paul's avatar
Paul committed
302
303
304
305
    }
    else
    {
        return generic_eval(
Paul's avatar
Paul committed
306
            *this, ctx, std::move(params), [&](auto&, auto f) { return check_context(f); });
Paul's avatar
Paul committed
307
    }
Paul's avatar
Paul committed
308
309
}

310
const int program_file_version = 5;
311
312
313
314
315
316
317
318

value program::to_value() const
{
    value result;
    result["version"] = program_file_version;
    result["target"]  = this->impl->target_name;
    if(not this->impl->target_name.empty())
        result["context"] = this->impl->ctx.to_value();
Shucai Xiao's avatar
Shucai Xiao committed
319

320
    value module_vals = value::object{};
Shucai Xiao's avatar
Shucai Xiao committed
321
    std::unordered_map<instruction_ref, std::string> names;
322
    for(auto& mod : this->get_modules())
Shucai Xiao's avatar
Shucai Xiao committed
323
    {
Shucai Xiao's avatar
Shucai Xiao committed
324
325
        value mod_val;
        value nodes;
326
327
        mod_val["name"] = mod->name();
        names           = mod->print(
Shucai Xiao's avatar
Shucai Xiao committed
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
            [&](auto ins, auto ins_names) {
                value node;
                node["output"]     = ins_names.at(ins);
                node["name"]       = ins->name();
                node["shape"]      = migraphx::to_value(ins->get_shape());
                node["normalized"] = ins->is_normalized();
                if(ins->name() == "@literal")
                    node["literal"] = migraphx::to_value(ins->get_literal());
                node["operator"] = ins->get_operator().to_value();
                std::vector<std::string> inputs;
                std::transform(ins->inputs().begin(),
                               ins->inputs().end(),
                               std::back_inserter(inputs),
                               [&](auto i) {
                                   assert(contains(ins_names, i));
                                   return ins_names.at(i);
                               });
                node["inputs"]   = inputs;
                auto module_args = ins->module_inputs();
                if(not module_args.empty())
                {
                    std::vector<std::string> module_inputs;
                    std::transform(module_args.begin(),
                                   module_args.end(),
                                   std::back_inserter(module_inputs),
                                   [&](auto mod_ref) { return mod_ref->name(); });
                    node["module_inputs"] = module_inputs;
                }

                nodes.push_back(node);
            },
            names);
        mod_val["nodes"] = nodes;

362
        module_vals[mod->name()] = mod_val;
Shucai Xiao's avatar
Shucai Xiao committed
363
    }
Shucai Xiao's avatar
Shucai Xiao committed
364
365
366

    result["modules"] = module_vals;

367
368
    return result;
}
Shucai Xiao's avatar
Shucai Xiao committed
369

Shucai Xiao's avatar
Shucai Xiao committed
370
371
372
373
374
static void mod_from_val(module_ref mod,
                         const value& v,
                         std::unordered_map<std::string, instruction_ref>& instructions,
                         const std::unordered_map<std::string, module_ref>& map_mods)
{
375
    const auto& module_val = v.at(mod->name());
Shucai Xiao's avatar
Shucai Xiao committed
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
    for(const value& node : module_val.at("nodes"))
    {
        instruction_ref output;
        auto name       = node.at("name").to<std::string>();
        auto fields     = node.at("operator");
        auto normalized = node.at("normalized").to<bool>();

        if(name == "@param")
        {
            output = mod->add_parameter(fields["parameter"].to<std::string>(),
                                        migraphx::from_value<shape>(node.at("shape")));
        }
        else if(name == "@literal")
        {
            output = mod->add_literal(migraphx::from_value<literal>(node.at("literal")));
        }
        else
        {
            auto op = make_op(name, fields);
            std::vector<instruction_ref> inputs;
            std::transform(node.at("inputs").begin(),
                           node.at("inputs").end(),
                           std::back_inserter(inputs),
                           [&](const value& i) {
                               auto i_name = i.to<std::string>();
                               assert(contains(instructions, i_name));
                               return instructions.at(i_name);
                           });

            std::vector<module_ref> module_inputs;
            if(node.contains("module_inputs"))
            {
                std::transform(node.at("module_inputs").begin(),
                               node.at("module_inputs").end(),
                               std::back_inserter(module_inputs),
                               [&](const value& i) { return map_mods.at(i.to<std::string>()); });

                for(auto& smod : module_inputs)
                {
                    mod_from_val(smod, v, instructions, map_mods);
                }
            }

            if(name == "@return")
            {
                output = mod->add_return(inputs);
            }
            else if(module_inputs.empty())
            {
                output = mod->add_instruction(op, inputs);
            }
            else
            {
                output = mod->add_instruction(op, inputs, module_inputs);
            }
        }
        output->set_normalized(normalized);
        instructions[node.at("output").to<std::string>()] = output;
    }
}

437
438
439
440
void program::from_value(const value& v)
{
    auto version = v.at("version").to<int>();
    if(version != program_file_version)
Shucai Xiao's avatar
Shucai Xiao committed
441
442
443
444
    {
        MIGRAPHX_THROW("Warning: Program version mismatch");
    }

445
446
447
448
449
450
451
452
    this->impl->target_name = v.at("target").to<std::string>();
    if(not this->impl->target_name.empty())
    {
        target t        = make_target(this->impl->target_name);
        this->impl->ctx = t.get_context();
        this->impl->ctx.from_value(v.at("context"));
    }

Shucai Xiao's avatar
Shucai Xiao committed
453
454
    auto module_vals = v.at("modules");
    for(const auto& vv : module_vals)
455
    {
456
        const auto& name = vv.get_key();
Shucai Xiao's avatar
Shucai Xiao committed
457
458
        if(name == "main")
            continue;
459
        impl->modules.emplace(name, name);
460
    }
461
    std::unordered_map<std::string, module_ref> map_mods;
Paul's avatar
Paul committed
462
463
464
465
    std::transform(impl->modules.begin(),
                   impl->modules.end(),
                   std::inserter(map_mods, map_mods.end()),
                   [&](auto&& pp) { return std::make_pair(pp.first, &pp.second); });
Shucai Xiao's avatar
Shucai Xiao committed
466
467
468
469
470

    std::unordered_map<std::string, instruction_ref> map_insts;
    auto* mm = get_main_module();
    mod_from_val(mm, module_vals, map_insts, map_mods);

471
472
473
    this->finalize();
}

Paul's avatar
Paul committed
474
475
476
double common_average(const std::vector<double>& v)
{
    std::size_t n = v.size() / 4;
Paul's avatar
Paul committed
477
478
    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
479
480
}

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

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

Shucai Xiao's avatar
Shucai Xiao committed
542
543
544
    std::unordered_map<instruction_ref, std::string> names;
    this->print(names, [&](auto ins, auto ins_names) {
        instruction::print(std::cout, ins, ins_names);
545
546
547
548
549

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

Paul's avatar
Paul committed
550
551
552
        double avg     = common_average(ins_vec[ins]);
        double percent = std::ceil(100.0 * avg / total_instruction_time);
        os << ": " << avg << "ms, " << percent << "%";
553
        os << std::endl;
Paul's avatar
Paul committed
554
    });
Paul's avatar
Paul committed
555
556
557

    os << std::endl;
    os << "Summary:" << std::endl;
558
559
560
561
562
563
564
    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
565
    {
566
567
        auto&& name    = p.second;
        double avg     = p.first;
Paul's avatar
Paul committed
568
569
570
571
572
        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
573

Paul's avatar
Paul committed
574
    os << "Rate: " << rate << "/sec" << std::endl;
Paul's avatar
Paul committed
575
576
    os << "Total time: " << total_time << "ms" << std::endl;
    os << "Total instructions time: " << total_instruction_time << "ms" << std::endl;
Paul's avatar
Paul committed
577
578
579
580
    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
581
582
}

Paul's avatar
Paul committed
583
584
void program::debug_print() const { std::cout << *this << std::endl; }
void program::debug_print(instruction_ref ins) const
Paul's avatar
Paul committed
585
{
Shucai Xiao's avatar
Shucai Xiao committed
586
    std::unordered_map<instruction_ref, std::string> names;
587
588
    if(std::any_of(this->impl->modules.begin(), this->impl->modules.end(), [&](const auto& pp) {
           return (pp.second.end() == ins);
Shucai Xiao's avatar
Shucai Xiao committed
589
       }))
Paul's avatar
Paul committed
590
591
592
593
    {
        std::cout << "End instruction" << std::endl;
        return;
    }
594
595
    else if(std::none_of(this->impl->modules.begin(),
                         this->impl->modules.end(),
596
                         [&](const auto& pp) { return pp.second.has_instruction(ins); }))
Paul's avatar
Paul committed
597
598
599
600
    {
        std::cout << "Instruction not part of program" << std::endl;
        return;
    }
Shucai Xiao's avatar
Shucai Xiao committed
601

Paul's avatar
Paul committed
602
    std::stringstream ss;
Shucai Xiao's avatar
Shucai Xiao committed
603
    this->print(names, [&](auto x, auto ins_names) {
Paul's avatar
Paul committed
604
        if(x == ins)
Paul's avatar
Paul committed
605
        {
Shucai Xiao's avatar
Shucai Xiao committed
606
            instruction::print(std::cout, x, ins_names);
Paul's avatar
Paul committed
607
608
609
610
611
            std::cout << std::endl;
        }
    });
}

Shucai Xiao's avatar
Shucai Xiao committed
612
613
614
615
void program::print(
    std::unordered_map<instruction_ref, std::string>& names,
    const std::function<void(instruction_ref, std::unordered_map<instruction_ref, std::string>)>&
        print_func) const
616
{
617
    for(const auto& pp : this->impl->modules)
618
    {
619
        names = pp.second.print(print_func, names);
620
621
622
    }
}

Shucai Xiao's avatar
Shucai Xiao committed
623
void program::print_graph(std::ostream& os, bool brief) const
624
{
Shucai Xiao's avatar
Shucai Xiao committed
625
626
    const auto* mm = this->get_main_module();
    mm->print_graph(os, brief);
627
628
629
630
}

void program::print_cpp(std::ostream& os) const
{
Shucai Xiao's avatar
Shucai Xiao committed
631
632
633
634
635
636
637
638
    auto vec_modules = this->get_modules();
    std::unordered_map<instruction_ref, std::string> names;
    for(auto& mod : vec_modules)
    {
        os << "module: \"" << mod->name() << "\"" << std::endl;
        names = mod->print_cpp(os, names);
        os << std::endl;
    }
639
640
}

Paul's avatar
Paul committed
641
642
void program::dry_run(std::unordered_map<std::string, argument> params) const
{
Paul's avatar
Paul committed
643
    auto& ctx = this->impl->ctx;
Paul's avatar
Paul committed
644
    generic_eval(*this, ctx, std::move(params), [](auto&&...) { return argument{}; });
Paul's avatar
Paul committed
645
646
}

Shucai Xiao's avatar
Shucai Xiao committed
647
void program::annotate(std::ostream& os, const std::function<void(instruction_ref)>& a) const
Paul's avatar
Paul committed
648
{
649
    for(auto& pp : this->impl->modules)
Shucai Xiao's avatar
Shucai Xiao committed
650
    {
651
652
        std::cout << pp.first << ":" << std::endl;
        pp.second.annotate(os, a);
Shucai Xiao's avatar
Shucai Xiao committed
653
    }
Paul's avatar
Paul committed
654
655
}

Paul's avatar
Paul committed
656
const module* program::get_module(const std::string& name) const { return &impl->modules.at(name); }
Shucai Xiao's avatar
Shucai Xiao committed
657
658
659

module* program::create_module(const std::string& name)
{
660
    assert(not contains(impl->modules, name));
661
662
    auto r = impl->modules.emplace(name, name);
    return &(r.first->second);
Shucai Xiao's avatar
Shucai Xiao committed
663
664
}

Paul's avatar
Paul committed
665
module* program::get_module(const std::string& name) { return &impl->modules.at(name); }
Shucai Xiao's avatar
Shucai Xiao committed
666
667
668
669
670

module* program::get_main_module() { return get_module("main"); }

const module* program::get_main_module() const { return get_module("main"); }

Paul's avatar
Paul committed
671
template <class T>
672
std::vector<T*> generic_get_modules(T* mm)
Shucai Xiao's avatar
Shucai Xiao committed
673
{
674
    std::vector<T*> vec_modules;
Shucai Xiao's avatar
Shucai Xiao committed
675
676
677
678
679
    vec_modules.push_back(mm);
    auto sub_modules = mm->get_sub_modules();
    vec_modules.insert(vec_modules.end(), sub_modules.begin(), sub_modules.end());
    return vec_modules;
}
Shucai Xiao's avatar
Shucai Xiao committed
680

Paul's avatar
Paul committed
681
template <class Map, class T, class OutputIterator>
682
void generic_get_unused_modules(Map& m, const std::vector<T*>& mods, OutputIterator out)
Shucai Xiao's avatar
Shucai Xiao committed
683
{
684
685
686
687
    std::unordered_set<std::string> used;
    std::transform(mods.begin(), mods.end(), std::inserter(used, used.end()), [](auto&& mod) {
        return mod->name();
    });
Paul's avatar
Paul committed
688
689
690
691
692
    transform_if(m.begin(),
                 m.end(),
                 out,
                 [&](auto&& pp) { return not contains(used, pp.first); },
                 [](auto&& pp) { return &pp.second; });
693
}
Shucai Xiao's avatar
Shucai Xiao committed
694

695
696
697
698
699
700
701
702
703
704
705
706
std::vector<const module*> program::get_modules() const
{
    auto result = generic_get_modules(this->get_main_module());
    generic_get_unused_modules(impl->modules, result, std::back_inserter(result));
    return result;
}

std::vector<module*> program::get_modules()
{
    auto result = generic_get_modules(this->get_main_module());
    generic_get_unused_modules(impl->modules, result, std::back_inserter(result));
    return result;
Shucai Xiao's avatar
Shucai Xiao committed
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
template <class Map, class T>
bool is_unused_module(Map& m, const std::vector<T*>& mods, const std::string& name)
{
    bool is_unused = false;
    generic_get_unused_modules(m, mods, make_function_output_iterator([&](auto* mod) {
                                   if(mod->name() == name)
                                       is_unused = true;
                               }));
    return is_unused;
}

template <class Map>
bool references_instruction(Map& m, const instruction& ins, const std::string& name)
{
    return std::any_of(m.begin(), m.end(), [&](auto&& p) {
        if(p.first == name)
            return false;
        return std::any_of(p.second.begin(), p.second.end(), [&](auto&& i) {
            return std::any_of(i.inputs().begin(), i.inputs().end(), [&](auto&& j) {
                return std::addressof(*j) == std::addressof(ins);
            });
        });
    });
}

void program::remove_module(const std::string& name)
{
    // cppcheck-suppress assertWithSideEffect
    assert(is_unused_module(impl->modules, generic_get_modules(this->get_main_module()), name) &&
           "Module used in program");
    assert(std::none_of(
               impl->modules.at(name).begin(),
               impl->modules.at(name).end(),
               [&](auto&& ins) { return references_instruction(impl->modules, ins, name); }) &&
           "Instruction referenced in another module");
    impl->modules.erase(name);
}

void program::remove_unused_modules()
{
    std::vector<module*> unused;
    generic_get_unused_modules(
        impl->modules, generic_get_modules(this->get_main_module()), std::back_inserter(unused));
    for(auto* m : unused)
        this->remove_module(m->name());
}

756
757
program& program::sort()
{
758
    for(auto& pp : this->impl->modules)
Shucai Xiao's avatar
Shucai Xiao committed
759
    {
760
        pp.second.sort();
Shucai Xiao's avatar
Shucai Xiao committed
761
762
    }

763
764
765
    return *this;
}

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

Paul's avatar
Paul committed
768
std::ostream& operator<<(std::ostream& os, const program& p)
Paul's avatar
Paul committed
769
{
Shucai Xiao's avatar
Shucai Xiao committed
770
771
772
    auto vec_modules = p.get_modules();
    std::unordered_map<instruction_ref, std::string> names;
    for(auto& mod : vec_modules)
Shucai Xiao's avatar
Shucai Xiao committed
773
    {
Shucai Xiao's avatar
Shucai Xiao committed
774
775
776
777
778
779
780
        os << "module: \"" << mod->name() << "\"" << std::endl;
        names = mod->print(
            [&](auto ins, auto ins_names) {
                instruction::print(os, ins, ins_names);
                os << std::endl;
            },
            names);
781
        os << std::endl;
Shucai Xiao's avatar
Shucai Xiao committed
782
783
    }

Paul's avatar
Paul committed
784
    return os;
Paul's avatar
Paul committed
785
}
Paul's avatar
Paul committed
786

Paul's avatar
Paul committed
787
} // namespace MIGRAPHX_INLINE_NS
Paul's avatar
Paul committed
788
} // namespace migraphx