"docs/vscode:/vscode.git/clone" did not exist on "ea84b674490cc5fe52478c7886de2b99843883cf"
program.cpp 27.2 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/iterator.hpp>
13
#include <migraphx/algorithm.hpp>
14
#include <migraphx/output_iterator.hpp>
Shucai Xiao's avatar
Shucai Xiao committed
15
#include <migraphx/make_op.hpp>
Paul's avatar
Paul committed
16
#include <iostream>
Paul's avatar
Paul committed
17
#include <sstream>
Paul's avatar
Paul committed
18
#include <algorithm>
19
#include <set>
Paul's avatar
Paul committed
20
#include <utility>
21

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

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

29
30
using milliseconds = std::chrono::duration<double, std::milli>;

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

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

Paul's avatar
Paul committed
41
program::program(program&&) noexcept = default;
Shucai Xiao's avatar
Shucai Xiao committed
42
program::~program() noexcept         = default;
Paul's avatar
Paul committed
43

44
// copy constructor
Shucai Xiao's avatar
Shucai Xiao committed
45
program::program(const program& p) { assign(p); }
46
47

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

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

65
    impl->ctx         = p.impl->ctx;
Shucai Xiao's avatar
Shucai Xiao committed
66
67
    impl->target_name = p.impl->target_name;
    impl->modules     = p.impl->modules;
Shucai Xiao's avatar
Shucai Xiao committed
68
69
70
71

    // 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
72
73
74
75
76
    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
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92

    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)
    {
93
        for(auto ins : iterator_for(mp.second))
Shucai Xiao's avatar
Shucai Xiao committed
94
95
            instruction::replace_refs(ins, ins_map, mod_map);
    }
96
97
}

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

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

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

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

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

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

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

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

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

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

148
149
    options.trace(*this);
    options.trace();
Shucai Xiao's avatar
Shucai Xiao committed
150

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

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

156
157
    // Validate and finalize
    for(const auto& mod : reverse(mods))
Paul's avatar
Paul committed
158
    {
Shucai Xiao's avatar
Shucai Xiao committed
159
160
161
162
163
164
        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)));
        }
165
166
167
168
169
170
171
        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
172
        mod->finalize(this->impl->ctx);
Paul's avatar
Paul committed
173
    }
Paul's avatar
Paul committed
174
175
176
177
}

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

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

            const auto& mod_args = ins->module_inputs();
            auto module_eval     = [&](module_ref smod,
                                   const std::unordered_map<std::string, argument>& inputs) {
244
                return generic_eval(smod, ctx, inputs, results, make_trace);
Shucai Xiao's avatar
Shucai Xiao committed
245
246
            };

Shucai Xiao's avatar
Shucai Xiao committed
247
248
249
250
            results.emplace(ins, trace(ins, [&] {
                                return ins->normalized_operator().compute(
                                    ctx, ins->get_shape(), values, mod_args, module_eval);
                            }));
Paul's avatar
Paul committed
251
        }
252
        assert(results.find(ins) != results.end());
Paul's avatar
Paul committed
253
    }
Shucai Xiao's avatar
Shucai Xiao committed
254
    return {results.at(std::prev(mod->end()))};
Paul's avatar
Paul committed
255
256
}

Shucai Xiao's avatar
Shucai Xiao committed
257
258
259
260
template <class F>
std::vector<argument> generic_eval(const program& p,
                                   context& ctx,
                                   std::unordered_map<std::string, argument> params,
261
                                   F make_trace)
Shucai Xiao's avatar
Shucai Xiao committed
262
{
Shucai Xiao's avatar
Shucai Xiao committed
263
    const module* mm = p.get_main_module();
264
    return generic_eval(mm, ctx, params, {}, make_trace);
Shucai Xiao's avatar
Shucai Xiao committed
265
266
}

