opr_impl.cpp 32.3 KB
Newer Older
1 2 3 4
/**
 * \file dnn/src/fallback/convolution/opr_impl.cpp
 * MegEngine is Licensed under the Apache License, Version 2.0 (the "License")
 *
5
 * Copyright (c) 2014-2021 Megvii Inc. All rights reserved.
6 7 8
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
9 10
 * "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or
 * implied.
11 12 13 14 15 16 17
 */

#include "src/common/algo_chooser.h"
#include "src/common/metahelper.h"
#include "src/common/opr_delegate.h"
#include "src/common/utils.h"
#include "src/fallback/convolution/algos.h"
18
#include "src/fallback/convolution/opr_impl.h"
19 20 21 22 23 24
#include "src/fallback/convolution/run_conv.h"
#include "src/naive/convolution/helper.h"
#include "src/naive/handle.h"

#include "midout.h"

25 26 27 28
#if MEGDNN_AARCH64 || MEGDNN_ARMV7
#include "src/arm_common/convolution/opr_impl.h"
#endif

29
#include <cstring>
30
#include <unordered_map>
31 32 33 34 35 36 37 38 39 40 41

MIDOUT_DECL(megdnn_fb_convbwd_float)

using namespace megdnn;
using namespace fallback;

namespace {
template <typename T>
void incr_ptr(T*& dst, ptrdiff_t delta) {
    dst = reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(dst) + delta);
}
42

43 44 45 46 47 48
}  // namespace

class ConvolutionImpl::AlgoPack : NonCopyableObj {
    AlgoFallback algo_fallback;
    AlgoNaive algo_naive;
    SmallVector<std::unique_ptr<AlgoBase>> refhold;
49 50
    SmallVector<AlgoBase*> m_all_algos;
    AlgoBase::Mapper m_all_algos_map;
51 52 53 54 55
public:
    AlgoPack() {
        static CpuOprDelegationStorage<1> storage;
        auto conv_bias_opr = storage.get<ConvBias, 0>();
        auto&& conv_bias_algo =
56
                static_cast<ConvBiasImpl*>(conv_bias_opr)->get_all_packed_algo();
57 58
        for (auto&& algorithm : conv_bias_algo) {
            // fallback algo
59
            refhold.emplace_back(new AlgoDefault(algorithm));
60
            m_all_algos.emplace_back(refhold.back().get());
61 62
        }

63 64 65 66 67 68
        m_all_algos.emplace_back(&algo_fallback);
        m_all_algos.emplace_back(&algo_naive);

        for (auto&& algo : m_all_algos) {
            m_all_algos_map.emplace(algo->info().desc, algo);
        }
69
    }
70 71 72

    const SmallVector<AlgoBase*>& all_algos() const { return m_all_algos; }
    const AlgoBase::Mapper& all_algos_map() const { return m_all_algos_map; }
73 74
};

75 76 77 78 79 80 81
const ConvolutionImpl::AlgoPack& ConvolutionImpl::algo_pack() {
    static AlgoPack algo_pack;
    return algo_pack;
}

SmallVector<ConvolutionImpl::AlgoBase*> ConvolutionImpl::get_all_packed_algo() {
    return algo_pack().all_algos();
82
}
83 84 85 86 87 88

SmallVector<ConvolutionImpl::AlgoBase*> ConvolutionImpl::select_algo_type(
        ConvAlgoTypePack target_type) {
    megdnn_assert(nr_type_contain(target_type.data_type),
                  "ConvBias algo selection only support one type");
    SmallVector<ConvolutionImpl::AlgoBase*> algos;
89
    for (auto&& algo : get_all_packed_algo()) {
90 91 92 93 94 95 96 97 98
        auto algo_type = algo->get_algo_type();
        if (contain_data_type(algo_type.data_type, target_type.data_type) &&
            algo_type.algo_category == target_type.algo_category) {
            algos.push_back(algo);
        }
    }
    return algos;
}

99 100 101
bool ConvolutionImpl::is_naive_algo(ConvolutionImpl::Algorithm* algo) {
    return algo == nullptr || strcmp(algo->name(), "DEFAULT") == 0;
}
102 103

#define NCB_ALGO_FUNC(name, algo, param) \
104
    static_cast<AlgoBase*>(algo)->name(param)
105

106 107
void ConvolutionImpl::exec(_megdnn_tensor_in src, _megdnn_tensor_in filter,
                           _megdnn_tensor_out dst,
108
                           const PreprocessedFilter* preprocessed_filter,
109
                           _megdnn_workspace workspace) {
110 111
    auto fparam = make_ncb_kern_param(src, filter, dst, preprocessed_filter,
                                      workspace);
112
    auto&& algo = get_algorithm(fparam, workspace.size);
113
    if (!is_naive_algo(algo) &&
114
        NCB_ALGO_FUNC(get_workspace, algo, fparam) <= workspace.size) {
115 116
        exec_with_ncb_kern(fparam, algo);
    } else {
117 118
        naive::ConvolutionForwardImpl::exec(src, filter, dst,
                                            preprocessed_filter, workspace);
119 120 121
    }
}

