|
22 | 22 | * Description: Optimized s8 depthwise separable convolution function for |
23 | 23 | * channel multiplier of 1. |
24 | 24 | * |
25 | | - * $Date: 6 July 2022 |
26 | | - * $Revision: V.3.0.1 |
| 25 | + * $Date: 27 July 2022 |
| 26 | + * $Revision: V.3.1.0 |
27 | 27 | * |
28 | 28 | * Target Processor: Cortex-M CPUs |
29 | 29 | * |
@@ -64,7 +64,7 @@ arm_cmsis_nn_status arm_depthwise_conv_s8_opt(const cmsis_nn_context *ctx, |
64 | 64 | const int32_t input_ch = input_dims->c; |
65 | 65 | const int32_t output_ch = output_dims->c; |
66 | 66 |
|
67 | | - /* Check input constraints input_ch == output_ch */ |
| 67 | + /* Check depth multiplier is 1 */ |
68 | 68 | if (input_ch != output_ch) |
69 | 69 | { |
70 | 70 | return ARM_CMSIS_NN_ARG_ERROR; |
@@ -102,116 +102,130 @@ arm_cmsis_nn_status arm_depthwise_conv_s8_opt(const cmsis_nn_context *ctx, |
102 | 102 | int buffer_count = 0; |
103 | 103 | const int32_t kernel_size = kernel_x * kernel_y; |
104 | 104 |
|
105 | | - /* This part implements the im2col function */ |
106 | | - for (int i_out_y = 0, base_idx_y = -pad_y; i_out_y < output_y; base_idx_y += stride_y, i_out_y++) |
| 105 | + const int32_t ch_loop = (input_ch + (CH_IN_BLOCK_MVE - 1)) / CH_IN_BLOCK_MVE; |
| 106 | + int32_t remaining_ch = output_ch; |
| 107 | + int32_t active_ch = MIN(CH_IN_BLOCK_MVE, remaining_ch); |
| 108 | + remaining_ch -= CH_IN_BLOCK_MVE; |
| 109 | + |
| 110 | + for (int i_ch = 0; i_ch < ch_loop; i_ch++) |
107 | 111 | { |
108 | | - for (int i_out_x = 0, base_idx_x = -pad_x; i_out_x < output_x; base_idx_x += stride_x, i_out_x++) |
| 112 | + out = output + i_ch * CH_IN_BLOCK_MVE; |
| 113 | + const int8_t *input_slice = input + (i_ch * CH_IN_BLOCK_MVE); |
| 114 | + |
| 115 | + for (int i_out_y = 0, base_idx_y = -pad_y; i_out_y < output_y; base_idx_y += stride_y, i_out_y++) |
109 | 116 | { |
110 | | - for (int i_ker_y = base_idx_y; i_ker_y < base_idx_y + kernel_y; i_ker_y++) |
| 117 | + for (int i_out_x = 0, base_idx_x = -pad_x; i_out_x < output_x; base_idx_x += stride_x, i_out_x++) |
111 | 118 | { |
112 | | - for (int i_ker_x = base_idx_x; i_ker_x < base_idx_x + kernel_x; i_ker_x++) |
| 119 | + for (int i_ker_y = base_idx_y; i_ker_y < base_idx_y + kernel_y; i_ker_y++) |
| 120 | + { |
| 121 | + for (int i_ker_x = base_idx_x; i_ker_x < base_idx_x + kernel_x; i_ker_x++) |
| 122 | + { |
| 123 | + if (i_ker_y < 0 || i_ker_y >= input_y || i_ker_x < 0 || i_ker_x >= input_x) |
| 124 | + { |
| 125 | + arm_memset_q7(lhs_buffer, (int8_t)-input_offset, (uint32_t)active_ch); |
| 126 | + padded = 1; |
| 127 | + } |
| 128 | + else |
| 129 | + { |
| 130 | + arm_memcpy_q7(lhs_buffer, |
| 131 | + input_slice + (i_ker_y * input_x + i_ker_x) * input_ch, |
| 132 | + (uint32_t)active_ch); |
| 133 | + } |
| 134 | + lhs_buffer += CH_IN_BLOCK_MVE; |
| 135 | + } |
| 136 | + } |
| 137 | + buffer_count++; |
| 138 | + |
| 139 | + if (buffer_count == 4) |
113 | 140 | { |
114 | | - if (i_ker_y < 0 || i_ker_y >= input_y || i_ker_x < 0 || i_ker_x >= input_x) |
| 141 | + const int32_t block_offset = i_ch * CH_IN_BLOCK_MVE; |
| 142 | + lhs_buffer = (q7_t *)buffer_a; |
| 143 | + if (padded == 0) |
115 | 144 | { |
116 | | - arm_memset_q7(lhs_buffer, (int8_t)-input_offset, (uint32_t)input_ch); |
117 | | - padded = 1; |
| 145 | + arm_nn_depthwise_conv_nt_t_s8(lhs_buffer, |
| 146 | + kernel + block_offset, |
| 147 | + input_offset, |
| 148 | + active_ch, |
| 149 | + input_ch, |
| 150 | + output_shift + block_offset, |
| 151 | + output_mult + block_offset, |
| 152 | + output_offset, |
| 153 | + output_activation_min, |
| 154 | + output_activation_max, |
| 155 | + kernel_size, |
| 156 | + bias + block_offset, |
| 157 | + out); |
118 | 158 | } |
119 | 159 | else |
120 | 160 | { |
121 | | - arm_memcpy_q7(lhs_buffer, input + (i_ker_y * input_x + i_ker_x) * input_ch, (uint32_t)input_ch); |
| 161 | + arm_nn_depthwise_conv_nt_t_padded_s8(lhs_buffer, |
| 162 | + kernel + block_offset, |
| 163 | + input_offset, |
| 164 | + active_ch, |
| 165 | + input_ch, |
| 166 | + output_shift + block_offset, |
| 167 | + output_mult + block_offset, |
| 168 | + output_offset, |
| 169 | + output_activation_min, |
| 170 | + output_activation_max, |
| 171 | + kernel_size, |
| 172 | + bias + block_offset, |
| 173 | + out); |
| 174 | + padded = 0; |
122 | 175 | } |
123 | | - lhs_buffer += input_ch; |
| 176 | + out += (4 * input_ch); |
| 177 | + buffer_count = 0; |
124 | 178 | } |
125 | 179 | } |
126 | | - buffer_count++; |
| 180 | + } |
| 181 | + /* Handle left over buffers */ |
| 182 | + lhs_buffer = (q7_t *)buffer_a; |
127 | 183 |
|
128 | | - if (buffer_count == 4) |
| 184 | + int8_t *out_base = out; |
| 185 | + for (int i_buf = 0; i_buf < buffer_count; i_buf++) |
| 186 | + { |
| 187 | + int32_t loop_count = (active_ch + 3) / 4; |
| 188 | + int32_t num_ch_to_process = active_ch; |
| 189 | + out = out_base + (i_buf * input_ch); |
| 190 | + for (int i_loop_cnt = 0, offset = i_ch * CH_IN_BLOCK_MVE; i_loop_cnt < loop_count; |
| 191 | + num_ch_to_process -= 4, offset += 4, i_loop_cnt++) |
129 | 192 | { |
130 | | - lhs_buffer = (q7_t *)buffer_a; |
131 | | - if (padded == 0) |
132 | | - { |
133 | | - out = arm_nn_depthwise_conv_nt_t_s8(lhs_buffer, |
134 | | - kernel, |
135 | | - input_offset, |
136 | | - input_ch, |
137 | | - output_shift, |
138 | | - output_mult, |
139 | | - output_offset, |
140 | | - output_activation_min, |
141 | | - output_activation_max, |
142 | | - kernel_size, |
143 | | - bias, |
144 | | - out); |
145 | | - } |
146 | | - else |
| 193 | + const int8_t *col_0 = lhs_buffer + (kernel_size * CH_IN_BLOCK_MVE * i_buf) + (i_loop_cnt * 4); |
| 194 | + const int8_t *row_0 = kernel + offset; |
| 195 | + int32x4_t out_0 = vdupq_n_s32(0); |
| 196 | + if (bias) |
147 | 197 | { |
148 | | - out = arm_nn_depthwise_conv_nt_t_padded_s8(lhs_buffer, |
149 | | - kernel, |
150 | | - input_offset, |
151 | | - input_ch, |
152 | | - output_shift, |
153 | | - output_mult, |
154 | | - output_offset, |
155 | | - output_activation_min, |
156 | | - output_activation_max, |
157 | | - kernel_size, |
158 | | - bias, |
159 | | - out); |
160 | | - padded = 0; |
| 198 | + out_0 = vldrwq_s32(&bias[offset]); |
161 | 199 | } |
162 | | - buffer_count = 0; |
163 | | - } |
164 | | - } |
165 | | - } |
166 | 200 |
|
167 | | - /* Handle left over buffers */ |
168 | | - lhs_buffer = (q7_t *)buffer_a; |
169 | | - |
170 | | - for (int i_buf = 0; i_buf < buffer_count; i_buf++) |
171 | | - { |
172 | | - int32_t loop_count = (input_ch + 3) / 4; |
173 | | - int32_t num_ch_to_process = input_ch; |
| 201 | + for (int i_ker = 0; i_ker < kernel_size; i_ker++) |
| 202 | + { |
| 203 | + const int32x4_t ker_0 = vldrbq_s32(row_0); |
| 204 | + int32x4_t ip_0 = vldrbq_s32(col_0); |
| 205 | + ip_0 = vaddq_n_s32(ip_0, input_offset); |
| 206 | + out_0 += vmulq_s32(ip_0, ker_0); |
174 | 207 |
|
175 | | - for (int i_loop_cnt = 0, offset = 0; i_loop_cnt < loop_count; num_ch_to_process -= 4, offset += 4, i_loop_cnt++) |
176 | | - { |
177 | | - const int8_t *col_0 = lhs_buffer + (kernel_size * input_ch * i_buf) + offset; |
178 | | - const int8_t *row_0 = kernel + offset; |
179 | | - int32x4_t out_0 = vdupq_n_s32(0); |
180 | | - if (bias) |
181 | | - { |
182 | | - out_0 = vldrwq_s32(&bias[offset]); |
183 | | - } |
| 208 | + col_0 += CH_IN_BLOCK_MVE; |
| 209 | + row_0 += input_ch; |
| 210 | + } |
184 | 211 |
|
185 | | - for (int i_ker = 0; i_ker < kernel_size; i_ker++) |
186 | | - { |
187 | | - const int32x4_t ker_0 = vldrbq_s32(row_0); |
| 212 | + const int32x4_t mult = vldrwq_s32(&output_mult[offset]); |
| 213 | + const int32x4_t shift = vldrwq_s32(&output_shift[offset]); |
188 | 214 |
|
189 | | - int32x4_t ip_0 = vldrbq_s32(col_0); |
190 | | - ip_0 = vaddq_n_s32(ip_0, input_offset); |
191 | | - out_0 += vmulq_s32(ip_0, ker_0); |
| 215 | + out_0 = arm_requantize_mve_32x4(out_0, mult, shift); |
| 216 | + out_0 = vaddq_n_s32(out_0, output_offset); |
| 217 | + out_0 = vmaxq_s32(out_0, vdupq_n_s32(output_activation_min)); |
| 218 | + out_0 = vminq_s32(out_0, vdupq_n_s32(output_activation_max)); |
| 219 | + mve_pred16_t p = vctp32q((uint32_t)num_ch_to_process); |
| 220 | + vstrbq_p_s32(out, out_0, p); |
192 | 221 |
|
193 | | - col_0 += input_ch; |
194 | | - row_0 += input_ch; |
| 222 | + out += 4; |
195 | 223 | } |
196 | | - |
197 | | - const int32x4_t mult = vldrwq_s32(&output_mult[offset]); |
198 | | - const int32x4_t shift = vldrwq_s32(&output_shift[offset]); |
199 | | - |
200 | | - out_0 = arm_requantize_mve_32x4(out_0, mult, shift); |
201 | | - out_0 = vaddq_n_s32(out_0, output_offset); |
202 | | - out_0 = vmaxq_s32(out_0, vdupq_n_s32(output_activation_min)); |
203 | | - out_0 = vminq_s32(out_0, vdupq_n_s32(output_activation_max)); |
204 | | - mve_pred16_t p = vctp32q((uint32_t)num_ch_to_process); |
205 | | - vstrbq_p_s32(out, out_0, p); |
206 | | - |
207 | | - out += 4; |
208 | 224 | } |
| 225 | + buffer_count = 0; |
209 | 226 |
|
210 | | - const int tail_ch = input_ch & 0x3; |
211 | | - if (tail_ch != 0) |
212 | | - { |
213 | | - out -= (4 - tail_ch); |
214 | | - } |
| 227 | + active_ch = MIN(CH_IN_BLOCK_MVE, remaining_ch); |
| 228 | + remaining_ch -= CH_IN_BLOCK_MVE; |
215 | 229 | } |
216 | 230 |
|
217 | 231 | #else // ARM_MATH_DSP |
@@ -431,8 +445,8 @@ arm_cmsis_nn_status arm_depthwise_conv_s8_opt(const cmsis_nn_context *ctx, |
431 | 445 | int32_t arm_depthwise_conv_s8_opt_get_buffer_size(const cmsis_nn_dims *input_dims, const cmsis_nn_dims *filter_dims) |
432 | 446 | { |
433 | 447 | #if defined(ARM_MATH_MVEI) |
434 | | - /* The + 4 accounts for out of bounds read of the lhs buffers in the *_nt_t_* functions. */ |
435 | | - return (4 * input_dims->c * filter_dims->w * filter_dims->h) * (int32_t)sizeof(int8_t) + 4; |
| 448 | + (void)input_dims; |
| 449 | + return (4 * CH_IN_BLOCK_MVE * filter_dims->w * filter_dims->h) * (int32_t)sizeof(int8_t); |
436 | 450 | #elif defined(ARM_MATH_DSP) |
437 | 451 | return (input_dims->c * filter_dims->w * filter_dims->h) * sizeof(int16_t); |
438 | 452 | #else |
|
0 commit comments