267
std::vector<argument> program::eval(parameter_map params) const
Paul's avatar
Paul committed
268
{
Paul's avatar
Paul committed
269
270
    auto& ctx = this->impl->ctx;
#ifndef NDEBUG
271
272
273
274
275
276
277
278
279
280
281
    auto with_check_context = [&](auto f) {
        return [=, &ctx](auto&&) {
            auto sctx          = std::make_shared<context>(ctx);
            auto check_context = [=, &ctx](auto g) {
                assert(is_shared(ctx, *sctx));
                auto x = g();
                *sctx  = ctx;
                return x;
            };
            return [=](auto&&... xs) { return f(xs..., check_context); };
        };
Paul's avatar
Paul committed
282
283
    };
#else
284
285
286
287
288
    auto with_check_context = [](auto f) {
        return [=](auto&&) {
            return [=](auto&&... xs) { return f(xs..., [](auto g) { return g(); }); };
        };
    };
Paul's avatar
Paul committed
289
#endif
Paul's avatar
Paul committed
290
291
292
293

    auto trace_level = value_of(MIGRAPHX_TRACE_EVAL{});

    if(trace_level > 0)
Paul's avatar
Paul committed
294
    {
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
        return generic_eval(*this,
                            ctx,
                            std::move(params),
                            with_check_context([&](auto& ins, auto f, auto&& check_context) {
                                ctx.finish();
                                std::cout << "Run instruction: ";
                                this->debug_print(ins);
                                timer t{};
                                auto result = check_context(f);
                                double t1   = t.record<milliseconds>();
                                ctx.finish();
                                double t2 = t.record<milliseconds>();
                                std::cout << "Time: " << t1 << "ms, " << t2 << "ms" << std::endl;
                                if(trace_level > 1 and ins->name().front() != '@' and
                                   ins->name() != "load")
                                    std::cout << "Output: " << result << std::endl;
                                return result;
                            }));
Paul's avatar
Paul committed
313
314
315
    }
    else
    {
316
317
318
319
320
321
        return generic_eval(*this,
                            ctx,
                            std::move(params),
                            with_check_context([&](auto&, auto f, auto&& check_context) {
                                return check_context(f);
                            }));
Paul's avatar
Paul committed
322
    }
Paul's avatar
Paul committed
323
324
}

325
const int program_file_version = 5;
326
327
328
329
330
331
332
333

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
334

335
    value module_vals = value::object{};
Shucai Xiao's avatar
Shucai Xiao committed
336
    std::unordered_map<instruction_ref, std::string> names;
337
    for(auto& mod : this->get_modules())
Shucai Xiao's avatar
Shucai Xiao committed
338
    {
Shucai Xiao's avatar
Shucai Xiao committed
339
340
        value mod_val;
        value nodes;
341
342
        mod_val["name"] = mod->name();
        names           = mod->print(
Shucai Xiao's avatar
Shucai Xiao committed
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
            [&](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;

377
        module_vals[mod->name()] = mod_val;
Shucai Xiao's avatar
Shucai Xiao committed
378
    }
Shucai Xiao's avatar
Shucai Xiao committed
379
380
381

    result["modules"] = module_vals;

382
383
    return result;
}
Shucai Xiao's avatar
Shucai Xiao committed
384

Shucai Xiao's avatar
Shucai Xiao committed
385
386
387
388
389
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)
{
390
    const auto& module_val = v.at(mod->name());
Shucai Xiao's avatar
Shucai Xiao committed
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
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
    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;
    }
}

452
453
454
455
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
456
457
458
459
    {
        MIGRAPHX_THROW("Warning: Program version mismatch");
    }

460
461
462
463
464
465
466
467
    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
468
469
    auto module_vals = v.at("modules");
    for(const auto& vv : module_vals)
470
    {
471
        const auto& name = vv.get_key();
Shucai Xiao's avatar
Shucai Xiao committed
472
473
        if(name == "main")
            continue;
474
        impl->modules.emplace(name, name);
475
    }
476
    std::unordered_map<std::string, module_ref> map_mods;
Paul's avatar
Paul committed
477
478
479
480
    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
481
482
483
484
485

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