122
void ConvolutionImpl::exec_preprocess(const TensorLayout& src_layout,
123 124 125 126
                                      _megdnn_tensor_in filter,
                                      const TensorLayout& dst_layout,
                                      PreprocessedFilter* preprocessed_filter,
                                      _megdnn_workspace workspace) {
127 128 129 130 131
    //! exec_preprocess currently only support preprocess weights before exec,
    //! src/dst will be ignored, just set to nullptr
    TensorND src{nullptr, src_layout}, dst{nullptr, dst_layout};
    auto fparam = make_ncb_kern_param(src, filter, dst, preprocessed_filter,
                                      workspace);
132 133

    //! should not pass workspace_size limit otherwise can not find match algo
134 135 136 137
    auto&& algo = get_algorithm(fparam);
    if (!is_naive_algo(algo) &&
        NCB_ALGO_FUNC(get_preprocess_workspace, algo, fparam) <=
                workspace.size) {
138 139 140 141 142 143 144
        exec_preprocess_with_ncb_kern(fparam, algo);
    } else {
        naive::ConvolutionForwardImpl::exec_preprocess(
                src_layout, filter, dst_layout, preprocessed_filter, workspace);
    }
}

145 146 147 148
size_t ConvolutionImpl::get_workspace_in_bytes(
        const TensorLayout& src, const TensorLayout& filter,
        const TensorLayout& dst,
        const PreprocessedFilter* preprocessed_filter) {
149 150 151 152 153 154 155 156 157
    TensorLayoutArray layouts{src, filter, dst};
    HeuristicCache::Key key{this->handle(), this->get_opr_type(),
                            layouts.data(), layouts.size(), &this->param(),
                            sizeof(this->param())};
    auto rst = HeuristicCache::instance().get(key);
    if (rst.policy.algo.valid()) {
        return rst.workspace;
    }

158 159
    auto fparam =
            make_ncb_kern_size_param(src, filter, dst, preprocessed_filter);
160
    auto&& algo = get_algorithm(fparam);
161 162
    if (is_naive_algo(algo)) {
        return naive::ConvolutionForwardImpl::get_workspace_in_bytes(
163
                src, filter, dst, preprocessed_filter);
164
    } else {
165
        return NCB_ALGO_FUNC(get_workspace, algo, fparam);
166 167 168 169 170 171 172
    }
}

size_t ConvolutionImpl::get_preprocess_workspace_in_bytes(
        const TensorLayout& src, const TensorLayout& filter,
        const TensorLayout& dst) {
    auto fparam = make_ncb_kern_size_param(src, filter, dst, nullptr);
173
    auto&& algo = get_algorithm(fparam);
174 175 176 177
    if (is_naive_algo(algo)) {
        return naive::ConvolutionForwardImpl::get_preprocess_workspace_in_bytes(
                src, filter, dst);
    } else {
178
        return NCB_ALGO_FUNC(get_preprocess_workspace, algo, fparam);
179 180 181 182 183
    }
}

SmallVector<TensorLayout> ConvolutionImpl::deduce_preprocessed_filter_layout(
        const TensorLayout& src, const TensorLayout& filter,
184
        const TensorLayout& dst) {
185
    auto fparam = make_ncb_kern_size_param(src, filter, dst, nullptr);
186
    auto&& algo = get_algorithm(fparam);
187 188 189 190
    if (is_naive_algo(algo)) {
        return naive::ConvolutionForwardImpl::deduce_preprocessed_filter_layout(
                src, filter, dst);
    } else {
191
        return NCB_ALGO_FUNC(deduce_preprocessed_filter_layout, algo, fparam);
192 193 194 195 196 197
    }
}

std::vector<ConvolutionImpl::Algorithm*> ConvolutionImpl::get_all_algorithms(
        const TensorLayout& src, const TensorLayout& filter,
        const TensorLayout& dst) {
198
    auto fparam = make_ncb_kern_size_param(src, filter, dst, nullptr);
199 200 201 202 203 204 205 206 207 208 209
    auto ret = get_all_algorithms_with_ncb(fparam);
    if (ret.empty()) {
        return naive::ConvolutionForwardImpl::get_all_algorithms(src, filter,
                                                                 dst);
    }
    return ret;
}

ConvolutionImpl::Algorithm* ConvolutionImpl::get_algorithm_heuristic(
        const TensorLayout& src, const TensorLayout& filter,
        const TensorLayout& dst, size_t workspace_limit_in_bytes,
210 211
        const AlgoAttribute& positive_attr,
        const AlgoAttribute& negative_attr) {
212
    auto fparam = make_ncb_kern_size_param(src, filter, dst, nullptr);
213
    auto result = get_algorithm_heuristic_with_ncb(
214
            fparam, workspace_limit_in_bytes, positive_attr, negative_attr);
215 216
    if (result == nullptr) {
        result = naive::ConvolutionForwardImpl::get_algorithm_heuristic(
217 218
                src, filter, dst, workspace_limit_in_bytes, positive_attr,
                negative_attr);
219 220 221 222 223 224
    }
    return result;
}

