From 81a5dfe76bddd3dc92c58123cdb31a099edc82ba Mon Sep 17 00:00:00 2001 From: nihuini Date: Fri, 19 Jul 2019 19:55:44 +0800 Subject: [PATCH] general convolution and convolutiondepthwise arm neon pack4, wip --- src/layer/arm/convolution_arm.cpp | 496 ++++++++++++++++- src/layer/arm/convolution_arm.h | 5 + src/layer/arm/convolutiondepthwise_arm.cpp | 587 ++++++++++++++++++++- src/layer/arm/convolutiondepthwise_arm.h | 7 + 4 files changed, 1065 insertions(+), 30 deletions(-) diff --git a/src/layer/arm/convolution_arm.cpp b/src/layer/arm/convolution_arm.cpp index 8ada5973d..e04ee3fc7 100644 --- a/src/layer/arm/convolution_arm.cpp +++ b/src/layer/arm/convolution_arm.cpp @@ -19,6 +19,7 @@ #if __ARM_NEON #include +#include "neon_mathfun.h" #endif // __ARM_NEON namespace ncnn { @@ -84,12 +85,171 @@ int Convolution_arm::create_pipeline(const Option& opt) activation->create_pipeline(opt_cpu); } + const int maxk = kernel_w * kernel_h; + int num_input = weight_data_size / maxk / num_output; + + if (opt.use_packing_layout) + { + + // pack4 + if (num_input % 4 == 0 && num_output % 4 == 0) + { + // src = kw-kh-inch-outch + // dst = 4b-4a-kw-kh-inch/4a-outch/4b + { + Mat weight_data_r2 = weight_data.reshape(maxk, num_input, num_output); + + weight_data_pack4.create(maxk, num_input/4, num_output/4, (size_t)4*16, 16); + + for (int q=0; q+3= 16 && num_output >= 16) use_winograd3x3 = true; @@ -101,7 +261,6 @@ int Convolution_arm::create_pipeline(const Option& opt) // TODO assume more proper condition if (opt.use_sgemm_convolution && kernel_w == 1 && kernel_h == 1 && dilation_w == 1 && dilation_h == 1 && stride_w == 1 && stride_h == 1) { - int num_input = weight_data_size / num_output; if (num_input >= 64 && num_output >= 64) use_sgemm1x1 = true; } @@ -110,28 +269,22 @@ int Convolution_arm::create_pipeline(const Option& opt) { if (use_winograd3x3) { - int num_input = weight_data_size / 9 / num_output; // conv3x3s1_winograd23_transform_kernel_int8_neon(weight_data, weight_3x3_winograd23_int8_data, num_input, num_output); conv3x3s1_winograd43_transform_kernel_int8_neon(weight_data, weight_3x3_winograd23_int8_data, num_input, num_output); } if (kernel_w == 3 && kernel_h == 3 && dilation_w == 1 && dilation_h == 1 && stride_w == 2 && stride_h == 2) { - int num_input = weight_data_size / 9 / num_output; conv3x3s2_transform_kernel_int8_neon(weight_data, weight_3x3s2_int8_data, num_input, num_output); } else if (kernel_w == 1 && kernel_h == 1 && dilation_w == 1 && dilation_h == 1 && stride_w == 1 && stride_h == 1) { - int num_input = weight_data_size / num_output; conv1x1s1_sgemm_transform_kernel_int8_neon(weight_data, weight_1x1s1_sgemm_int8_data, num_input, num_output); use_sgemm1x1 = true; } else { - int kernel_size = kernel_w * kernel_h; - int num_input = weight_data_size / kernel_size / num_output; - - conv_im2col_sgemm_transform_kernel_int8_neon(weight_data, weight_sgemm_int8_data, num_input, num_output, kernel_size); + conv_im2col_sgemm_transform_kernel_int8_neon(weight_data, weight_sgemm_int8_data, num_input, num_output, maxk); } return 0; @@ -139,32 +292,25 @@ int Convolution_arm::create_pipeline(const Option& opt) if (impl_type > 0) { - int num_input = 0; - int kernel_size = 0; switch(impl_type) { case 1: // winograd - num_input = weight_data_size / 9 / num_output; conv3x3s1_winograd64_transform_kernel_neon5(weight_data, weight_3x3_winograd64_data, num_input, num_output); break; case 2: // pointwise - num_input = weight_data_size / num_output; conv1x1s1_sgemm_transform_kernel_neon(weight_data, weight_1x1_sgemm_data, num_input, num_output); break; case 3: // im2col - kernel_size = kernel_w * kernel_h; - num_input = weight_data_size / kernel_size / num_output; - conv_im2col_sgemm_transform_kernel_neon(weight_data, weight_sgemm_data, num_input, num_output, kernel_size); + conv_im2col_sgemm_transform_kernel_neon(weight_data, weight_sgemm_data, num_input, num_output, maxk); break; case 4: // direct break; case 5: // conv3x3s2 - num_input = weight_data_size / 9 / num_output; conv3x3s2_transform_kernel_neon(weight_data, weight_3x3s2_data, num_input, num_output); default: return -1; @@ -174,28 +320,22 @@ int Convolution_arm::create_pipeline(const Option& opt) if (use_winograd3x3) { - int num_input = weight_data_size / 9 / num_output; // conv3x3s1_winograd64_transform_kernel_neon(weight_data, weight_3x3_winograd64_data, num_input, num_output); conv3x3s1_winograd64_transform_kernel_neon5(weight_data, weight_3x3_winograd64_data, num_input, num_output); } if (use_sgemm1x1) { - int num_input = weight_data_size / num_output; conv1x1s1_sgemm_transform_kernel_neon(weight_data, weight_1x1_sgemm_data, num_input, num_output); } if (kernel_w == 3 && kernel_h == 3 && dilation_w == 1 && dilation_h == 1 && stride_w == 2 && stride_h == 2) { - int num_input = weight_data_size / 9 / num_output; conv3x3s2_transform_kernel_neon(weight_data, weight_3x3s2_data, num_input, num_output); } { - int kernel_size = kernel_w * kernel_h; - int num_input = weight_data_size / kernel_size / num_output; - - conv_im2col_sgemm_transform_kernel_neon(weight_data, weight_sgemm_data, num_input, num_output, kernel_size); + conv_im2col_sgemm_transform_kernel_neon(weight_data, weight_sgemm_data, num_input, num_output, maxk); } return 0; @@ -324,6 +464,314 @@ int Convolution_arm::forward(const Mat& bottom_blob, Mat& top_blob, const Option // convolv with NxN kernel // value = value + bias + if (opt.use_packing_layout) + { + + int w = bottom_blob.w; + int h = bottom_blob.h; + int channels = bottom_blob.c; + size_t elemsize = bottom_blob.elemsize; + int packing = bottom_blob.packing; + +// fprintf(stderr, "Convolution input %d x %d pad = %d %d ksize=%d %d stride=%d %d\n", w, h, pad_w, pad_h, kernel_w, kernel_h, stride_w, stride_h); + + const int kernel_extent_w = dilation_w * (kernel_w - 1) + 1; + const int kernel_extent_h = dilation_h * (kernel_h - 1) + 1; + + Mat bottom_blob_bordered = bottom_blob; + if (pad_w > 0 || pad_h > 0) + { + copy_make_border(bottom_blob, bottom_blob_bordered, pad_h, pad_h, pad_w, pad_w, BORDER_CONSTANT, 0.f, opt.workspace_allocator, opt.num_threads); + if (bottom_blob_bordered.empty()) + return -100; + + w = bottom_blob_bordered.w; + h = bottom_blob_bordered.h; + } + else if (pad_w == -233 && pad_h == -233) + { + int wpad = kernel_extent_w + (w - 1) / stride_w * stride_w - w; + int hpad = kernel_extent_h + (h - 1) / stride_h * stride_h - h; + if (wpad > 0 || hpad > 0) + { + copy_make_border(bottom_blob, bottom_blob_bordered, hpad / 2, hpad - hpad / 2, wpad / 2, wpad - wpad / 2, BORDER_CONSTANT, 0.f, opt.workspace_allocator, opt.num_threads); + if (bottom_blob_bordered.empty()) + return -100; + } + + w = bottom_blob_bordered.w; + h = bottom_blob_bordered.h; + } + + int outw = (w - kernel_extent_w) / stride_w + 1; + int outh = (h - kernel_extent_h) / stride_h + 1; + int out_packing = num_output % 4 == 0 ? 4 : 1; + size_t out_elemsize = elemsize / packing * out_packing; + + const int maxk = kernel_w * kernel_h; + + // kernel offsets + std::vector _space_ofs(maxk); + int* space_ofs = &_space_ofs[0]; + { + int p1 = 0; + int p2 = 0; + int gap = w * dilation_h - kernel_w * dilation_w; + for (int i = 0; i < kernel_h; i++) + { + for (int j = 0; j < kernel_w; j++) + { + space_ofs[p1] = p2; + p1++; + p2 += dilation_w; + } + p2 += gap; + } + } + + // float32 + top_blob.create(outw, outh, num_output / out_packing, out_elemsize, out_packing, opt.blob_allocator); + if (top_blob.empty()) + return -100; + + if (packing == 4 && out_packing == 4) + { + // num_output + #pragma omp parallel for num_threads(opt.num_threads) + for (int p=0; p 0.f ? sum : sum * slope; + } + else if (activation_type == 3) + { + float min = activation_params[0]; + float max = activation_params[1]; + if (sum < min) + sum = min; + if (sum > max) + sum = max; + } + else if (activation_type == 4) + { + sum = 1.f / (1.f + exp(-sum)); + } + + outptr[j] = sum; + } + + outptr += outw; + } + } + + return 0; + } + + } // opt.use_packed_layout + if (bottom_blob.dims != 3) { return Convolution::forward(bottom_blob, top_blob, opt); diff --git a/src/layer/arm/convolution_arm.h b/src/layer/arm/convolution_arm.h index 91219f08e..f2448ddbb 100644 --- a/src/layer/arm/convolution_arm.h +++ b/src/layer/arm/convolution_arm.h @@ -45,6 +45,11 @@ public: Mat weight_sgemm_int8_data; Mat weight_sgemm_data; std::vector weight_3x3_winograd23_int8_data; + + // pack4 + Mat weight_data_pack4; + Mat weight_data_pack1to4; + Mat weight_data_pack4to1; }; } // namespace ncnn diff --git a/src/layer/arm/convolutiondepthwise_arm.cpp b/src/layer/arm/convolutiondepthwise_arm.cpp index 927d7a8bd..2df6a5a81 100644 --- a/src/layer/arm/convolutiondepthwise_arm.cpp +++ b/src/layer/arm/convolutiondepthwise_arm.cpp @@ -18,6 +18,7 @@ #if __ARM_NEON #include +#include "neon_mathfun.h" #endif // __ARM_NEON namespace ncnn { @@ -80,6 +81,199 @@ int ConvolutionDepthWise_arm::create_pipeline(const Option& opt) const int maxk = kernel_w * kernel_h; int channels = (weight_data_size / group) / maxk / (num_output / group) * group; + if (opt.use_packing_layout) + { + + // depth-wise + if (channels == group && group == num_output) + { + // pack4 + if (num_output % 4 == 0) + { + Mat weight_data_r2 = weight_data.reshape(maxk, group); + convert_packing(weight_data_r2, weight_data_pack4, 4); + } + } + + // group convolution + const int channels_g = channels / group; + const int num_output_g = num_output / group; + + // pack4 + if (channels_g % 4 == 0 && num_output_g % 4 == 0) + { + // src = kw-kh-inch-outch + // dst = 4a-4b-kw-kh-inch/4a-outch/4b + { + Mat weight_data_r2_groups = weight_data.reshape(maxk, channels_g, num_output_g * group); + + weight_data_pack4_groups.create(maxk, channels_g/4, num_output_g/4 * group, (size_t)4*16, 16); + + for (int g=0; g _space_ofs(maxk); + int* space_ofs = &_space_ofs[0]; + { + int p1 = 0; + int p2 = 0; + int gap = w * dilation_h - kernel_w * dilation_w; + for (int i = 0; i < kernel_h; i++) + { + for (int j = 0; j < kernel_w; j++) + { + space_ofs[p1] = p2; + p1++; + p2 += dilation_w; + } + p2 += gap; + } + } + + top_blob.create(outw, outh, num_output / out_packing, out_elemsize, out_packing, opt.blob_allocator); + if (top_blob.empty()) + return -100; + + // depth-wise + if (channels == group / packing && group / packing == num_output / packing) + { + if (packing == 4) + { + #pragma omp parallel for num_threads(opt.num_threads) + for (int g=0; g 0.f ? sum : sum * slope; + } + else if (activation_type == 3) + { + float min = activation_params[0]; + float max = activation_params[1]; + if (sum < min) + sum = min; + if (sum > max) + sum = max; + } + else if (activation_type == 4) + { + sum = 1.f / (1.f + exp(-sum)); + } + + outptr[j] = sum; + } + + outptr += outw; + } + } + } + } + + // packing + if (num_output_g % 4 != 0 && out_packing == 4) + { + convert_packing(top_blob_unpacked, top_blob, 4, opt.blob_allocator, opt.num_threads); + } + else + { + top_blob = top_blob_unpacked; + } + + return 0; + + } // opt.use_packing_layout // int8 if (use_int8_inference) diff --git a/src/layer/arm/convolutiondepthwise_arm.h b/src/layer/arm/convolutiondepthwise_arm.h index 09320bfc1..6d16f69c8 100644 --- a/src/layer/arm/convolutiondepthwise_arm.h +++ b/src/layer/arm/convolutiondepthwise_arm.h @@ -32,6 +32,13 @@ public: public: Layer* activation; std::vector group_ops; + + // packing + Mat weight_data_pack4; + + Mat weight_data_pack4_groups; + Mat weight_data_pack1to4_groups; + Mat weight_data_pack4to1_groups; }; } // namespace ncnn