486
487
488
    this->finalize();
}

Paul's avatar
Paul committed
489
490
491
double common_average(const std::vector<double>& v)
{
    std::size_t n = v.size() / 4;
Paul's avatar
Paul committed
492
493
    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
494
495
}

Paul Fultz II's avatar
Paul Fultz II committed
496
497
498
499
500
501
502
503
std::string perf_group(const operation& op)
{
    auto attr = op.attributes();
    if(attr.contains("group"))
        return attr.at("group").to<std::string>();
    return op.name();
}

Paul's avatar
Paul committed
504
505
void program::perf_report(std::ostream& os, std::size_t n, parameter_map params) const
{
506
    auto& ctx = this->impl->ctx;
Paul's avatar
Paul committed
507
508
    // Run once by itself
    eval(params);
Paul's avatar
Paul committed
509
    ctx.finish();
Paul's avatar
Paul committed
510
    // Run and time entire program
Paul's avatar
Paul committed
511
512
    std::vector<double> total_vec;
    total_vec.reserve(n);
Paul's avatar
Paul committed
513
    for(std::size_t i = 0; i < n; i++)
Paul's avatar
Paul committed
514
    {
Paul's avatar
Paul committed
515
516
517
518
        total_vec.push_back(time<milliseconds>([&] {
            eval(params);
            ctx.finish();
        }));
Paul's avatar
Paul committed
519
    }
Paul's avatar
Paul committed
520
521
    std::sort(total_vec.begin(), total_vec.end());
    std::unordered_map<instruction_ref, std::vector<double>> ins_vec;
Paul's avatar
Paul committed
522
    // Fill the map
523
    generic_eval(*this, ctx, params, always([&](auto ins, auto) {
Paul's avatar
Paul committed
524
        ins_vec[ins].reserve(n);
Paul's avatar
Paul committed
525
        return argument{};
526
    }));
Paul's avatar
Paul committed
527
    // Run and time each instruction
Paul's avatar
Paul committed
528
    for(std::size_t i = 0; i < n; i++)
Paul's avatar
Paul committed
529
    {
530
        generic_eval(*this, ctx, params, always([&](auto ins, auto f) {
531
            argument result;
Paul's avatar
Paul committed
532
533
534
535
            ins_vec[ins].push_back(time<milliseconds>([&] {
                result = f();
                ctx.finish();
            }));
536
            return result;
537
        }));
Paul's avatar
Paul committed
538
    }
Paul's avatar
Paul committed
539
540
    for(auto&& p : ins_vec)
        std::sort(p.second.begin(), p.second.end());
Paul's avatar
Paul committed
541
    // Run and time implicit overhead
Paul's avatar
Paul committed
542
543
    std::vector<double> overhead_vec;
    overhead_vec.reserve(n);
Paul's avatar
Paul committed
544
    for(std::size_t i = 0; i < n; i++)
Paul's avatar
Paul committed
545
    {
Paul's avatar
Paul committed
546
        overhead_vec.push_back(time<milliseconds>([&] { dry_run(params); }));
Paul's avatar
Paul committed
547
548
    }

Paul's avatar
Paul committed
549
    double total_time             = common_average(total_vec);
Paul's avatar
Paul committed
550
    double rate                   = 1000.0 / total_time;
Paul's avatar
Paul committed
551
    double overhead_time          = common_average(overhead_vec);
Paul's avatar
Paul committed
552
    double overhead_percent       = overhead_time * 100.0 / total_time;
Paul's avatar
Paul committed
553
    double total_instruction_time = 0.0;
Paul's avatar
Paul committed
554
    std::unordered_map<std::string, double> op_times;
Paul's avatar
Paul committed
555
    for(auto&& p : ins_vec)
Paul's avatar
Paul committed
556
557
    {
        double avg = common_average(p.second);
Paul Fultz II's avatar
Paul Fultz II committed
558
        op_times[perf_group(p.first->get_operator())] += avg;
Paul's avatar
Paul committed
559
560
        total_instruction_time += avg;
    }
Paul's avatar
Paul committed
561
562
    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
563

Shucai Xiao's avatar
Shucai Xiao committed
564
565
566
    std::unordered_map<instruction_ref, std::string> names;
    this->print(names, [&](auto ins, auto ins_names) {
        instruction::print(std::cout, ins, ins_names);
567
568
569
570
571

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

Paul's avatar
Paul committed
572
573
574
        double avg     = common_average(ins_vec[ins]);
        double percent = std::ceil(100.0 * avg / total_instruction_time);
        os << ": " << avg << "ms, " << percent << "%";
575
        os << std::endl;
Paul's avatar
Paul committed
576
    });
Paul's avatar
Paul committed
577
578
579

    os << std::endl;
    os << "Summary:" << std::endl;
580
581
582
583
584
585
586
    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
587
    {
588
589
        auto&& name    = p.second;
        double avg     = p.first;
Paul's avatar
Paul committed
590
591
592
593
594
        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
595

Paul's avatar
Paul committed
596
    os << "Rate: " << rate << "/sec" << std::endl;
Paul's avatar
Paul committed
597
598
    os << "Total time: " << total_time << "ms" << std::endl;
    os << "Total instructions time: " << total_instruction_time << "ms" << std::endl;
Paul's avatar
Paul committed
599
600
601
602
    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
603
604
}

Paul's avatar
Paul committed
605
606
void program::debug_print() const { std::cout << *this << std::endl; }
void program::debug_print(instruction_ref ins) const
Paul's avatar
Paul committed
607
{
Shucai Xiao's avatar
Shucai Xiao committed
608
    std::unordered_map<instruction_ref, std::string> names;
609
    if(std::any_of(this->impl->modules.begin(), this->impl->modules.end(), [&](const auto& pp) {
610
           return is_end(pp.second.end(), ins);
Shucai Xiao's avatar
Shucai Xiao committed
611
       }))
Paul's avatar
Paul committed
612
613
614
615
    {
        std::cout << "End instruction" << std::endl;
        return;
    }
616
617
    else if(std::none_of(this->impl->modules.begin(),
                         this->impl->modules.end(),
618
                         [&](const auto& pp) { return pp.second.has_instruction(ins); }))
Paul's avatar
Paul committed
619
620
621
622
    {
        std::cout << "Instruction not part of program" << std::endl;
        return;
    }
Shucai Xiao's avatar
Shucai Xiao committed
623

Paul's avatar
Paul committed
624
    std::stringstream ss;
Shucai Xiao's avatar
Shucai Xiao committed
625
    this->print(names, [&](auto x, auto ins_names) {
Paul's avatar
Paul committed
626
        if(x == ins)
Paul's avatar
Paul committed
627
        {
Shucai Xiao's avatar
Shucai Xiao committed
628
            instruction::print(std::cout, x, ins_names);
Paul's avatar
Paul committed
629
630
631
632
633
            std::cout << std::endl;
        }
    });
}

Shucai Xiao's avatar
Shucai Xiao committed
634
635
636
637
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
638
{
639
    for(const auto& pp : this->impl->modules)
640
    {
641
        names = pp.second.print(print_func, names);
642
643
644
    }
}

Shucai Xiao's avatar
Shucai Xiao committed
645
void program::print_graph(std::ostream& os, bool brief) const
646
{
Shucai Xiao's avatar
Shucai Xiao committed
647
648
    const auto* mm = this->get_main_module();
    mm->print_graph(os, brief);
649
650
651
652
}

void program::print_cpp(std::ostream& os) const
{
Shucai Xiao's avatar
Shucai Xiao committed
653
654
655
656
657
658
659
660
    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;
    }
661
662
}