ConvolutionImpl::NCBKernSizeParam ConvolutionImpl::make_ncb_kern_size_param(
        const TensorLayout& src, const TensorLayout& filter,
225 226
        const TensorLayout& dst,
        const PreprocessedFilter* preprocessed_filter) {
227 228 229 230 231 232 233 234
    auto safe_u32 = [](size_t v) -> uint32_t {
        megdnn_assert(v <= std::numeric_limits<uint32_t>::max(),
                      "value too large: %zu", v);
        return v;
    };
    size_t spatial_pos;
    if (param().format == Param::Format::NCHW88 ||
        param().format == Param::Format::NCHW8 ||
235
        param().format == Param::Format::NCHW4 ||
236
        param().format == Param::Format::NCHW44_DOT ||
237
        param().format == Param::Format::NCHW44) {
238
        spatial_pos = 2;
239
    } else if (param().format == Param::Format::NCHW) {
240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262
        spatial_pos = 2;
    } else if (param().format == Param::Format::NHWC) {
        spatial_pos = 1;
    } else {
        megdnn_assert(0, "invalid conv format %d",
                      static_cast<int>(param().format));
    }
    size_t nr_threads = static_cast<naive::HandleImpl*>(handle())
                                ->megcore_dispatcher()
                                ->nr_threads();

    return {safe_u32(src[0]),
            {{safe_u32(src[spatial_pos]), safe_u32(src[spatial_pos + 1])}},
            {{safe_u32(dst[spatial_pos]), safe_u32(dst[spatial_pos + 1])}},
            check_layout_fwd(src, filter, dst),
            src.dtype,
            filter.dtype,
            dst.dtype,
            src.stride[0],
            dst.stride[0],
            {src.stride[0], src.stride[1], src.stride[2], src.stride[3]},
            {dst.stride[0], dst.stride[1], dst.stride[2], dst.stride[3]},
            param().compute_mode,
263 264
            nr_threads,
            preprocessed_filter};
265 266 267 268
}

ConvolutionImpl::NCBKernParam ConvolutionImpl::make_ncb_kern_param(
        _megdnn_tensor_in src, _megdnn_tensor_in filter, _megdnn_tensor_out dst,
269
        const PreprocessedFilter* preprocessed_filter,
270 271
        _megdnn_workspace workspace) {
    NCBKernParam ret;
272 273
    static_cast<NCBKernSizeParam&>(ret) = make_ncb_kern_size_param(
            src.layout, filter.layout, dst.layout, preprocessed_filter);
274 275 276 277 278 279 280 281
    ret.src_ptr = src.raw_ptr;
    ret.filter_ptr = filter.raw_ptr;
    ret.dst_ptr = dst.raw_ptr;
    ret.workspace_ptr = workspace.raw_ptr;
    ret.workspace_size = workspace.size;
    return ret;
}

282 283
void ConvolutionImpl::exec_preprocess_with_ncb_kern(const NCBKernParam& param,
                                                    Algorithm* algo) {
284 285 286
    auto&& kerns = NCB_ALGO_FUNC(dispatch_preprocess_kern, algo, param);
    auto&& fallback_handle = handle();
    for (auto&& kernel : kerns) {
287 288 289 290
        megdnn_assert(
                param.filter_meta.format == Param::Format::NCHW ||
                        param.filter_meta.format == Param::Format::NHWC ||
                        param.filter_meta.format == Param::Format::NCHW88 ||
291 292
                        param.filter_meta.format == Param::Format::NCHW44 ||
                        param.filter_meta.format == Param::Format::NCHW44_DOT,
293 294 295 296 297 298 299 300 301 302
                "invalid conv format");
        auto run = [param, kernel](size_t index, size_t thread_id) {
            CpuNDRange ndrange_id(kernel.global_size, index);
            kernel.kern(param, {thread_id, ndrange_id});
        };
        static_cast<naive::HandleImpl*>(fallback_handle)
                ->dispatch_kern(run, kernel.global_size.total_size());
    }
}

