program.cpp 35.4 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2015-2022 Advanced Micro Devices, Inc. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
Paul's avatar
Paul committed
24
25
26
#include <migraphx/program.hpp>
#include <migraphx/stringutils.hpp>
#include <migraphx/instruction.hpp>
27
#include <migraphx/op/identity.hpp>
Paul's avatar
Paul committed
28
#include <migraphx/target.hpp>
Paul's avatar
Paul committed
29
30
31
#include <migraphx/env.hpp>
#include <migraphx/ranges.hpp>
#include <migraphx/time.hpp>
32
#include <migraphx/pass_manager.hpp>
33
#include <migraphx/register_target.hpp>
Shucai Xiao's avatar
Shucai Xiao committed
34
#include <migraphx/iterator_for.hpp>
35
#include <migraphx/iterator.hpp>
36
#include <migraphx/algorithm.hpp>
37
#include <migraphx/output_iterator.hpp>
Shucai Xiao's avatar
Shucai Xiao committed
38
#include <migraphx/make_op.hpp>
39
#include <migraphx/marker.hpp>
varunsh's avatar
varunsh committed
40
#include <migraphx/supported_segments.hpp>
Paul's avatar
Paul committed
41
#include <iostream>
Paul's avatar
Paul committed
42
#include <sstream>
Paul's avatar
Paul committed
43
#include <algorithm>
44
#include <set>
Paul's avatar
Paul committed
45
#include <utility>
46

47
#include <unordered_set>
Shucai Xiao's avatar
Shucai Xiao committed
48
49
#include <map>
#include <cassert>
Paul's avatar
Paul committed
50

