[CPU] optimize ExtractImagePatches (#4898)

This commit is contained in:
Ivan Novoselov authored and GitHub committed 2021-04-25 19:52:07 +03:00
1 parent b770be227b
commit 594c4882e6
5 files changed
+623 -228

No files matched your search

@@ -1,17 +1,15 @@
// Copyright (C) 2018-2021 Intel Corporation
// Copyright (C) 2020-2021 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#include "list.hpp"
#include "base.hpp"
#include "extract_image_patches.hpp"
#include "caseless.hpp"
#include <cmath>
#include <string>
#include <vector>
#include <cassert>
#include <set>
#include "ie_parallel.hpp"
#include "list.hpp"
#include <cpu/x64/jit_generator.hpp>
#include <cstring>
#include <string>
#include <cmath>
namespace InferenceEngine {
namespace Extensions {
@@ -19,229 +17,479 @@ namespace Cpu {
using details::CaselessEq;
class ExtractImagePatchesImpl : public ExtLayerBase {
public:
explicit ExtractImagePatchesImpl(const CNNLayer* layer) {
try {
std::string errorPrefix = std::string("Layer ") + layer->type + " with name '" + layer->name + "' ";
if (details::CaselessEq<std::string>()("ExtractImagePatchesLayer", layer->type))
IE_THROW() << errorPrefix << "is not an instance of ExtractImagePatchesLayer class";
using namespace dnnl::impl::cpu;
using namespace dnnl::impl::cpu::x64;
using namespace dnnl::impl::utils;
using namespace Xbyak;
if (layer->insData.size() != 1 || layer->outData.size() != 1)
IE_THROW() << errorPrefix << "has incorrect number of input or output edges!"
<< " Input: " << layer->insData.size() << "; Output: " << layer->outData.size();
#define GET_OFF(field) offsetof(jit_extract_image_patches_args, field)
auto inData = layer->insData[0].lock();
if (inData == nullptr)
IE_THROW() << errorPrefix << "has nullable input data";
template <cpu_isa_t isa>
struct jit_extract_image_patches_kernel : public jit_uni_extract_image_patches_kernel, public jit_generator {
DECLARE_CPU_JIT_AUX_FUNCTIONS(jit_extract_image_patches_kernel)
if (inData->getTensorDesc().getDims().size() != 4)
IE_THROW() << errorPrefix << "must have 4D input tensor. Actual: " << inData->getTensorDesc().getDims().size();
explicit jit_extract_image_patches_kernel(jit_extract_image_patches_params jpp) : jit_uni_extract_image_patches_kernel(jpp), jit_generator() {}
if (layer->outData[0]->getTensorDesc().getDims().size() != 4)
IE_THROW() << errorPrefix << "must have 4D output tensor. Actual: " << layer->outData[0]->getTensorDesc().getDims().size();
if (inData->getLayout() != NCHW)
IE_THROW() << errorPrefix << "has unsupported layout: " << inData->getLayout();
const auto precision = inData->getTensorDesc().getPrecision();
if (_supported_precisions_sizes.find(precision.size()) == _supported_precisions_sizes.end())
IE_THROW() << errorPrefix << "has unsupported precision: " << precision.name();
auto ksizes = layer->GetParamAsUInts("sizes");
auto strides = layer->GetParamAsUInts("strides");
auto rates = layer->GetParamAsUInts("rates");
_auto_pad = layer->GetParamAsString("auto_pad");
if (!CaselessEq<std::string>()(_auto_pad, "valid")
&& !CaselessEq<std::string>()(_auto_pad, "same_upper")
&& !CaselessEq<std::string>()(_auto_pad, "same_lower"))
IE_THROW() << errorPrefix << "has unsupported auto_pad value: " << _auto_pad;
if (ksizes.size() != 2 || strides.size() != 2 || rates.size() != 2)
IE_THROW() << errorPrefix << "must have the following attributes with shape {2}: sizes, strides, rates.";
_ksizes.clear();
_strides.clear();
_rates.clear();
for (size_t i = 0; i < ksizes.size(); i++)
_ksizes.push_back((int64_t)ksizes[i]);
for (size_t i = 0; i < strides.size(); i++)
_strides.push_back((int64_t)strides[i]);
for (size_t i = 0; i < rates.size(); i++)
_rates.push_back((int64_t)rates[i]);
LayerConfig config;
DataConfig inConfig;
inConfig.desc = inData->getTensorDesc();
config.inConfs.push_back(inConfig);
DataConfig outConfig;
outConfig.desc = layer->outData[0]->getTensorDesc();
outConfig.desc.setPrecision(inConfig.desc.getPrecision());
outConfig.desc.setLayout(inConfig.desc.getLayout());
config.outConfs.push_back(outConfig);
config.dynBatchSupport = false;
confs.push_back(config);
} catch (InferenceEngine::Exception &ex) {
errorMsg = ex.what();
}
void create_ker() override {
jit_generator::create_kernel();
ker_ = (decltype(ker_))jit_ker();
}
StatusCode execute(std::vector<Blob::Ptr>& inputs, std::vector<Blob::Ptr>& outputs, ResponseDesc *resp) noexcept override {
switch (inputs[0]->getTensorDesc().getPrecision().size()) {
case 1: {
process_data<PrecisionTrait<Precision::U8>::value_type>(inputs, outputs);
break;
}
case 2: {
process_data<PrecisionTrait<Precision::U16>::value_type>(inputs, outputs);
break;
}
case 4: {
process_data<PrecisionTrait<Precision::I32>::value_type>(inputs, outputs);
break;
}
case 8: {
process_data<PrecisionTrait<Precision::U64>::value_type>(inputs, outputs);
break;
}
default: {
if (resp) {
std::string errorMsg = "ExtractImagePatches layer does not support precision '"
+ std::string(inputs[0]->getTensorDesc().getPrecision().name()) + "'";
errorMsg.copy(resp->msg, sizeof(resp->msg) - 1);
}
return GENERAL_ERROR;
}
void generate() override {
this->preamble();
mov(reg_num_pads, ptr[reg_params + GET_OFF(h_lo_pad)]);
mov(reg_h_hi_pad, ptr[reg_params + GET_OFF(h_hi_pad)]);
mov(reg_w_lo_pad, ptr[reg_params + GET_OFF(w_lo_pad)]);
mov(reg_w_hi_pad, ptr[reg_params + GET_OFF(w_hi_pad)]);
mov(reg_src, ptr[reg_params + GET_OFF(src)]);
mov(reg_dst, ptr[reg_params + GET_OFF(dst)]);
mov(reg_src_incr, jpp.SH * jpp.IW * jpp.dtype_size);
mov(reg_aux64, reg_w_hi_pad);
mul_by_const(reg_aux64, reg_aux64_2, jpp.SW * jpp.dtype_size);
sub(reg_src_incr, reg_aux64);
mov(reg_aux64, reg_w_lo_pad);
mul_by_const(reg_aux64, reg_aux64_2, jpp.SW * jpp.dtype_size);
add(reg_src_incr, reg_aux64);
add(reg_src, reg_aux64);
mov(reg_ow_work_amount, reg_w_hi_pad);
sub(reg_ow_work_amount, reg_w_lo_pad);
uni_vpxor(vmm_zero, vmm_zero, vmm_zero);
if (mayiuse_gather) {
mov(reg_aux64, gather_index_table);
uni_vmovups(vmm_gather_index, ptr[reg_aux64]);
}
loop();
return OK;
}
this->postamble();
template<typename T>
void process_data(std::vector<Blob::Ptr>& inputs, std::vector<Blob::Ptr>& outputs) noexcept {
const T* src_data = inputs[0]->cbuffer().as<const T*>() +
inputs[0]->getTensorDesc().getBlockingDesc().getOffsetPadding();
T* dst_data = outputs[0]->buffer().as<T*>() +
outputs[0]->getTensorDesc().getBlockingDesc().getOffsetPadding();
const auto& inDims = inputs[0]->getTensorDesc().getDims();
const size_t inDimsSize = inDims.size();
const size_t BATCH = 0, CHANNEL = 1, HIGHT = 0, WIDTH = 1;
const int64_t IC = inDims[CHANNEL];
const int64_t IH = inDims[inDimsSize - 2];
const int64_t IW = inDims[inDimsSize - 1];
const auto& outDims = outputs[0]->getTensorDesc().getDims();
const size_t outDimsSize = outDims.size();
const int64_t OB = outDims[BATCH];
const int64_t OC = outDims[CHANNEL];
const int64_t OH = outDims[outDimsSize - 2];
const int64_t OW = outDims[outDimsSize - 1];
const int64_t KH = _ksizes[HIGHT];
const int64_t KW = _ksizes[WIDTH];
const int64_t SH = _strides[HIGHT];
const int64_t SW = _strides[WIDTH];
const int64_t RH = _rates[HIGHT];
const int64_t RW = _rates[WIDTH];
int64_t iwStep = KW + (RW - 1) * (KW - 1);
int64_t ihStep = KH + (RH - 1) * (KH - 1);
int64_t PL = 0, PT = 0;
if (!CaselessEq<std::string>()(_auto_pad, "valid")) {
int64_t PW = (std::ceil(1.f * IW/SW) - 1) * SW + iwStep - IW;
int64_t PH = (std::ceil(1.f * IH/SH) - 1) * SH + ihStep - IH;
if ((PW > 0) && (PW < iwStep)) {
if (PW % 2 == 1) {
if (CaselessEq<std::string>()(_auto_pad, "same_lower")) {
PL = (PW + 1) / 2;
} else if (CaselessEq<std::string>()(_auto_pad, "same_upper")) {
PL = (PW - 1) / 2;
}
} else {
PL = PW / 2;
}
}
if ((PH > 0) && (PH < ihStep)) {
if (PH % 2 == 1) {
if (CaselessEq<std::string>()(_auto_pad, "same_lower")) {
PT = (PH + 1) / 2;
} else if (CaselessEq<std::string>()(_auto_pad, "same_upper")) {
PT = (PH - 1) / 2;
}
} else {
PT = PH / 2;
}
}
}
const int64_t OH_OW = OH * OW;
const int64_t OC_OH_OW = OC * OH_OW;
const int64_t IH_IW = IH * IW;
const int64_t IC_IH_IW = IC * IH_IW;
const int64_t work_amount = OB;
auto thread_body = [&](const int ithr, const int nthr) {
int64_t start(0lu), end(0lu);
splitter(work_amount, nthr, ithr, start, end);
if (start >= end)
return;
for (int64_t ob = start; ob < end; ob++) {
const int64_t ibICIHIW = ob * IC_IH_IW;
const int64_t obOCOHOW = ob * OC_OH_OW;
for (int64_t oh = 0; oh < OH; oh++) {
const int64_t obOCOHOWohOW = obOCOHOW + oh * OW;
int64_t ih0 = oh * SH - PT;
for (int64_t ow = 0; ow < OW; ow++) {
const int64_t obOCOHOWohOWow = obOCOHOWohOW + ow;
int64_t iw0 = ow * SW - PL;
int64_t oc = 0;
for (int64_t kh = 0; kh < KH; kh++) {
int64_t ihKH = ih0 + kh * RH;
int64_t ibICIHIWihFHIW = ibICIHIW + ihKH * IW;
for (int64_t kw = 0; kw < KW; kw++) {
for (int64_t ic = 0; ic < IC; ic++, oc++) {
int64_t iwKW = iw0 + kw * RW;
int64_t dst_idx = obOCOHOWohOWow + oc * OH_OW;
if (ihKH < 0 || ihKH >= IH || iwKW < 0 || iwKW >= IW) {
dst_data[dst_idx] = T(0);
} else {
int64_t src_idx = ibICIHIWihFHIW + ic * IH_IW + iwKW;
dst_data[dst_idx] = src_data[src_idx];
}
}
}
}
}
}
}
};
parallel_nt(0, thread_body);
if (mayiuse_gather)
prepare_table();
}
private:
std::vector<int64_t> _ksizes;
std::vector<int64_t> _strides;
std::vector<int64_t> _rates;
std::string _auto_pad;
using Vmm = typename conditional3<isa == x64::sse41, Xbyak::Xmm, isa == x64::avx2, Xbyak::Ymm, Xbyak::Zmm>::type;
using reg64_t = const Xbyak::Reg64;
using reg32_t = const Xbyak::Reg32;
bool mayiuse_gather = (mayiuse(x64::avx2) || mayiuse(x64::avx512_common)) && (jpp.dtype_size == 4);
uint32_t vlen = cpu_isa_traits<isa>::vlen;
reg64_t reg_src = r8;
reg64_t reg_dst = r9;
reg64_t reg_oh_count = r10;
reg64_t reg_ow_count = r11;
reg64_t reg_num_pads = r12;
reg64_t reg_src_incr = r13;
reg64_t reg_aux64 = rax;
reg64_t reg_w_hi_pad = r14;
reg64_t reg_w_lo_pad = r15;
reg64_t reg_h_hi_pad = rbp;
reg64_t reg_aux64_2 = rbx;
reg64_t reg_ow_work_amount = rsi;
reg64_t reg_params = abi_param1;
static const std::set<size_t> _supported_precisions_sizes;
Vmm vmm = Vmm(0);
Xmm xmm = Xmm(0);
Vmm vmm_zero = Vmm(1); // reserved for pad
Xbyak::Xmm xmm_aux = Xbyak::Xmm(2);
Vmm vmm_gather_index = Vmm(3);
Vmm vmm_gather_mask = Vmm(4);
Opmask k_mask = Xbyak::Opmask(1);
Xbyak::Label gather_index_table;
inline void load_scalar(Vmm vmm_arg, const Xbyak::Address &op) {
Xbyak::Xmm xmm_src = Xmm(vmm_arg.getIdx());
switch (jpp.dtype_size) {
case 4: uni_vmovss(vmm_arg, op); break;
case 2: uni_vpinsrw(xmm_src, xmm_src, op, 0x0); break;
case 1: uni_vpinsrb(xmm_src, xmm_src, op, 0x0); break;
default: IE_THROW() << "The data type of size '" << jpp.dtype_size << "' is not supported.";
}
}
inline void store_scalar(const Xbyak::Address &op, Vmm vmm_arg) {
Xbyak::Xmm xmm_dst = Xmm(vmm_arg.getIdx());
switch (jpp.dtype_size) {
case 4: uni_vmovss(op, vmm_arg); break;
case 2: uni_vpextrw(op, xmm_dst, 0x0); break;
case 1: uni_vpextrb(op, xmm_dst, 0x0); break;
default: IE_THROW() << "The data type of size '" << jpp.dtype_size << "' is not supported.";
}
}
inline void pad_with_zeros(reg64_t &reg_num_pads_arg, reg64_t &reg_dst_arg) {
Xbyak::Label main, tail, exit;
L(main);
{
cmp(reg_num_pads_arg, jpp.block_size);
jl(tail);
uni_vmovups(ptr[reg_dst_arg], vmm_zero);
add(reg_dst_arg, jpp.dtype_size * jpp.block_size);
sub(reg_num_pads_arg, jpp.block_size);
jmp(main);
}
L(tail);
{
cmp(reg_num_pads_arg, 0);
jle(exit);
store_scalar(ptr[reg_dst_arg], vmm_zero);
add(reg_dst_arg, jpp.dtype_size);
dec(reg_num_pads_arg);
jmp(tail);
}
L(exit);
}
inline void custom_uni_vgatherdps(const Vmm &vmm_arg, reg64_t &mem_base, const Vmm &mem_offset, Vmm &vmm_mask) {
switch (isa) {
case x64::avx2:
uni_vpcmpeqd(vmm_mask, vmm_mask, vmm_mask);
vgatherdps(vmm_arg, ptr[mem_base + mem_offset], vmm_mask);
break;
case x64::avx512_common:
kxnord(k_mask, k_mask, k_mask);
vgatherdps(vmm_arg | k_mask, ptr[mem_base + mem_offset]);
break;
case x64::sse41:
emulate_gather(vmm_arg, mem_base);
break;
default: IE_THROW() << "Got unsupported instruction set.";
}
}
inline void gather_src2vmm(const Vmm &vmm_arg, reg64_t &mem_base) {
switch (jpp.dtype_size) {
case 4: custom_uni_vgatherdps(vmm, mem_base, vmm_gather_index, vmm_gather_mask); break;
case 2:
case 1: emulate_gather(vmm_arg, mem_base); break;
default: IE_THROW() << "The data type of size '" << jpp.dtype_size << "' is not supported.";
}
}
inline void emulate_gather(const Xbyak::Xmm &xmm_arg, reg64_t &mem_base, int xmm_offset = 0) {
const int xmm_size = 16; // bytes
const int xmm_block_size = xmm_size / jpp.dtype_size;
const int offset = xmm_offset * jpp.SW * jpp.dtype_size * xmm_block_size;
for (int i = 0; i < xmm_block_size; i++) {
Xbyak::Address addr = ptr[mem_base + i * jpp.SW * jpp.dtype_size + offset];
switch (jpp.dtype_size) {
case 4: uni_vpinsrd(xmm_arg, xmm_arg, addr, i); break;
case 2: uni_vpinsrw(xmm_arg, xmm_arg, addr, i); break;
case 1: uni_vpinsrb(xmm_arg, xmm_arg, addr, i); break;
default: IE_THROW() << "The data type of size '" << jpp.dtype_size << "' is not supported.";
}
}
}
inline void emulate_gather(const Xbyak::Ymm &ymm_arg, reg64_t &mem_base) {
Xbyak::Xmm low_xmm = Xbyak::Xmm(ymm_arg.getIdx());
emulate_gather(low_xmm, mem_base, 0);
emulate_gather(xmm_aux, mem_base, 1);
vinserti128(ymm_arg, ymm_arg, xmm_aux, 1);
}
inline void emulate_gather(const Xbyak::Zmm &zmm_arg, reg64_t &mem_base) {
Xbyak::Xmm low_xmm = Xbyak::Xmm(zmm_arg.getIdx());
emulate_gather(low_xmm, mem_base, 0);
for (int i = 1; i < 4; i++) {
emulate_gather(xmm_aux, mem_base, i);
vinserti64x2(zmm_arg, zmm_arg, xmm_aux, i);
}
}
void loop() {
mov(reg_oh_count, reg_h_hi_pad);
// reg_num_pads contains h_lo_pad at this point
sub(reg_oh_count, reg_num_pads);
Xbyak::Label ih_loop, ih_tail, ih_exit;
Xbyak::Label iw_loop, iw_tail, iw_exit;
if (jpp.need_padding) {
mul_by_const(reg_num_pads, reg_aux64, jpp.OW);
pad_with_zeros(reg_num_pads, reg_dst);
}
L(ih_loop);
{
cmp(reg_oh_count, 0);
jle(ih_exit, T_NEAR);
if (jpp.need_padding) {
mov(reg_num_pads, reg_w_lo_pad);
pad_with_zeros(reg_num_pads, reg_dst);
}
mov(reg_ow_count, reg_ow_work_amount);
L(iw_loop);
{
cmp(reg_ow_count, jpp.block_size);
jle(iw_tail, T_NEAR);
gather_src2vmm(vmm, reg_src);
add(reg_src, jpp.SW * jpp.dtype_size * jpp.block_size);
uni_vmovups(ptr[reg_dst], vmm);
add(reg_dst, jpp.dtype_size * jpp.block_size);
sub(reg_ow_count, jpp.block_size);
jmp(iw_loop);
}
L(iw_tail);
{
cmp(reg_ow_count, 0);
jle(iw_exit, T_NEAR);
load_scalar(vmm, ptr[reg_src]);
store_scalar(ptr[reg_dst], vmm);
dec(reg_ow_count);
add(reg_src, jpp.SW * jpp.dtype_size);
add(reg_dst, jpp.dtype_size);
jmp(iw_tail);
}
L(iw_exit);
if (jpp.need_padding) {
mov(reg_num_pads, jpp.OW);
sub(reg_num_pads, reg_w_hi_pad);
pad_with_zeros(reg_num_pads, reg_dst);
}
dec(reg_oh_count);
add(reg_src, reg_src_incr);
jmp(ih_loop, T_NEAR);
}
L(ih_exit);
if (jpp.need_padding) {
mov(reg_num_pads, jpp.OH);
sub(reg_num_pads, reg_h_hi_pad);
mul_by_const(reg_num_pads, reg_aux64, jpp.OW);
pad_with_zeros(reg_num_pads, reg_dst);
}
}
void prepare_table() {
align(64);
L(gather_index_table);
for (int32_t i = 0; i < vlen / sizeof(int32_t); i++)
dd(i * jpp.SW * jpp.dtype_size);
}
};
const std::set<size_t> ExtractImagePatchesImpl::_supported_precisions_sizes = {1, 2, 4, 8};
ExtractImagePatchesImpl::ExtractImagePatchesImpl(const CNNLayer* layer) {
try {
std::string errorPrefix = std::string("Layer ") + layer->type + " with name '" + layer->name + "' ";
if (details::CaselessEq<std::string>()("ExtractImagePatchesLayer", layer->type))
IE_THROW() << errorPrefix << "is not an instance of ExtractImagePatchesLayer class";
REG_FACTORY_FOR(ExtractImagePatchesImpl, ExtractImagePatches);
if (layer->insData.size() != 1 || layer->outData.size() != 1)
IE_THROW() << errorPrefix << "has incorrect number of input or output edges!"
<< " Input: " << layer->insData.size() << "; Output: " << layer->outData.size();
auto inData = layer->insData[0].lock();
if (inData == nullptr)
IE_THROW() << errorPrefix << "has nullable input data";
if (inData->getTensorDesc().getDims().size() != 4)
IE_THROW() << errorPrefix << "must have 4D input tensor. Actual: " << inData->getTensorDesc().getDims().size();
if (layer->outData[0]->getTensorDesc().getDims().size() != 4)
IE_THROW() << errorPrefix << "must have 4D output tensor. Actual: " << layer->outData[0]->getTensorDesc().getDims().size();
if (inData->getLayout() != NCHW)
IE_THROW() << errorPrefix << "has unsupported layout: " << inData->getLayout();
const auto precision = inData->getTensorDesc().getPrecision();
if (_supported_precisions_sizes.find(precision.size()) == _supported_precisions_sizes.end())
IE_THROW() << errorPrefix << "has unsupported precision: " << precision.name();
auto ksizes = layer->GetParamAsUInts("sizes");
auto strides = layer->GetParamAsUInts("strides");
auto rates = layer->GetParamAsUInts("rates");
std::string auto_pad = layer->GetParamAsString("auto_pad");
if (!CaselessEq<std::string>()(auto_pad, "valid")
&& !CaselessEq<std::string>()(auto_pad, "same_upper")
&& !CaselessEq<std::string>()(auto_pad, "same_lower"))
IE_THROW() << errorPrefix << "has unsupported auto_pad value: " << auto_pad;
if (ksizes.size() != 2 || strides.size() != 2 || rates.size() != 2)
IE_THROW() << errorPrefix << "must have the following attributes with shape {2}: sizes, strides, rates.";
_ksizes.clear();
_strides.clear();
_rates.clear();
for (const auto& x : ksizes) {
if (x < 0)
IE_THROW() << "Kernel sizes must be non-negative, got '" << x << "'.";
_ksizes.push_back(static_cast<size_t>(x));
}
for (const auto& x : strides) {
if (x < 0)
IE_THROW() << "Strides must be non-negative, got '" << x << "'.";
_strides.push_back(static_cast<size_t>(x));
}
for (const auto& x : rates) {
if (x < 0)
IE_THROW() << "Rates must be non-negative, got '" << x << "'.";
_rates.push_back(static_cast<size_t>(x));
}
SizeVector in_dims = inData->getTensorDesc().getDims();
_pad_left = 0;
_pad_top = 0;
jit_extract_image_patches_params jpp;
jpp.need_padding = false;
if (!CaselessEq<std::string>()(auto_pad, "valid")) {
const size_t iheight = in_dims[2];
const size_t iwidth = in_dims[3];
const int64_t ihStep = _ksizes[0] + (_rates[0] - 1) * (_ksizes[0] - 1);
const int64_t iwStep = _ksizes[1] + (_rates[1] - 1) * (_ksizes[1] - 1);
int64_t PW = (std::ceil(1.f * iwidth/_strides[1]) - 1) * _strides[1] + iwStep - iwidth;
int64_t PH = (std::ceil(1.f * iheight/_strides[0]) - 1) * _strides[0] + ihStep - iheight;
int64_t increment_sign = 0;
if (CaselessEq<std::string>()(auto_pad, "same_lower")) {
increment_sign = 1;
} else if (CaselessEq<std::string>()(auto_pad, "same_upper")) {
increment_sign = -1;
}
if ((PW > 0) && (PW < iwStep)) {
_pad_left = static_cast<size_t>((PW + increment_sign * (PW % 2)) / 2);
jpp.need_padding = true;
}
if ((PH > 0) && (PH < ihStep)) {
_pad_top = static_cast<size_t>((PH + increment_sign * (PH % 2)) / 2);
jpp.need_padding = true;
}
}
jpp.IW = in_dims[3];
SizeVector out_dims = layer->outData[0]->getTensorDesc().getDims();
jpp.OH = out_dims[2];
jpp.OW = out_dims[3];
jpp.KH = _ksizes[0];
jpp.KW = _ksizes[1];
jpp.SH = _strides[0];
jpp.SW = _strides[1];
jpp.dtype_size = layer->insData.front().lock()->getPrecision().size();
jpp.block_size = 1;
if (mayiuse(x64::avx512_common)) {
jpp.block_size = cpu_isa_traits<x64::avx512_common>::vlen / jpp.dtype_size;
extract_image_patches_kernel.reset(new jit_extract_image_patches_kernel<x64::avx512_common>(jpp));
} else if (mayiuse(x64::avx2)) {
jpp.block_size = cpu_isa_traits<x64::avx2>::vlen / jpp.dtype_size;
extract_image_patches_kernel.reset(new jit_extract_image_patches_kernel<x64::avx2>(jpp));
} else if (mayiuse(x64::sse41)) {
jpp.block_size = cpu_isa_traits<x64::sse41>::vlen / jpp.dtype_size;
extract_image_patches_kernel.reset(new jit_extract_image_patches_kernel<x64::sse41>(jpp));
}
if (extract_image_patches_kernel)
extract_image_patches_kernel->create_ker();
LayerConfig config;
DataConfig inConfig;
inConfig.desc = inData->getTensorDesc();
config.inConfs.push_back(inConfig);
DataConfig outConfig;
outConfig.desc = layer->outData[0]->getTensorDesc();
outConfig.desc.setPrecision(inConfig.desc.getPrecision());
outConfig.desc.setLayout(inConfig.desc.getLayout());
config.outConfs.push_back(outConfig);
config.dynBatchSupport = false;
confs.push_back(config);
} catch (InferenceEngine::Exception &ex) {
errorMsg = ex.what();
}
}
StatusCode ExtractImagePatchesImpl::execute(std::vector<Blob::Ptr>& inputs, std::vector<Blob::Ptr>& outputs, ResponseDesc *resp) noexcept {
const char *src_data = inputs[0]->cbuffer().as<const char *>() +
inputs[0]->getTensorDesc().getBlockingDesc().getOffsetPadding();
char *dst_data = outputs[0]->buffer().as<char *>() +
outputs[0]->getTensorDesc().getBlockingDesc().getOffsetPadding();
const size_t dtype_size = inputs[0]->getTensorDesc().getPrecision().size();
const auto& inDims = inputs[0]->getTensorDesc().getDims();
const size_t IC = inDims[1];
const size_t IH = inDims[2];
const size_t IW = inDims[3];
const auto& outDims = outputs[0]->getTensorDesc().getDims();
const size_t OB = outDims[0];
const size_t OH = outDims[2];
const size_t OW = outDims[3];
const size_t KH = _ksizes[0], KW = _ksizes[1];
const size_t SH = _strides[0], SW = _strides[1];
const size_t RH = _rates[0], RW = _rates[1];
const size_t PT = _pad_top, PL = _pad_left;
const std::vector<size_t> istrides = inputs[0]->getTensorDesc().getBlockingDesc().getStrides();
const std::vector<size_t> ostrides = outputs[0]->getTensorDesc().getBlockingDesc().getStrides();
const std::vector<size_t> ostrides_partial = {ostrides[0], KW * IC * ostrides[1], IC * ostrides[1], ostrides[1]};
if (extract_image_patches_kernel) {
parallel_for4d(OB, KH, KW, IC, [&](const size_t ob, const size_t kh, const size_t kw, const size_t ic) {
const int64_t ih_start = kh * RH - PT;
const int64_t iw_start = kw * RW - PL;
const size_t ih_lpad = ih_start >= 0 ? 0 : std::ceil(-1.f * ih_start / SH);
const size_t iw_lpad = iw_start >= 0 ? 0 : std::ceil(-1.f * iw_start / SW);
const size_t ih_hpad = std::ceil((IH - 1.f * ih_start) / SH) > OH ? OH : std::ceil((IH - 1.f * ih_start) / SH);
const size_t iw_hpad = std::ceil((IW - 1.f * iw_start) / SW) > OW ? OW : std::ceil((IW - 1.f * iw_start) / SW);
size_t dst_offset = ob * ostrides_partial[0] + kh * ostrides_partial[1] + kw * ostrides_partial[2] + ic * ostrides_partial[3];
size_t src_offset = ob * istrides[0] + ic * istrides[1] + ih_start * istrides[2] + iw_start + ih_lpad * SH * IW;
auto args = jit_extract_image_patches_args();
args.src = src_data + src_offset * dtype_size;
args.dst = dst_data + dst_offset * dtype_size;
args.h_lo_pad = ih_lpad;
args.h_hi_pad = ih_hpad;
args.w_lo_pad = iw_lpad;
args.w_hi_pad = iw_hpad;
(*extract_image_patches_kernel)(&args);
});
} else {
parallel_for4d(OB, KH, KW, IC, [&](const size_t ob, const size_t kh, const size_t kw, const size_t ic) {
const int64_t iw_start = kw * RW - PL;
const int64_t ih_start = kh * RH - PT;
const size_t ih_lpad = ih_start >= 0 ? 0 : std::ceil(- 1.f * ih_start / SH);
const size_t iw_lpad = iw_start >= 0 ? 0 : std::ceil(- 1.f * iw_start / SW);
const size_t ih_hpad = std::ceil((IH - 1.f * ih_start) / SH) > OH ? OH : std::ceil((IH + -1.f * ih_start) / SH);
const size_t iw_hpad = std::ceil((IW - 1.f * iw_start) / SW) > OW ? OW : std::ceil((IW - 1.f * iw_start) / SW);
char *my_dst_ptr = dst_data +
(ob * ostrides_partial[0] + kh * ostrides_partial[1] + kw * ostrides_partial[2] + ic * ostrides_partial[3]) * dtype_size;
const char *my_src_ptr = src_data + (ob * istrides[0] + ic * istrides[1] + ih_start * istrides[2] + iw_start) * dtype_size;
size_t num_bytes_to_set = ih_lpad * OW * dtype_size;
memset(my_dst_ptr, 0, num_bytes_to_set);
my_dst_ptr += num_bytes_to_set;
const char* src_ptr_h_stop = my_src_ptr + ih_hpad * SH * IW * dtype_size;
for (const char *src_h_ptr = my_src_ptr + ih_lpad * SH * IW * dtype_size;
src_h_ptr < src_ptr_h_stop; src_h_ptr += SH * IW * dtype_size) {
num_bytes_to_set = iw_lpad * dtype_size;
memset(my_dst_ptr, 0, num_bytes_to_set);
my_dst_ptr += num_bytes_to_set;
const char* src_ptr_w_stop = src_h_ptr + iw_hpad * SW * dtype_size;
for (const char* src_w_ptr = src_h_ptr + iw_lpad * SW * dtype_size;
src_w_ptr < src_ptr_w_stop; src_w_ptr += SW * dtype_size) {
num_bytes_to_set = dtype_size;
memcpy(my_dst_ptr, src_w_ptr, num_bytes_to_set);
my_dst_ptr += num_bytes_to_set;
}
num_bytes_to_set = (OW - iw_hpad) * dtype_size;
memset(my_dst_ptr, 0, num_bytes_to_set);
my_dst_ptr += num_bytes_to_set;
}
num_bytes_to_set = (OH - ih_hpad) * OW * dtype_size;
memset(my_dst_ptr, 0, num_bytes_to_set);
});
}
return OK;
}
const std::set<size_t> ExtractImagePatchesImpl::_supported_precisions_sizes = {1, 2, 4};
} // namespace Cpu
} // namespace Extensions
@@ -0,0 +1,62 @@
// Copyright (C) 2021 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#pragma once
#include "base.hpp"
#include <vector>
#include <set>
namespace InferenceEngine {
namespace Extensions {
namespace Cpu {
struct jit_extract_image_patches_params {
size_t IW;
size_t OH, OW;
size_t KH, KW;
size_t SH, SW;
size_t dtype_size;
size_t block_size;
bool need_padding;
};
struct jit_extract_image_patches_args {
uint64_t h_lo_pad;
uint64_t h_hi_pad;
uint64_t w_lo_pad;
uint64_t w_hi_pad;
const void* src;
void* dst;
};
struct jit_uni_extract_image_patches_kernel {
void (*ker_)(const jit_extract_image_patches_args *);
void operator()(const jit_extract_image_patches_args *args) { assert(ker_); ker_(args); }
jit_extract_image_patches_params jpp;
virtual void create_ker() = 0;
explicit jit_uni_extract_image_patches_kernel(jit_extract_image_patches_params jpp) : ker_(nullptr), jpp(jpp) {}
virtual ~jit_uni_extract_image_patches_kernel() {}
};
class ExtractImagePatchesImpl : public ExtLayerBase {
public:
explicit ExtractImagePatchesImpl(const CNNLayer*);
StatusCode execute(std::vector<Blob::Ptr>&, std::vector<Blob::Ptr>&, ResponseDesc*) noexcept override;
private:
std::vector<size_t> _ksizes;
std::vector<size_t> _strides;
std::vector<size_t> _rates;
size_t _pad_left;
size_t _pad_top;
std::shared_ptr<jit_uni_extract_image_patches_kernel> extract_image_patches_kernel;
static const std::set<size_t> _supported_precisions_sizes;
};
REG_FACTORY_FOR(ExtractImagePatchesImpl, ExtractImagePatches);
} // namespace Cpu
} // namespace Extensions
} // namespace InferenceEngine
@@ -1,4 +1,4 @@
// Copyright (C) 2018-2021 Intel Corporation
// Copyright (C) 2020-2021 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
@@ -19,19 +19,12 @@ const std::vector<PadType> autoPads = {PadType::VALID, PadType::SAME_UPPER, PadT
const std::vector<InferenceEngine::Precision> netPrecisions = {
InferenceEngine::Precision::I8,
InferenceEngine::Precision::U8,
InferenceEngine::Precision::I16,
InferenceEngine::Precision::BF16,
InferenceEngine::Precision::I32,
InferenceEngine::Precision::FP32
InferenceEngine::Precision::FP32,
InferenceEngine::Precision::I64
};
const auto extractImagePatchesParamsSet = ::testing::Combine(
::testing::ValuesIn(inDataShape),
::testing::ValuesIn(kernels),
::testing::ValuesIn(strides),
::testing::ValuesIn(rates),
::testing::ValuesIn(autoPads)
);
INSTANTIATE_TEST_CASE_P(smoke_layers_CPU, ExtractImagePatchesTest,
::testing::Combine(
::testing::ValuesIn(inDataShape),
@@ -0,0 +1,92 @@
// Copyright (C) 2021 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#include "test_utils/cpu_test_utils.hpp"
#include "shared_test_classes/base/layer_test_utils.hpp"
#include "ngraph_functions/utils/ngraph_helpers.hpp"
#include <shared_test_classes/single_layer/extract_image_patches.hpp>
using namespace InferenceEngine;
using namespace CPUTestUtils;
namespace CPULayerTestsDefinitions {
using LayerTestsDefinitions::extractImagePatchesTuple;
typedef std::tuple<
extractImagePatchesTuple,
CPUSpecificParams> extractImagePatchesCPUTestParamsSet;
class ExtractImagePatchesLayerCPUTest : public testing::WithParamInterface<extractImagePatchesCPUTestParamsSet>,
virtual public LayerTestsUtils::LayerTestsCommon, public CPUTestsBase {
public:
static std::string getTestCaseName(testing::TestParamInfo<extractImagePatchesCPUTestParamsSet> obj) {
extractImagePatchesTuple basicParamsSet;
CPUSpecificParams cpuParams;
std::tie(basicParamsSet, cpuParams) = obj.param;
std::ostringstream result;
result << LayerTestsDefinitions::ExtractImagePatchesTest::getTestCaseName(testing::TestParamInfo<extractImagePatchesTuple>(
basicParamsSet, 0));
result << CPUTestsBase::getTestCaseName(cpuParams);
return result.str();
}
protected:
void SetUp() override {
extractImagePatchesTuple basicParamsSet;
CPUSpecificParams cpuParams;
std::tie(basicParamsSet, cpuParams) = this->GetParam();
std::tie(inFmts, outFmts, priority, selectedType) = cpuParams;
std::vector<size_t> inputShape, kernel, strides, rates;
ngraph::op::PadType pad_type;
InferenceEngine::Precision netPrecision;
std::tie(inputShape, kernel, strides, rates, pad_type, netPrecision, inPrc, outPrc, inLayout, targetDevice) = basicParamsSet;
selectedType = std::string("unknown_") + netPrecision.name();
auto ngPrc = FuncTestUtils::PrecisionUtils::convertIE2nGraphPrc(netPrecision);
auto inputNode = std::make_shared<ngraph::opset6::Parameter>(ngPrc, ngraph::Shape(inputShape));
ngraph::ParameterVector params = {inputNode};
auto extImgPatches = std::make_shared<ngraph::opset6::ExtractImagePatches>(
inputNode, ngraph::Shape(kernel), ngraph::Strides(strides), ngraph::Shape(rates), pad_type);
ngraph::ResultVector results{std::make_shared<ngraph::opset6::Result>(extImgPatches)};
function = std::make_shared<ngraph::Function>(results, params, "ExtractImagePatches");
}
};
TEST_P(ExtractImagePatchesLayerCPUTest, CompareWithRefs) {
SKIP_IF_CURRENT_TEST_IS_DISABLED()
Run();
CheckPluginRelatedResults(executableNetwork, "ExtractImagePatches");
}
namespace {
const std::vector<std::vector<size_t>> inShapes = {{2, 3, 13, 37}};
const std::vector<std::vector<size_t>> kSizes = {{1, 5}, {3, 4}, {3, 1}};
const std::vector<std::vector<size_t>> strides = {{1, 2}, {2, 2}, {2, 1}};
const std::vector<std::vector<size_t>> rates = {{1, 3}, {3, 3}, {3, 1}};
const std::vector<ngraph::op::PadType> autoPads = {ngraph::op::PadType::VALID, ngraph::op::PadType::SAME_UPPER, ngraph::op::PadType::SAME_LOWER};
const std::vector<Precision> netPrecision = {Precision::I8, Precision::BF16, Precision::FP32};
const CPUSpecificParams CPUParams = emptyCPUSpec;
const auto Layer_params = ::testing::Combine(
::testing::ValuesIn(inShapes),
::testing::ValuesIn(kSizes),
::testing::ValuesIn(strides),
::testing::ValuesIn(rates),
::testing::ValuesIn(autoPads),
::testing::ValuesIn(netPrecision),
::testing::Values(InferenceEngine::Precision::UNSPECIFIED),
::testing::Values(InferenceEngine::Precision::UNSPECIFIED),
::testing::Values(InferenceEngine::Layout::ANY),
::testing::Values(CommonTestUtils::DEVICE_CPU));
INSTANTIATE_TEST_CASE_P(smoke_ExtractImagePatches_CPU, ExtractImagePatchesLayerCPUTest,
::testing::Combine(Layer_params, ::testing::Values(CPUParams)),
ExtractImagePatchesLayerCPUTest::getTestCaseName);
} // namespace
} // namespace CPULayerTestsDefinitions
@@ -1,4 +1,4 @@
// Copyright (C) 2018-2021 Intel Corporation
// Copyright (C) 2020 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//