303 304
void ConvolutionImpl::exec_with_ncb_kern(const NCBKernParam& param,
                                         Algorithm* algo) {
305 306 307
    auto&& kerns = NCB_ALGO_FUNC(dispatch_kern, algo, param);
    auto&& fallback_handle = handle();
    for (auto&& kernel : kerns) {
308 309 310 311
        megdnn_assert(
                param.filter_meta.format == Param::Format::NCHW ||
                        param.filter_meta.format == Param::Format::NHWC ||
                        param.filter_meta.format == Param::Format::NCHW88 ||
312 313
                        param.filter_meta.format == Param::Format::NCHW44 ||
                        param.filter_meta.format == Param::Format::NCHW44_DOT,
314
                "invalid conv format");
315
        auto run = [param, kernel](size_t index, size_t thread_id) {
316
            CpuNDRange ndrange_id(kernel.global_size, index);
317
            kernel.kern(param, {thread_id, ndrange_id});
318 319 320 321 322 323 324 325
        };
        static_cast<naive::HandleImpl*>(fallback_handle)
                ->dispatch_kern(run, kernel.global_size.total_size());
    }
}

ConvolutionImpl::Algorithm* ConvolutionImpl::get_algorithm_heuristic_with_ncb(
        const NCBKernSizeParam& param, size_t workspace_limit_in_bytes,
326 327
        const AlgoAttribute& positive_attr,
        const AlgoAttribute& negative_attr) {
328 329 330 331 332 333
    auto algo_data_type = param.deduce_algo_data_type();
    auto suggest_category_order = suggest_algo_category_order(param);
    for (auto category : suggest_category_order) {
        auto&& origin_algos = select_algo_type({algo_data_type, category});
        ConvolutionImpl::Algorithm* heuristic_algo = nullptr;
        for (auto i : origin_algos) {
334
            bool usable_attribute = static_cast<AlgoBase*>(i)->usable_attribute(
335 336
                    param, AlgoSelectionStrategy::HEURISTIC, positive_attr,
                    negative_attr);
337
            if (usable_attribute &&
338 339 340 341 342 343 344 345 346 347 348 349 350 351 352
                static_cast<AlgoBase*>(i)->get_workspace(param) <=
                        workspace_limit_in_bytes) {
                //! store the first usable algo if no prefer algo, choose it as
                //! the target algo
                if (!heuristic_algo) {
                    heuristic_algo = i;
                }
                //! choose the first prefer algo
                if (i->is_preferred(param)) {
                    return i;
                }
            }
        }
        if (heuristic_algo) {
            return heuristic_algo;
353 354 355 356 357 358 359 360 361
        }
    }
    return nullptr;
}

std::vector<ConvolutionImpl::Algorithm*>
ConvolutionImpl::get_all_algorithms_with_ncb(const NCBKernSizeParam& param) {
    std::vector<Algorithm*> ret;
    std::vector<Algorithm*> prefer_algos;
362
    for (auto&& i : get_all_packed_algo()) {
363 364
        if (i->usable(param, AlgoSelectionStrategy::FULL_RUN)) {
            if (i->is_preferred(param)) {
365 366 367 368 369 370 371 372 373 374
                prefer_algos.push_back(i);
            } else {
                ret.push_back(i);
            }
        }
    }
    ret.insert(ret.begin(), prefer_algos.begin(), prefer_algos.end());
    return ret;
}

375 376
ConvolutionImpl::Algorithm* ConvolutionImpl::get_algorithm_from_desc(
        const AlgorithmDesc& desc) {
377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398
    if (!desc.valid()) {
        return nullptr;
    } else {
        switch (desc.handle_type) {
            case Handle::HandleType::FALLBACK: {
                const auto& map = algo_pack().all_algos_map();
                megdnn_assert(map.find(desc) != map.end());
                return map.at(desc);
            }
            case Handle::HandleType::NAIVE: {
                auto algo = static_cast<naive::HandleImpl*>(handle())
                                    ->default_conv_fwd_algo();
                megdnn_assert(algo->info().desc == desc);
                return algo;
            }
            default:
                megdnn_throw("Unknown handle type");
                return nullptr;
        }
    }
}

399 400
ConvolutionImpl::Algorithm* ConvolutionImpl::get_algorithm(
        const NCBKernSizeParam& param, size_t workspace_size) {
401
    if (auto algo = get_algorithm_from_desc(execution_policy().algo)) {
402
        return algo;
403 404 405
    }
    if (!m_prev_selected_algo ||
        memcmp(&m_prev_selected_algo_sizep, &param, sizeof(NCBKernSizeParam))) {
406
        m_prev_selected_algo = get_algorithm_heuristic_with_ncb(
407 408
                param, workspace_size, AlgoAttribute::DEFAULT,
                AlgoAttribute::DEFAULT);
409 410 411 412 413
        m_prev_selected_algo_sizep = param;
    }
    return m_prev_selected_algo;
}

414 415 416 417 418 419 420 421 422 423
SmallVector<AlgoCategory> ConvolutionImpl::suggest_algo_category_order(
        const NCBKernSizeParam& param) const {
    static CpuOprDelegationStorage<1> storage;
    auto conv_bias_opr = storage.get<ConvBias, 0>();
    auto conv_bias_param =
            ConvolutionImpl::AlgoDefault::init_conv_bias_param(param);
    return static_cast<ConvBiasImpl*>(conv_bias_opr)
            ->suggest_algo_category_order(conv_bias_param);
}