Paul's avatar
Paul committed
51
namespace migraphx {
Paul's avatar
Paul committed
52
inline namespace MIGRAPHX_INLINE_NS {
Paul's avatar
Paul committed
53

54
55
using milliseconds = std::chrono::duration<double, std::milli>;

Paul's avatar
Paul committed
56
57
struct program_impl
{
Shucai Xiao's avatar
Shucai Xiao committed
58
    // A map is used to keep references to modules of the program
59
    std::unordered_map<std::string, module> modules;
Paul's avatar
Paul committed
60
    context ctx;
61
    std::string target_name;
Paul's avatar
Paul committed
62
63
};

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

Paul's avatar
Paul committed
66
program::program(program&&) noexcept = default;
Shucai Xiao's avatar
Shucai Xiao committed
67
program::~program() noexcept         = default;
Paul's avatar
Paul committed
68

69
// copy constructor
Shucai Xiao's avatar
Shucai Xiao committed
70
program::program(const program& p) { assign(p); }
71
72

// copy assignment operator
Shucai Xiao's avatar
Shucai Xiao committed
73
program& program::operator=(program p)
74
{
Shucai Xiao's avatar
Shucai Xiao committed
75
    std::swap(p.impl, this->impl);
76
77
78
    return *this;
}

Shucai Xiao's avatar
Shucai Xiao committed
79
void program::assign(const program& p)
80
{
81
    if(not impl)
82
83
84
    {
        impl = std::make_unique<program_impl>();
    }
85
    else if(not impl->modules.empty())
86
    {
Shucai Xiao's avatar
Shucai Xiao committed
87
        impl->modules.clear();
88
    }
Shucai Xiao's avatar
Shucai Xiao committed
89

90
    impl->ctx         = p.impl->ctx;
Shucai Xiao's avatar
Shucai Xiao committed
91
92
    impl->target_name = p.impl->target_name;
    impl->modules     = p.impl->modules;
Shucai Xiao's avatar
Shucai Xiao committed
93
94
95
96

    // 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
97
98
99
100
101
    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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117

    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)
    {
118
        for(auto ins : iterator_for(mp.second))
Shucai Xiao's avatar
Shucai Xiao committed
119
120
            instruction::replace_refs(ins, ins_map, mod_map);
    }
121
122
}

Paul's avatar
Paul committed
123
shape program::get_parameter_shape(std::string name) const
Paul's avatar
Paul committed
124
{
Shucai Xiao's avatar
Shucai Xiao committed
125
126
    const auto* mm = this->get_main_module();
    return mm->get_parameter_shape(std::move(name));
Paul's avatar
Paul committed
127
128
}

129
130
std::vector<std::string> program::get_parameter_names() const
{
Shucai Xiao's avatar
Shucai Xiao committed
131
132
    const auto* mm = this->get_main_module();
    return mm->get_parameter_names();
133
134
}

mei-ye's avatar
mei-ye committed
135
136
instruction_ref program::get_parameter(std::string name) const
{
Shucai Xiao's avatar
Shucai Xiao committed
137
138
    const auto* mm = this->get_main_module();
    return mm->get_parameter(std::move(name));
mei-ye's avatar
mei-ye committed
139
140
}

Paul's avatar
Paul committed
141
142
std::unordered_map<std::string, shape> program::get_parameter_shapes() const
{
Shucai Xiao's avatar
Shucai Xiao committed
143
144
    const auto* mm = this->get_main_module();
    return mm->get_parameter_shapes();
Paul's avatar
Paul committed
145
146
}

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

149
150
std::vector<shape> program::get_output_shapes() const
{
Shucai Xiao's avatar
Shucai Xiao committed
151
152
    const auto* mm = this->get_main_module();
    return mm->get_output_shapes();
153
}
Paul's avatar
Paul committed
154

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

Paul's avatar
Paul committed
157
158
instruction_ref program::validate() const
{
Shucai Xiao's avatar
Shucai Xiao committed
159
160
    const auto* mm = this->get_main_module();
    return mm->validate();
Paul's avatar
Paul committed
161
162
}

163
164
165
166
167
168
169
170
target_assignments program::get_target_assignments(const std::vector<target>& targets,
                                                   assignment_options options)
{
    const auto m = options.metric;

    target_assignments p;

    const auto* mod = get_main_module();
varunsh's avatar
varunsh committed
171
172
173
174
175
176
177
178
    std::vector<std::pair<target, supported_segments>> target_subgraphs;
    target_subgraphs.reserve(targets.size());
    std::transform(targets.begin(),
                   targets.end(),
                   std::back_inserter(target_subgraphs),
                   [&](const auto& t) { return std::make_pair(t, t.find_supported(mod, m)); });

    for(const auto ins : iterator_for(*mod))
179
    {
varunsh's avatar
varunsh committed
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
        if(contains(p, ins))
        {
            continue;
        }

        for(const auto& [target, subgraph] : target_subgraphs)
        {
            // can't pass a structured binding into lambda in C++17 so create a variable for it
            const auto& t = target;
            for(const auto& segment : subgraph)
            {
                const auto& instructions = segment.instructions;
                if(not contains(instructions, ins))
                {
                    continue;
                }
                std::transform(instructions.begin(),
                               instructions.end(),
                               std::inserter(p, p.end()),
                               [&](auto instr) { return std::make_pair(instr, t.name()); });
            }
        }
202
203
204
205
    }
    return p;
}

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

208
void program::compile(const target& t, compile_options options)
Paul's avatar
Paul committed
209
{
210
211
212
    assert(not this->is_compiled());
    this->impl->target_name = t.name();
    this->impl->ctx         = t.get_context();
Paul's avatar
Paul committed
213
    if(enabled(MIGRAPHX_TRACE_COMPILE{}))
214
        options.trace = tracer{std::cout};
Shucai Xiao's avatar
Shucai Xiao committed
215

216
217
    options.trace(*this);
    options.trace();
Shucai Xiao's avatar
Shucai Xiao committed
218

Shucai Xiao's avatar
Shucai Xiao committed
219
    auto&& passes = t.get_passes(this->impl->ctx, options);
220
221
222
    run_passes(*this, passes, options.trace);

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

224
225
    // Validate and finalize
    for(const auto& mod : reverse(mods))
Paul's avatar
Paul committed
226
    {
Shucai Xiao's avatar
Shucai Xiao committed
227
228
229
230
231
232
        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)));
        }
233
234
235
236
237
238
239
        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
240
        mod->finalize(this->impl->ctx);
Paul's avatar
Paul committed
241
    }
Paul's avatar
Paul committed
242
243
244
245
}

void program::finalize()
{
Shucai Xiao's avatar
Shucai Xiao committed
246
247
    auto* mm = this->get_main_module();
    mm->finalize(this->impl->ctx);
Paul's avatar
Paul committed
248
249
}