Paul's avatar
Paul committed
663
664
void program::dry_run(std::unordered_map<std::string, argument> params) const
{
Paul's avatar
Paul committed
665
    auto& ctx = this->impl->ctx;
666
    generic_eval(*this, ctx, std::move(params), always([](auto&&...) { return argument{}; }));
Paul's avatar
Paul committed
667
668
}

Shucai Xiao's avatar
Shucai Xiao committed
669
void program::annotate(std::ostream& os, const std::function<void(instruction_ref)>& a) const
Paul's avatar
Paul committed
670
{
671
    for(auto& pp : this->impl->modules)
Shucai Xiao's avatar
Shucai Xiao committed
672
    {
673
674
        std::cout << pp.first << ":" << std::endl;
        pp.second.annotate(os, a);
Shucai Xiao's avatar
Shucai Xiao committed
675
    }
Paul's avatar
Paul committed
676
677
}

Paul's avatar
Paul committed
678
const module* program::get_module(const std::string& name) const { return &impl->modules.at(name); }
Shucai Xiao's avatar
Shucai Xiao committed
679
680
681

module* program::create_module(const std::string& name)
{
682
    assert(not contains(impl->modules, name));
683
684
    auto r = impl->modules.emplace(name, name);
    return &(r.first->second);
Shucai Xiao's avatar
Shucai Xiao committed
685
686
}