424 425 426 427 428
const char* ConvolutionImpl::get_algorithm_set_name() const {
    // fallback version 0
    return "F0";
}

429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445
ConvolutionImpl::AlgoDataType
ConvolutionImpl::NCBKernSizeParam::deduce_algo_data_type() const {
    if (src_type.enumv() == DTypeEnum::Float32) {
        return ConvolutionImpl::AlgoDataType::FLOAT32;
#if !MEGDNN_DISABLE_FLOAT16
    } else if (src_type.enumv() == DTypeEnum::Float16) {
        return ConvolutionImpl::AlgoDataType::FLOAT16;
#endif
    } else if (src_type.enumv() == DTypeEnum::Int8 ||
               src_type.enumv() == DTypeEnum::QuantizedS8) {
        if (dst_type.enumv() == DTypeEnum::Int16) {
            return ConvolutionImpl::AlgoDataType::INT8X8X16;
        } else {
            return ConvolutionImpl::AlgoDataType::QINT8X8X32;
        }
    } else if (src_type.enumv() == DTypeEnum::Quantized8Asymm) {
        return ConvolutionImpl::AlgoDataType::QUINT8X8X32;
446 447
    } else if (src_type.enumv() == DTypeEnum::QuantizedS4) {
        return ConvolutionImpl::AlgoDataType::QINT4x4x32;
448
    } else {
M
Megvii Engine Team 已提交
449
        megdnn_throw(ssprintf("not support data type of %s * %s -> %s\n",
450 451 452 453 454
                              src_type.name(), filter_type.name(),
                              dst_type.name()));
    }
}

455 456
/* ===================== ConvolutionBackwardData ===================== */

457 458 459 460
class ConvolutionBackwardDataImpl::AlgoPack : NonCopyableObj {
    AlgoNaive algo_naive;
    AlgoDirect algo_direct;
    AlgoMatrixMul algo_matmul;
461 462
    SmallVector<AlgoBase*> m_all_algos;
    AlgoBase::Mapper m_all_algos_map;
463 464 465

public:
    AlgoPack() {
466 467 468 469 470 471 472
        m_all_algos.emplace_back(&algo_matmul);
        m_all_algos.emplace_back(&algo_direct);
        m_all_algos.emplace_back(&algo_naive);

        for (auto&& algo : m_all_algos) {
            m_all_algos_map.emplace(algo->info().desc, algo);
        }
473
    }
474 475
    const SmallVector<AlgoBase*>& all_algos() const { return m_all_algos; }
    const AlgoBase::Mapper& all_algos_map() const { return m_all_algos_map; }
476
};
477 478 479 480 481
const ConvolutionBackwardDataImpl::AlgoPack&
ConvolutionBackwardDataImpl::algo_pack() {
    static AlgoPack algo_pack;
    return algo_pack;
}
482 483

SmallVector<ConvolutionBackwardDataImpl::AlgoBase*>
484 485
ConvolutionBackwardDataImpl::get_all_packed_algo() {
    return algo_pack().all_algos();
486
}
487 488 489 490 491 492

void ConvolutionBackwardDataImpl::exec(_megdnn_tensor_in filter,
                                       _megdnn_tensor_in diff,
                                       _megdnn_tensor_out grad,
                                       _megdnn_workspace workspace) {
    if (param().format == param::Convolution::Format::NHWCD4 ||
493 494 495
        param().format == param::Convolution::Format::NCHW4 ||
        (param().format == param::Convolution::Format::NCHW &&
         grad.layout.dtype.enumv() == DTypeEnum::QuantizedS8)) {
496 497 498 499 500 501 502 503 504 505
        return naive::ConvolutionBackwardDataImpl::exec(filter, diff, grad,
                                                        workspace);
    }
    auto fparam = make_ncb_kern_param(filter, diff, grad, workspace);
    return exec_with_ncb_kern(fparam);
}

size_t ConvolutionBackwardDataImpl::get_workspace_in_bytes(
        const TensorLayout& filter, const TensorLayout& diff,
        const TensorLayout& grad) {
506 507 508 509 510 511 512 513 514
    TensorLayoutArray layouts{filter, diff, grad};
    HeuristicCache::Key key{this->handle(), this->get_opr_type(),
                            layouts.data(), layouts.size(), &this->param(),
                            sizeof(this->param())};
    auto rst = HeuristicCache::instance().get(key);
    if (rst.policy.algo.valid()) {
        return rst.workspace;
    }

515
    if (param().format == param::Convolution::Format::NHWCD4 ||
516 517 518
        param().format == param::Convolution::Format::NCHW4 ||
        (param().format == param::Convolution::Format::NCHW &&
         grad.dtype.enumv() == DTypeEnum::QuantizedS8)) {
519 520 521 522 523 524 525 526 527 528 529 530
        return naive::ConvolutionBackwardDataImpl::get_workspace_in_bytes(
                filter, diff, grad);
    }
    auto fparam = make_ncb_kern_size_param(filter, diff, grad);
    return get_workspace_with_ncb(fparam);
}