250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
template <class T>
std::string classify(T x)
{
    switch(std::fpclassify(x))
    {
    case FP_INFINITE: return "inf";
    case FP_NAN: return "nan";
    case FP_NORMAL: return "normal";
    case FP_SUBNORMAL: return "subnormal";
    case FP_ZERO: return "zero";
    default: return "unknown";
    }
}

std::unordered_set<std::string> classify_argument(const argument& a)
{
    std::unordered_set<std::string> result;
    a.visit(
        [&](auto t) {
            for(const auto& x : t)
                result.insert(classify(x));
        },
        [&](const auto& xs) {
            for(const auto& x : xs)
            {
                auto r = classify_argument(x);
                result.insert(r.begin(), r.end());
            }
        });
    return result;
}

void preview_argument(std::ostream& os, const argument& a)
{
    a.visit(
        [&](auto t) {
            if(t.size() <= 10)
            {
                os << t;
            }
            else
            {
                os << to_string_range(t.begin(), t.begin() + 5);
                os << ", ..., ";
                os << to_string_range(t.end() - 5, t.end());
            }
        },
        [&](const auto& xs) {
            for(const auto& x : xs)
            {
                os << '{';
                preview_argument(os, x);
                os << '}';
            }
        });
}

Paul's avatar
Paul committed
307
template <class F>
Shucai Xiao's avatar
Shucai Xiao committed
308
std::vector<argument> generic_eval(const module* mod,
309
310
                                   context& ctx,
                                   std::unordered_map<std::string, argument> params,
Shucai Xiao's avatar
Shucai Xiao committed
311
                                   std::unordered_map<instruction_ref, argument> results,
312
                                   F make_trace)
Paul's avatar
Paul committed
313
{
Shucai Xiao's avatar
Shucai Xiao committed
314
315
    assert(mod->validate() == mod->end());
    results.reserve(mod->size() * 2);
Paul's avatar
Paul committed
316
317
    std::vector<argument> values;
    values.reserve(16);
318
    auto trace = make_trace(mod);
Shucai Xiao's avatar
Shucai Xiao committed
319
    for(auto ins : iterator_for(*mod))
Paul's avatar
Paul committed
320
    {
321
        assert(results.find(ins) == results.end());
322
323
        const auto& name = ins->name();
        if(name == "@literal")
Paul's avatar
Paul committed
324
        {
Paul's avatar
Paul committed
325
            results.emplace(ins, trace(ins, [&] { return ins->get_literal().get_argument(); }));
Paul's avatar
Paul committed
326
        }
327
        else if(name == "@param")
Paul's avatar
Paul committed
328
        {
Paul's avatar
Paul committed
329
330
331
332
333
            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);
334
                    auto param = params[param_name];
335
336
337
                    // TODO: may want to check correct number of dimensions and/or was within bounds
                    if(not ins->get_shape().dynamic() and param.get_shape() != ins->get_shape())
                    {
Paul's avatar
Paul committed
338
339
                        MIGRAPHX_THROW("Incorrect shape {" + to_string(param.get_shape()) +
                                       "} for parameter: " + param_name);
340
                    }
Paul's avatar
Paul committed
341
342
                    return param;
                }));
Paul's avatar
Paul committed
343
        }
344
        else if(name == "@outline")
Paul's avatar
Paul committed
345
        {
Paul's avatar
Paul committed
346
            results.emplace(ins, trace(ins, [&] { return argument{ins->get_shape(), nullptr}; }));
Paul's avatar
Paul committed
347
        }
348
349
350
351
352
353
354
355
356
357
358
359
360
        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
361
362
        else
        {
Paul's avatar
Paul committed
363
            values.resize(ins->inputs().size());
Paul's avatar
Paul committed
364
365
366
367
368
            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
369
370
371
372

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

Shucai Xiao's avatar
Shucai Xiao committed
377
378
379
380
            results.emplace(ins, trace(ins, [&] {
                                return ins->normalized_operator().compute(
                                    ctx, ins->get_shape(), values, mod_args, module_eval);
                            }));
Paul's avatar
Paul committed
381
        }
382
        assert(results.find(ins) != results.end());
383
384
385
386
        if(not ins->get_shape().dynamic())
        {
            assert(results.at(ins).get_shape() == ins->get_shape());
        }
Paul's avatar
Paul committed
387
    }
Shucai Xiao's avatar
Shucai Xiao committed
388
    return {results.at(std::prev(mod->end()))};