Paul's avatar
Paul committed
687
module* program::get_module(const std::string& name) { return &impl->modules.at(name); }
Shucai Xiao's avatar
Shucai Xiao committed
688
689
690
691
692

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
693
template <class T>
694
std::vector<T*> generic_get_modules(T* mm)
Shucai Xiao's avatar
Shucai Xiao committed
695
{
696
    std::vector<T*> vec_modules;
Shucai Xiao's avatar
Shucai Xiao committed
697
698
699
700
701
    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
702

Paul's avatar
Paul committed
703
template <class Map, class T, class OutputIterator>
704
void generic_get_unused_modules(Map& m, const std::vector<T*>& mods, OutputIterator out)
Shucai Xiao's avatar
Shucai Xiao committed
705
{
706
707
708
709
    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
710
711
712
713
714
    transform_if(m.begin(),
                 m.end(),
                 out,
                 [&](auto&& pp) { return not contains(used, pp.first); },
                 [](auto&& pp) { return &pp.second; });
715
}
Shucai Xiao's avatar
Shucai Xiao committed
716

717
718
719
720
721
722
723
724
725
726
727
728
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
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
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");
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781

    // if an instruction has an input out side of the current module, need to remove
    // the instruction from its input's outputs
    auto& mod = impl->modules.at(name);
    for(auto ins : iterator_for(mod))
    {
        auto inputs = ins->inputs();
        for(auto in : inputs)
        {
            if(not mod.has_instruction(in))
            {
                in->remove_output(ins);
            }
        }
    }

782
783
784
785
786
787
788
789
790
791
792
793
    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());
}

794
795
program& program::sort()
{
796
    for(auto& pp : this->impl->modules)
Shucai Xiao's avatar
Shucai Xiao committed
797
    {
798
        pp.second.sort();
Shucai Xiao's avatar
Shucai Xiao committed
799
800
    }

801
802
803
    return *this;
}

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

Paul's avatar
Paul committed
806
std::ostream& operator<<(std::ostream& os, const program& p)
Paul's avatar
Paul committed
807
{
Shucai Xiao's avatar
Shucai Xiao committed
808
809
810
    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
811
    {
Shucai Xiao's avatar
Shucai Xiao committed
812
813
814
815
816
817
818
        os << "module: \"" << mod->name() << "\"" << std::endl;
        names = mod->print(
            [&](auto ins, auto ins_names) {
                instruction::print(os, ins, ins_names);
                os << std::endl;
            },
            names);
819
        os << std::endl;
Shucai Xiao's avatar
Shucai Xiao committed
820
821
    }

Paul's avatar
Paul committed
822
    return os;
Paul's avatar
Paul committed
823
}
Paul's avatar
Paul committed
824

Paul's avatar
Paul committed
825
} // namespace MIGRAPHX_INLINE_NS
Paul's avatar
Paul committed
826
} // namespace migraphx