std::vector<ConvolutionBackwardDataImpl::Algorithm*>
ConvolutionBackwardDataImpl::get_all_algorithms(const TensorLayout& filter,
                                                const TensorLayout& diff,
                                                const TensorLayout& grad) {
    if (param().format == param::Convolution::Format::NHWCD4 ||
531 532 533
        param().format == param::Convolution::Format::NCHW4 ||
        (param().format == param::Convolution::Format::NCHW &&
         grad.dtype.enumv() == DTypeEnum::QuantizedS8)) {
534 535 536 537 538 539 540 541 542 543 544 545 546
        return naive::ConvolutionBackwardDataImpl::get_all_algorithms(
                filter, diff, grad);
    }
    auto fparam = make_ncb_kern_size_param(filter, diff, grad);
    auto ret = get_all_algorithms_with_ncb(fparam);
    megdnn_assert(!ret.empty(), "no usable conv fwd algorithm");
    return ret;
}

ConvolutionBackwardDataImpl::Algorithm*
ConvolutionBackwardDataImpl::get_algorithm_heuristic(
        const TensorLayout& filter, const TensorLayout& diff,
        const TensorLayout& grad, size_t workspace_limit_in_bytes,
547 548
        const AlgoAttribute& positive_attr,
        const AlgoAttribute& negative_attr) {
549
    if (param().format == param::Convolution::Format::NHWCD4 ||
550 551 552
        param().format == param::Convolution::Format::NCHW4 ||
        (param().format == param::Convolution::Format::NCHW &&
         grad.dtype.enumv() == DTypeEnum::QuantizedS8)) {
553
        return naive::ConvolutionBackwardDataImpl::get_algorithm_heuristic(
554 555
                filter, diff, grad, workspace_limit_in_bytes, positive_attr,
                negative_attr);
556 557 558
    }
    auto fparam = make_ncb_kern_size_param(filter, diff, grad);
    return get_algorithm_heuristic_with_ncb(fparam, workspace_limit_in_bytes,
559
                                            positive_attr, negative_attr);
560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 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 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630
}

ConvolutionBackwardDataImpl::NCBKernSizeParam
ConvolutionBackwardDataImpl::make_ncb_kern_size_param(
        const TensorLayout& filter, const TensorLayout& diff,
        const TensorLayout& grad) {
    auto safe_u32 = [](size_t v) -> uint32_t {
        megdnn_assert(v <= std::numeric_limits<uint32_t>::max(),
                      "value too large: %zu", v);
        return v;
    };
    size_t spatial_pos;
    if (param().format == Param::Format::NCHW) {
        spatial_pos = 2;
    } else {
        megdnn_assert(param().format == Param::Format::NHWC,
                      "invalid conv format");
        spatial_pos = 1;
    }
    auto grad_fwd = grad;
    auto filter_fwd = filter;
    auto diff_fwd = diff;

    std::swap(grad_fwd.dtype, diff_fwd.dtype);

    return {
            safe_u32(diff[0]),
            {{safe_u32(diff[spatial_pos]), safe_u32(diff[spatial_pos + 1])}},
            {{safe_u32(grad[spatial_pos]), safe_u32(grad[spatial_pos + 1])}},
            check_layout_fwd(grad_fwd, filter_fwd, diff_fwd),
            diff.dtype,
            filter.dtype,
            grad.dtype,
            diff,
            filter,
            grad,
            diff.stride[0],
            grad.stride[0],
            0,
            0,
            0,
            param().compute_mode,
    };
}

ConvolutionBackwardDataImpl::NCBKernParam
ConvolutionBackwardDataImpl::make_ncb_kern_param(_megdnn_tensor_in filter,
                                                 _megdnn_tensor_in diff,
                                                 _megdnn_tensor_out grad,
                                                 _megdnn_workspace workspace) {
    NCBKernParam ret;
    static_cast<NCBKernSizeParam&>(ret) =
            make_ncb_kern_size_param(filter.layout, diff.layout, grad.layout);

    auto required_workspace_in_bytes = get_workspace_with_ncb(ret);
    megdnn_assert(workspace.size >= required_workspace_in_bytes,
                  "required workspace: %zu; provided workspace: %zu",
                  required_workspace_in_bytes, workspace.size);
    ret.filter_ptr = filter.raw_ptr;
    ret.diff_ptr = diff.raw_ptr;
    ret.grad_ptr = grad.raw_ptr;
    ret.workspace_ptr = workspace.raw_ptr;
    ret.workspace_size = workspace.size;
    return ret;
}