Paul's avatar
Paul committed
389
390
}

Shucai Xiao's avatar
Shucai Xiao committed
391
392
393
394
template <class F>
std::vector<argument> generic_eval(const program& p,
                                   context& ctx,
                                   std::unordered_map<std::string, argument> params,
395
                                   F make_trace)
Shucai Xiao's avatar
Shucai Xiao committed
396
{
Shucai Xiao's avatar
Shucai Xiao committed
397
    const module* mm = p.get_main_module();
398
    return generic_eval(mm, ctx, params, {}, make_trace);
Shucai Xiao's avatar
Shucai Xiao committed
399
400
}

401
std::vector<argument> program::eval(parameter_map params, execution_environment exec_env) const
Paul's avatar
Paul committed
402
{
Paul's avatar
Paul committed
403
404
    auto& ctx = this->impl->ctx;
#ifndef NDEBUG
405
406
407
408
409
410
411
412
413
414
415
    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
416
417
    };
#else
418
419
420
421
422
    auto with_check_context = [](auto f) {
        return [=](auto&&) {
            return [=](auto&&... xs) { return f(xs..., [](auto g) { return g(); }); };
        };
    };
Paul's avatar
Paul committed
423
#endif
Paul's avatar
Paul committed
424
425

    auto trace_level = value_of(MIGRAPHX_TRACE_EVAL{});
426
427
428
429
430
431
    std::vector<argument> ret;

    if(exec_env.async)
    {
        ctx.wait_for(exec_env.queue);
    }
Paul's avatar
Paul committed
432
433

    if(trace_level > 0)
Paul's avatar
Paul committed
434
    {
Shucai Xiao's avatar
Shucai Xiao committed
435
436
437
438
439
440
441
442
        std::unordered_map<instruction_ref, std::string> ins_out;
        // get instruction names
        this->print([&](auto x, auto ins_names) {
            std::stringstream ss;
            instruction::print(ss, x, ins_names);
            ins_out[x] = ss.str();
        });

443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
        ret = generic_eval(*this,
                           ctx,
                           std::move(params),
                           with_check_context([&](auto& ins, auto f, auto&& check_context) {
                               ctx.finish();
                               std::cout << "Run instruction: " << ins_out.at(ins) << std::endl;
                               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" and not result.empty())
                               {
                                   target tgt  = make_target(this->impl->target_name);
                                   auto buffer = tgt.copy_from(result);
                                   if(trace_level == 2)
                                   {
                                       std::cout << "Output has "
                                                 << to_string_range(classify_argument(buffer))
                                                 << std::endl;
                                       std::cout << "Output: ";
                                       preview_argument(std::cout, buffer);
                                       std::cout << std::endl;
                                   }
                                   else
                                   {
                                       std::cout << "Output: " << buffer << std::endl;
                                   }
                               }
                               return result;
                           }));
Paul's avatar
Paul committed
476
477
478
    }
    else
    {
479
480
481
482
483
484
        ret = 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
485
    }
486
487
488
489
490
491
492

    if(exec_env.async)
    {
        ctx.finish_on(exec_env.queue);
    }

    return ret;
Paul's avatar
Paul committed
493
494
}

495
const int program_file_version = 5;
496
497
498
499
500
501
502
503

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
504

505
    value module_vals = value::object{};
Shucai Xiao's avatar
Shucai Xiao committed
506
    std::unordered_map<instruction_ref, std::string> names;
507
    for(auto& mod : this->get_modules())
Shucai Xiao's avatar
Shucai Xiao committed
508
    {
Shucai Xiao's avatar
Shucai Xiao committed
509
510
        value mod_val;
        value nodes;
511
512
        mod_val["name"] = mod->name();
        names           = mod->print(
Shucai Xiao's avatar
Shucai Xiao committed
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
            [&](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;

547
        module_vals[mod->name()] = mod_val;
Shucai Xiao's avatar
Shucai Xiao committed
548
    }
Shucai Xiao's avatar
Shucai Xiao committed
549
550
551

    result["modules"] = module_vals;

552
553
    return result;
}
Shucai Xiao's avatar
Shucai Xiao committed
554

Shucai Xiao's avatar
Shucai Xiao committed
555
556
557
558
559
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)
{
560
    const auto& module_val = v.at(mod->name());
Shucai Xiao's avatar
Shucai Xiao committed
561
562
563
564
565
566
567
568
569
    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")
        {
Paul Fultz II's avatar
Paul Fultz II committed
570
571
572
            output = mod->insert_parameter(mod->end(),
                                           fields["parameter"].to<std::string>(),
                                           migraphx::from_value<shape>(node.at("shape")));
Shucai Xiao's avatar
Shucai Xiao committed
573
574
575
        }
        else if(name == "@literal")
        {
Paul Fultz II's avatar
Paul Fultz II committed
576
577
            output =
                mod->insert_literal(mod->end(), migraphx::from_value<literal>(node.at("literal")));
Shucai Xiao's avatar
Shucai Xiao committed
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
        }
        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())
            {
Paul Fultz II's avatar
Paul Fultz II committed
612
                output = mod->insert_instruction(mod->end(), op, inputs);
Shucai Xiao's avatar
Shucai Xiao committed
613
614
615
            }
            else
            {
Paul Fultz II's avatar
Paul Fultz II committed
616
                output = mod->insert_instruction(mod->end(), op, inputs, module_inputs);
Shucai Xiao's avatar
Shucai Xiao committed
617
618
619
620
621
622
623
            }
        }
        output->set_normalized(normalized);
        instructions[node.at("output").to<std::string>()] = output;
    }
}

624
625
626
627
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
628
629
630
631
    {
        MIGRAPHX_THROW("Warning: Program version mismatch");
    }

632
633
634
635
636
637
638
639
    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
640
641
    auto module_vals = v.at("modules");
    for(const auto& vv : module_vals)
642
    {
643
        const auto& name = vv.get_key();
Shucai Xiao's avatar
Shucai Xiao committed
644
645
        if(name == "main")
            continue;
646
        impl->modules.emplace(name, name);
647
    }
648
    std::unordered_map<std::string, module_ref> map_mods;
Paul's avatar
Paul committed
649
650
651
652
    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
653
654
655
656
657

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

658
659
660
    this->finalize();
}

Paul's avatar
Paul committed
661
662
663
double common_average(const std::vector<double>& v)
{
    std::size_t n = v.size() / 4;
Paul's avatar
Paul committed
664
665
    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
666
667
}

Paul Fultz II's avatar
Paul Fultz II committed
668
669
670
671
672
673
674
675
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();
}

676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
void program::mark(const parameter_map& params, marker&& m)
{
    auto& ctx = this->impl->ctx;
    // Run once by itself
    eval(params);
    ctx.finish();
    // Start marking
    m.mark_start(*this);
    generic_eval(*this, ctx, params, always([&](auto ins, auto f) {
        argument result;
        m.mark_start(ins);
        result = f();
        m.mark_stop(ins);
        return result;
    }));
    m.mark_stop(*this);
}

694
695
696
697
void program::perf_report(std::ostream& os,
                          std::size_t n,
                          parameter_map params,
                          std::size_t batch) const