void ConvolutionBackwardDataImpl::exec_with_ncb_kern(
        const NCBKernParam& param) {
    auto p1g = param;
    auto group = p1g.filter_meta.group;
    p1g.filter_meta.group = 1;
631
    auto&& algo = get_algorithm(p1g);
632
    auto kptr = ncb_1g_dispatch_kern(algo, p1g);
633
    if (group == 1 || static_cast<AlgoBase*>(algo)->is_naive()) {
634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682
        auto run = [kptr, param]() { kptr(param); };
        static_cast<naive::HandleImpl*>(handle())->dispatch_kern(run);
    } else {
        megdnn_assert(p1g.filter_meta.format == Param::Format::NCHW ||
                              p1g.filter_meta.format == Param::Format::NHWC,
                      "invalid conv format");
        auto run = [kptr, p1g_orig = p1g, group]() {
            auto p1g = p1g_orig;
            ptrdiff_t istrd, fstrd, ostrd;
            fstrd = p1g.filter_meta.icpg * p1g.filter_meta.ocpg *
                    p1g.filter_meta.spatial[0] * p1g.filter_meta.spatial[1] *
                    p1g.filter_type.size();
            istrd = p1g.filter_meta.ocpg * p1g.diff_type.size();
            ostrd = p1g.filter_meta.icpg * p1g.grad_type.size();
            p1g.diff_extra_mem_size =
                    (group - 1) * p1g.filter_meta.ocpg * p1g.diff_type.size();
            p1g.filter_extra_mem_size =
                    (group - 1) * p1g.filter_meta.icpg * p1g.filter_meta.ocpg *
                    p1g.filter_meta.spatial[0] * p1g.filter_meta.spatial[1] *
                    p1g.filter_type.size();
            p1g.grad_extra_mem_size =
                    (group - 1) * p1g.filter_meta.icpg * p1g.grad_type.size();
            if (p1g.filter_meta.format == Param::Format::NCHW) {
                istrd *= p1g.isz[0] * p1g.isz[1];
                ostrd *= p1g.osz[0] * p1g.osz[1];
                p1g.diff_extra_mem_size *= p1g.isz[0] * p1g.isz[1];
                p1g.grad_extra_mem_size *= p1g.osz[0] * p1g.osz[1];
            } else {
                // must be NHWC. No action performed.
            }
            for (size_t i = 0; i < group; ++i) {
                kptr(p1g);
                incr_ptr(p1g.diff_ptr, istrd);
                incr_ptr(p1g.filter_ptr, fstrd);
                incr_ptr(p1g.grad_ptr, ostrd);
                p1g.diff_extra_mem_size -= istrd;
                p1g.filter_extra_mem_size -= fstrd;
                p1g.grad_extra_mem_size -= ostrd;
            }
        };
        static_cast<naive::HandleImpl*>(handle())->dispatch_kern(run);
    }
}

size_t ConvolutionBackwardDataImpl::get_workspace_with_ncb(
        const NCBKernSizeParam& param) {
    if (param.filter_meta.group != 1) {
        auto p1g = param;
        p1g.filter_meta.group = 1;
683 684
        auto algo = get_algorithm(p1g);
        return ncb_1g_get_workspace(algo, p1g);
685
    }
686 687
    auto algo = get_algorithm(param);
    return ncb_1g_get_workspace(algo, param);
688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703
}

std::vector<ConvolutionBackwardDataImpl::Algorithm*>
ConvolutionBackwardDataImpl::get_all_algorithms_with_ncb(
        const NCBKernSizeParam& param) {
    if (param.filter_meta.group != 1) {
        auto p1g = param;
        p1g.filter_meta.group = 1;
        return ncb_1g_get_all_algorithms(p1g);
    }
    return ncb_1g_get_all_algorithms(param);
}

ConvolutionBackwardDataImpl::Algorithm*
ConvolutionBackwardDataImpl::get_algorithm_heuristic_with_ncb(
        const NCBKernSizeParam& param, size_t workspace_limit_in_bytes,
704 705
        const AlgoAttribute& positive_attr,
        const AlgoAttribute& negative_attr) {
706 707 708 709
    if (param.filter_meta.group != 1) {
        auto p1g = param;
        p1g.filter_meta.group = 1;
        return ncb_1g_get_algorithm_heuristic(p1g, workspace_limit_in_bytes,
710
                                              positive_attr, negative_attr);
711 712
    }
    return ncb_1g_get_algorithm_heuristic(param, workspace_limit_in_bytes,
713
                                          positive_attr, negative_attr);
714 715 716 717 718
}

size_t ConvolutionBackwardDataImpl::ncb_1g_get_workspace(
        Algorithm* algo, const NCBKernSizeParam& param) {
    megdnn_assert(param.filter_meta.group == 1);
719
    if (algo->handle_type() == Handle::HandleType::FALLBACK) {
720 721 722 723 724 725 726 727 728 729
        return static_cast<AlgoBase*>(algo)->get_workspace(this, param);
    }
    return 0;
}