Paul's avatar
Paul committed
698
{
699
    auto& ctx = this->impl->ctx;
Paul's avatar
Paul committed
700
701
    // Run once by itself
    eval(params);
Paul's avatar
Paul committed
702
    ctx.finish();
Paul's avatar
Paul committed
703
    // Run and time entire program
Paul's avatar
Paul committed
704
705
    std::vector<double> total_vec;
    total_vec.reserve(n);
Paul's avatar
Paul committed
706
    for(std::size_t i = 0; i < n; i++)
Paul's avatar
Paul committed
707
    {
Paul's avatar
Paul committed
708
709
710
711
        total_vec.push_back(time<milliseconds>([&] {
            eval(params);
            ctx.finish();
        }));
Paul's avatar
Paul committed
712
    }
Paul's avatar
Paul committed
713
714
    std::sort(total_vec.begin(), total_vec.end());
    std::unordered_map<instruction_ref, std::vector<double>> ins_vec;
Paul's avatar
Paul committed
715
    // Fill the map
716
    generic_eval(*this, ctx, params, always([&](auto ins, auto) {
Paul's avatar
Paul committed
717
        ins_vec[ins].reserve(n);
718
        return argument{ins->get_shape(), nullptr};
719
    }));
720

Paul's avatar
Paul committed
721
    // Run and time each instruction
Paul's avatar
Paul committed
722
    for(std::size_t i = 0; i < n; i++)
Paul's avatar
Paul committed
723
    {
724
        generic_eval(*this, ctx, params, always([&](auto ins, auto f) {
725
            argument result;
Paul's avatar
Paul committed
726
727
728
729
            ins_vec[ins].push_back(time<milliseconds>([&] {
                result = f();
                ctx.finish();
            }));
730
            return result;
731
        }));
Paul's avatar
Paul committed
732
    }
Paul's avatar
Paul committed
733
734
    for(auto&& p : ins_vec)
        std::sort(p.second.begin(), p.second.end());
Paul's avatar
Paul committed
735
    // Run and time implicit overhead
Paul's avatar
Paul committed
736
737
    std::vector<double> overhead_vec;
    overhead_vec.reserve(n);
Paul's avatar
Paul committed
738
    for(std::size_t i = 0; i < n; i++)
Paul's avatar
Paul committed
739
    {
Paul's avatar
Paul committed
740
        overhead_vec.push_back(time<milliseconds>([&] { dry_run(params); }));
Paul's avatar
Paul committed
741
742
    }

Paul's avatar
Paul committed
743
    double total_time             = common_average(total_vec);
Paul's avatar
Paul committed
744
    double rate                   = 1000.0 / total_time;
Paul's avatar
Paul committed
745
    double overhead_time          = common_average(overhead_vec);
Paul's avatar
Paul committed
746
    double overhead_percent       = overhead_time * 100.0 / total_time;
Paul's avatar
Paul committed
747
    double total_instruction_time = 0.0;
Paul's avatar
Paul committed
748
    std::unordered_map<std::string, double> op_times;
749
    std::unordered_map<std::string, std::size_t> op_n;
Paul's avatar
Paul committed
750
    for(auto&& p : ins_vec)
Paul's avatar
Paul committed
751
752
    {
        double avg = common_average(p.second);
Paul Fultz II's avatar
Paul Fultz II committed
753
        op_times[perf_group(p.first->get_operator())] += avg;
Paul's avatar
Paul committed
754
        total_instruction_time += avg;
755
        op_n[perf_group(p.first->get_operator())]++;
Paul's avatar
Paul committed
756
    }
Paul's avatar
Paul committed
757
758
    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
759

Shucai Xiao's avatar
Shucai Xiao committed
760
761
762
    std::unordered_map<instruction_ref, std::string> names;
    this->print(names, [&](auto ins, auto ins_names) {
        instruction::print(std::cout, ins, ins_names);
763
764
765
766
767

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

Paul's avatar
Paul committed
768
769
770
        double avg     = common_average(ins_vec[ins]);
        double percent = std::ceil(100.0 * avg / total_instruction_time);
        os << ": " << avg << "ms, " << percent << "%";
771
        os << std::endl;
Paul's avatar
Paul committed
772
    });
Paul's avatar
Paul committed
773
774
775

    os << std::endl;
    os << "Summary:" << std::endl;
776
777
778
779
780
781
    std::vector<std::tuple<double, std::size_t, std::string>> op_times_sorted;
    std::transform(
        op_times.begin(), op_times.end(), std::back_inserter(op_times_sorted), [&](auto p) {
            auto&& name = p.first;
            return std::make_tuple(p.second, op_n.at(name), name);
        });
782
    std::sort(op_times_sorted.begin(), op_times_sorted.end(), std::greater<>{});
783
    for(auto&& [avg, nn, name] : op_times_sorted)
Paul's avatar
Paul committed
784
785
    {
        double percent = std::ceil(100.0 * avg / total_instruction_time);
786
787
788
        double per_ins = avg / nn;
        os << name << ": " << avg << "ms / " << nn << " = " << per_ins << "ms, " << percent << "%"
           << std::endl;
Paul's avatar
Paul committed
789
790
791
    }

    os << std::endl;
Paul's avatar
Paul committed
792

793
794
    os << "Batch size: " << batch << std::endl;
    os << "Rate: " << rate * batch << "/sec" << std::endl;
Paul's avatar
Paul committed
795
796
    os << "Total time: " << total_time << "ms" << std::endl;
    os << "Total instructions time: " << total_instruction_time << "ms" << std::endl;
Paul's avatar
Paul committed
797
798
799
800
    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
801
802
}

Paul's avatar
Paul committed
803
804
void program::debug_print() const { std::cout << *this << std::endl; }
void program::debug_print(instruction_ref ins) const
Paul's avatar
Paul committed
805
{
Shucai Xiao's avatar
Shucai Xiao committed
806
    std::unordered_map<instruction_ref, std::string> names;
807
    if(std::any_of(this->impl->modules.begin(), this->impl->modules.end(), [&](const auto& pp) {
808
           return is_end(pp.second.end(), ins);
Shucai Xiao's avatar
Shucai Xiao committed
809
       }))
Paul's avatar
Paul committed
810
811
812
813
    {
        std::cout << "End instruction" << std::endl;
        return;
    }
814
815
    else if(std::none_of(this->impl->modules.begin(),
                         this->impl->modules.end(),
816
                         [&](const auto& pp) { return pp.second.has_instruction(ins); }))
Paul's avatar
Paul committed
817
818
819
820
    {
        std::cout << "Instruction not part of program" << std::endl;
        return;
    }
Shucai Xiao's avatar
Shucai Xiao committed
821

Paul's avatar
Paul committed
822
    std::stringstream ss;
Shucai Xiao's avatar
Shucai Xiao committed
823
    this->print(names, [&](auto x, auto ins_names) {
Paul's avatar
Paul committed
824
        if(x == ins)
Paul's avatar
Paul committed
825
        {
Shucai Xiao's avatar
Shucai Xiao committed
826
            instruction::print(std::cout, x, ins_names);
Paul's avatar
Paul committed
827
828
829
830
831
            std::cout << std::endl;
        }
    });
}

Shucai Xiao's avatar
Shucai Xiao committed
832
833
834
835
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
836
{
837
    for(const auto& pp : this->impl->modules)
838
    {
839
        names = pp.second.print(print_func, names);
840
841
842
    }
}

Shucai Xiao's avatar
Shucai Xiao committed
843
844
845
846
847
848
849
850
void program::print(
    const std::function<void(instruction_ref ins,
                             std::unordered_map<instruction_ref, std::string>)>& print_func) const
{
    std::unordered_map<instruction_ref, std::string> names;
    this->print(names, print_func);
}

Shucai Xiao's avatar
Shucai Xiao committed
851
void program::print_graph(std::ostream& os, bool brief) const
852
{
Shucai Xiao's avatar
Shucai Xiao committed
853
854
    const auto* mm = this->get_main_module();
    mm->print_graph(os, brief);
855
856
}

857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
void program::print_py(std::ostream& os) const
{
    auto vec_modules = this->get_modules();
    std::unordered_map<instruction_ref, std::string> names;
    os << "p = migraphx.program()\n";
    for(auto& mod : vec_modules)
    {
        std::string var_name = "m" + mod->name();
        os << var_name << " = ";
        if(mod->name() == "main")
            os << "p.get_main_module()";
        else
            os << "p.create_module(\"" << mod->name() << "\");";
        os << std::endl;
        names = mod->print_py(os, var_name, names);
        os << std::endl;
    }
}

876
877
void program::print_cpp(std::ostream& os) const
{
Shucai Xiao's avatar
Shucai Xiao committed
878
879
    auto vec_modules = this->get_modules();
    std::unordered_map<instruction_ref, std::string> names;
880
    os << "migraphx::program p;\n";
Shucai Xiao's avatar
Shucai Xiao committed
881
882
    for(auto& mod : vec_modules)
    {
883
884
885
886
887
888
889
890
        std::string var_name = "m" + mod->name();
        os << "migraphx::module_ref " << var_name << " = ";
        if(mod->name() == "main")
            os << "p.get_main_module();";
        else
            os << "p.create_module(\"" << mod->name() << "\");";
        os << std::endl;
        names = mod->print_cpp(os, var_name, names);
Shucai Xiao's avatar
Shucai Xiao committed
891
892
        os << std::endl;
    }
893
894
}

Paul's avatar
Paul committed
895
896
void program::dry_run(std::unordered_map<std::string, argument> params) const
{
Paul's avatar
Paul committed
897
    auto& ctx = this->impl->ctx;
898
899
900
    generic_eval(*this, ctx, std::move(params), always([](auto ins, auto&&...) {
        return argument{ins->get_shape(), nullptr};
    }));
Paul's avatar
Paul committed
901
902
}