ConvolutionBackwardDataImpl::ncb_kern_t
ConvolutionBackwardDataImpl::ncb_1g_dispatch_kern(
        Algorithm* algo, const NCBKernSizeParam& param) {
    megdnn_assert(param.filter_meta.group == 1);

730
    if (algo->handle_type() == Handle::HandleType::FALLBACK) {
731 732 733
        return static_cast<AlgoBase*>(algo)->dispatch_kern(this, param);
    }

M
Megvii Engine Team 已提交
734
    megdnn_throw("no suitable ConvolutionBackwardData algorithm");
735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750
}

bool ConvolutionBackwardDataImpl::is_matrix_mul_preferred(
        const NCBKernSizeParam& param) {
    auto&& fm = param.filter_meta;
    auto OC = fm.ocpg, IC = fm.icpg;

    return (OC * IC >= 32) ||
           (fm.spatial[0] == 1 && fm.spatial[1] == 1 && fm.padding[0] == 0 &&
            fm.padding[1] == 0 && fm.stride[0] == 1 && fm.stride[1] == 1);
}

std::vector<ConvolutionBackwardDataImpl::Algorithm*>
ConvolutionBackwardDataImpl::ncb_1g_get_all_algorithms(
        const NCBKernSizeParam& param) {
    std::vector<Algorithm*> ret;
751
    std::vector<Algorithm*> prefer_algos;
752
    for (auto&& i : get_all_packed_algo()) {
753 754 755
        if (i->usable(this, param)) {
            if (i->is_preferred(param)) {
                prefer_algos.push_back(i);
756
            } else {
757
                ret.push_back(i);
758 759 760
            }
        }
    }
761
    ret.insert(ret.begin(), prefer_algos.begin(), prefer_algos.end());
762 763 764 765 766 767
    return ret;
}

ConvolutionBackwardDataImpl::Algorithm*
ConvolutionBackwardDataImpl::ncb_1g_get_algorithm_heuristic(
        const NCBKernSizeParam& param, size_t workspace_limit_in_bytes,
768 769
        const AlgoAttribute& positive_attr,
        const AlgoAttribute& negative_attr) {
770 771
    for (auto i : ncb_1g_get_all_algorithms(param)) {
        if (ncb_1g_get_workspace(i, param) <= workspace_limit_in_bytes) {
772 773
            if (i->contain_attribute_all(positive_attr) &&
                !i->contain_attribute_any(negative_attr)) {
774 775 776 777 778 779 780 781
                return i;
            }
        }
    }
    megdnn_assert(0,
                  "no suitable algorithm found within given workspace limit");
}

782
ConvolutionBackwardDataImpl::Algorithm*
783 784
ConvolutionBackwardDataImpl::get_algorithm_from_desc(
        const AlgorithmDesc& desc) {
785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814
    if (!desc.valid()) {
        return nullptr;
    } else {
        switch (desc.handle_type) {
            case Handle::HandleType::FALLBACK: {
                const auto& map = algo_pack().all_algos_map();
                megdnn_assert(map.find(desc) != map.end());
                return map.at(desc);
            }
#if MEGDNN_AARCH64 || MEGDNN_ARMV7
            case Handle::HandleType::ARM_COMMON:
            case Handle::HandleType::AARCH64:
            case Handle::HandleType::ARMV7:
                return arm_common::ConvolutionBackwardDataImpl::
                        get_algo_from_desc(desc);
#endif
            case Handle::HandleType::NAIVE: {
                auto algo = static_cast<naive::HandleImpl*>(handle())
                                    ->default_conv_bwd_data_algo();
                megdnn_assert(algo->info().desc == desc);
                return algo;
            }
            default:
                megdnn_throw("Unknown handle type");
                return nullptr;
        }
    }
}


815 816
ConvolutionBackwardDataImpl::Algorithm*
ConvolutionBackwardDataImpl::get_algorithm(const NCBKernSizeParam& param) {
817
    if (auto algo = get_algorithm_from_desc(execution_policy().algo)) {
818
        return algo;
819 820 821 822
    }
    if (!m_prev_selected_algo ||
        memcmp(&m_prev_selected_algo_sizep, &param, sizeof(NCBKernSizeParam))) {
        m_prev_selected_algo = ncb_1g_get_algorithm_heuristic(
823
                param, std::numeric_limits<size_t>::max(),
824
                AlgoAttribute::DEFAULT, AlgoAttribute::DEFAULT);
825 826 827 828 829 830 831 832 833 834 835
        m_prev_selected_algo_sizep = param;
    }
    return m_prev_selected_algo;
}

const char* ConvolutionBackwardDataImpl::get_algorithm_set_name() const {
    // fallback version 0
    return "FALLBACK_CONVOLUTION_BACKWARD_DATA_IMPL0";
}

// vim: syntax=cpp.doxygen