Shucai Xiao's avatar
Shucai Xiao committed
903
void program::annotate(std::ostream& os, const std::function<void(instruction_ref)>& a) const
Paul's avatar
Paul committed
904
{
905
    for(auto& pp : this->impl->modules)
Shucai Xiao's avatar
Shucai Xiao committed
906
    {
907
908
        std::cout << pp.first << ":" << std::endl;
        pp.second.annotate(os, a);
Shucai Xiao's avatar
Shucai Xiao committed
909
    }
Paul's avatar
Paul committed
910
911
}

Paul's avatar
Paul committed
912
const module* program::get_module(const std::string& name) const { return &impl->modules.at(name); }
Shucai Xiao's avatar
Shucai Xiao committed
913
914
915

module* program::create_module(const std::string& name)
{
916
    assert(not contains(impl->modules, name));
917
918
    auto r = impl->modules.emplace(name, name);
    return &(r.first->second);
Shucai Xiao's avatar
Shucai Xiao committed
919
920
}

Paul's avatar
Paul committed
921
module* program::get_module(const std::string& name) { return &impl->modules.at(name); }
Shucai Xiao's avatar
Shucai Xiao committed
922
923
924
925
926

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
927
template <class T>
928
std::vector<T*> generic_get_modules(T* mm)
Shucai Xiao's avatar
Shucai Xiao committed
929
{
930
    std::vector<T*> vec_modules;
Shucai Xiao's avatar
Shucai Xiao committed
931
932
933
934
935
    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
936

Paul's avatar
Paul committed
937
template <class Map, class T, class OutputIterator>
938
void generic_get_unused_modules(Map& m, const std::vector<T*>& mods, OutputIterator out)
Shucai Xiao's avatar
Shucai Xiao committed
939
{
940
941
942
943
    std::unordered_set<std::string> used;
    std::transform(mods.begin(), mods.end(), std::inserter(used, used.end()), [](auto&& mod) {
        return mod->name();
    });
bpickrel's avatar
bpickrel committed
944
945
946
947
948
949
    transform_if(
        m.begin(),
        m.end(),
        out,
        [&](auto&& pp) { return not contains(used, pp.first); },
        [](auto&& pp) { return &pp.second; });
950
}
Shucai Xiao's avatar
Shucai Xiao committed
951

952
953
954
955
956
957
958
959
960
961
962
963
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
964
965
}

966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
template <class Module, class Map>
void generic_insert_module_tree(Module* pm, Map& m)
{
    for(auto* sm : pm->get_sub_modules(true))
    {
        m.insert(std::make_pair(sm, pm));
        generic_insert_module_tree(sm, m);
    }
}

std::unordered_multimap<module_ref, module_ref> program::get_module_tree()
{
    std::unordered_multimap<module_ref, module_ref> result;
    generic_insert_module_tree(this->get_main_module(), result);
    return result;
}

983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
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");
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033

    // 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);
            }
        }
    }

1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
    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());
}

1046
1047
program& program::sort()
{
1048
    for(auto& pp : this->impl->modules)
Shucai Xiao's avatar
Shucai Xiao committed
1049
    {
1050
        pp.second.sort();
Shucai Xiao's avatar
Shucai Xiao committed
1051
1052
    }

1053
1054
1055
    return *this;
}

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

Paul's avatar
Paul committed
1058
std::ostream& operator<<(std::ostream& os, const program& p)
Paul's avatar
Paul committed
1059
{
Shucai Xiao's avatar
Shucai Xiao committed
1060
1061
1062
    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
1063
    {
Shucai Xiao's avatar
Shucai Xiao committed
1064
1065
1066
1067
1068
1069
1070
        os << "module: \"" << mod->name() << "\"" << std::endl;
        names = mod->print(
            [&](auto ins, auto ins_names) {
                instruction::print(os, ins, ins_names);
                os << std::endl;
            },
            names);
1071
        os << std::endl;
Shucai Xiao's avatar
Shucai Xiao committed
1072
1073
    }

Paul's avatar
Paul committed
1074
    return os;
Paul's avatar
Paul committed
1075
}
Paul's avatar
Paul committed
1076

Paul's avatar
Paul committed
1077
} // namespace MIGRAPHX_INLINE_NS
Paul's avatar
Paul committed
1078
} // namespace migraphx