|
1 | 1 | #pragma once |
2 | 2 | #include <cmath> |
| 3 | +#include <cstddef> |
3 | 4 | #include <stdexcept> |
4 | 5 | #include <vector> |
5 | 6 |
|
@@ -212,106 +213,103 @@ void Conv4D(const Tensor& input, const Tensor& kernel_, const Tensor& bias_, |
212 | 213 | size_t out_width = |
213 | 214 | ComputeConvOutputDim(in_width, kernel_width, stride_, pads_, dilations_); |
214 | 215 |
|
215 | | - std::vector<std::vector<std::vector<std::vector<ValueType>>>> padded_input( |
216 | | - batch_size, |
217 | | - std::vector<std::vector<std::vector<ValueType>>>( |
218 | | - in_height + 2 * pads_, |
219 | | - std::vector<std::vector<ValueType>>( |
220 | | - in_width + 2 * pads_, std::vector<ValueType>(in_channels, 0)))); |
221 | | - |
222 | 216 | parallel::Options options; |
223 | 217 | options.backend = backend; |
224 | 218 |
|
225 | | - parallel::parallel_for(batch_size, [&](size_t b) { |
226 | | - for (size_t h = 0; h < in_height; ++h) { |
227 | | - for (size_t w = 0; w < in_width; ++w) { |
228 | | - for (size_t c = 0; c < in_channels; ++c) { |
229 | | - padded_input[b][h + pads_][w + pads_][c] = |
230 | | - input.get<ValueType>({b, c, h, w}); |
231 | | - } |
232 | | - } |
233 | | - } |
234 | | - }, options); |
235 | | - |
236 | | - size_t dilated_kernel_height = (kernel_height - 1) * dilations_ + 1; |
237 | | - size_t dilated_kernel_width = (kernel_width - 1) * dilations_ + 1; |
238 | | - |
239 | | - std::vector<std::vector<std::vector<std::vector<ValueType>>>> dil_kernel( |
240 | | - out_channels, std::vector<std::vector<std::vector<ValueType>>>( |
241 | | - kernel_in_channels, |
242 | | - std::vector<std::vector<ValueType>>( |
243 | | - dilated_kernel_height, |
244 | | - std::vector<ValueType>(dilated_kernel_width, 0)))); |
245 | | - |
246 | | - parallel::parallel_for(out_channels, [&](size_t oc) { |
247 | | - for (size_t ic = 0; ic < kernel_in_channels; ++ic) { |
248 | | - for (size_t kh = 0; kh < kernel_height; ++kh) { |
249 | | - for (size_t kw = 0; kw < kernel_width; ++kw) { |
250 | | - dil_kernel[oc][ic][kh * dilations_][kw * dilations_] = |
251 | | - kernel_.get<ValueType>({oc, ic, kh, kw}); |
252 | | - } |
253 | | - } |
254 | | - } |
255 | | - }, options); |
| 219 | + const auto &input_data = *input.as<ValueType>(); |
| 220 | + const auto &kernel_data = *kernel_.as<ValueType>(); |
| 221 | + const std::vector<ValueType> *bias_data = nullptr; |
| 222 | + if (!bias_.empty()) { |
| 223 | + bias_data = bias_.as<ValueType>(); |
| 224 | + } |
256 | 225 |
|
257 | | - std::vector<std::vector<std::vector<std::vector<ValueType>>>> output_tensor( |
258 | | - batch_size, |
259 | | - std::vector<std::vector<std::vector<ValueType>>>( |
260 | | - out_channels, std::vector<std::vector<ValueType>>( |
261 | | - out_height, std::vector<ValueType>(out_width, 0)))); |
| 226 | + const size_t input_channel_stride = in_height * in_width; |
| 227 | + const size_t input_batch_stride = in_channels * input_channel_stride; |
| 228 | + const size_t kernel_channel_stride = kernel_height * kernel_width; |
| 229 | + const size_t kernel_output_stride = |
| 230 | + kernel_in_channels * kernel_channel_stride; |
| 231 | + const size_t output_channel_stride = out_height * out_width; |
| 232 | + const size_t output_batch_stride = out_channels * output_channel_stride; |
| 233 | + const size_t in_channels_per_group = in_channels / group_; |
| 234 | + const size_t out_channels_per_group = out_channels / group_; |
| 235 | + const bool collapsed_kernel = dilations_ == 0; |
262 | 236 |
|
| 237 | + Shape output_shape({batch_size, out_channels, out_height, out_width}); |
| 238 | + std::vector<ValueType> flat_output(output_shape.count(), 0); |
263 | 239 | size_t total_work = batch_size * out_channels; |
264 | 240 | parallel::parallel_for(total_work, [&](size_t idx) { |
265 | 241 | size_t b = idx / out_channels; |
266 | 242 | size_t oc = idx % out_channels; |
| 243 | + size_t input_batch_base = b * input_batch_stride; |
| 244 | + size_t output_base = b * output_batch_stride + oc * output_channel_stride; |
| 245 | + size_t group = (group_ > 1) ? oc / out_channels_per_group : 0; |
| 246 | + size_t group_start_channel = group * in_channels_per_group; |
| 247 | + size_t group_end_channel = group_start_channel + in_channels_per_group; |
| 248 | + size_t kernel_oc_base = oc * kernel_output_stride; |
| 249 | + ValueType bias_value = ValueType{}; |
| 250 | + if (bias_data != nullptr && oc < bias_data->size()) { |
| 251 | + bias_value = (*bias_data)[oc]; |
| 252 | + } |
267 | 253 |
|
268 | 254 | for (size_t oh = 0; oh < out_height; ++oh) { |
| 255 | + std::ptrdiff_t input_h_base = static_cast<std::ptrdiff_t>(oh * stride_) - |
| 256 | + static_cast<std::ptrdiff_t>(pads_); |
269 | 257 | for (size_t ow = 0; ow < out_width; ++ow) { |
270 | | - ValueType value = 0; |
271 | | - size_t h_start = oh * stride_; |
272 | | - size_t w_start = ow * stride_; |
273 | | - |
274 | | - size_t group = (group_ > 1) ? oc / (out_channels / group_) : 0; |
275 | | - size_t group_start_channel = group * (in_channels / group_); |
276 | | - size_t group_end_channel = (group + 1) * (in_channels / group_); |
| 258 | + ValueType value = bias_value; |
| 259 | + std::ptrdiff_t input_w_base = |
| 260 | + static_cast<std::ptrdiff_t>(ow * stride_) - |
| 261 | + static_cast<std::ptrdiff_t>(pads_); |
| 262 | + size_t output_idx = output_base + oh * out_width + ow; |
277 | 263 |
|
278 | 264 | for (size_t ic = group_start_channel; ic < group_end_channel; ++ic) { |
279 | 265 | size_t kernel_ic = ic - group_start_channel; |
280 | | - |
281 | | - for (size_t kh = 0; kh < dilated_kernel_height; ++kh) { |
282 | | - for (size_t kw = 0; kw < dilated_kernel_width; ++kw) { |
283 | | - size_t h_index = h_start + kh; |
284 | | - size_t w_index = w_start + kw; |
285 | | - |
286 | | - if (h_index < padded_input[b].size() && |
287 | | - w_index < padded_input[b][h_index].size()) { |
288 | | - value += padded_input[b][h_index][w_index][ic] * |
289 | | - dil_kernel[oc][kernel_ic][kh][kw]; |
290 | | - } |
| 266 | + size_t input_channel_base = |
| 267 | + input_batch_base + ic * input_channel_stride; |
| 268 | + size_t kernel_ic_base = |
| 269 | + kernel_oc_base + kernel_ic * kernel_channel_stride; |
| 270 | + |
| 271 | + if (collapsed_kernel) { |
| 272 | + if (input_h_base >= 0 && |
| 273 | + input_h_base < static_cast<std::ptrdiff_t>(in_height) && |
| 274 | + input_w_base >= 0 && |
| 275 | + input_w_base < static_cast<std::ptrdiff_t>(in_width)) { |
| 276 | + size_t input_idx = input_channel_base + |
| 277 | + static_cast<size_t>(input_h_base) * in_width + |
| 278 | + static_cast<size_t>(input_w_base); |
| 279 | + size_t kernel_idx = kernel_ic_base + kernel_channel_stride - 1; |
| 280 | + value += input_data[input_idx] * kernel_data[kernel_idx]; |
291 | 281 | } |
| 282 | + continue; |
292 | 283 | } |
293 | | - } |
294 | 284 |
|
295 | | - if (!bias_.empty() && oc < bias_.get_shape()[0]) { |
296 | | - value += bias_.get<ValueType>({oc}); |
297 | | - } |
| 285 | + for (size_t kh = 0; kh < kernel_height; ++kh) { |
| 286 | + std::ptrdiff_t input_h = |
| 287 | + input_h_base + static_cast<std::ptrdiff_t>(kh * dilations_); |
| 288 | + if (input_h < 0 || |
| 289 | + input_h >= static_cast<std::ptrdiff_t>(in_height)) { |
| 290 | + continue; |
| 291 | + } |
298 | 292 |
|
299 | | - output_tensor[b][oc][oh][ow] = value; |
300 | | - } |
301 | | - } |
302 | | - }, options); |
| 293 | + size_t input_row_base = |
| 294 | + input_channel_base + static_cast<size_t>(input_h) * in_width; |
| 295 | + size_t kernel_row_base = kernel_ic_base + kh * kernel_width; |
303 | 296 |
|
304 | | - Shape output_shape({batch_size, out_channels, out_height, out_width}); |
305 | | - std::vector<ValueType> flat_output(batch_size * out_channels * out_height * |
306 | | - out_width); |
| 297 | + for (size_t kw = 0; kw < kernel_width; ++kw) { |
| 298 | + std::ptrdiff_t input_w = |
| 299 | + input_w_base + static_cast<std::ptrdiff_t>(kw * dilations_); |
| 300 | + if (input_w < 0 || |
| 301 | + input_w >= static_cast<std::ptrdiff_t>(in_width)) { |
| 302 | + continue; |
| 303 | + } |
307 | 304 |
|
308 | | - parallel::parallel_for(batch_size, [&](size_t b) { |
309 | | - size_t base_idx = b * out_channels * out_height * out_width; |
310 | | - for (size_t oc = 0; oc < out_channels; ++oc) { |
311 | | - for (size_t h = 0; h < out_height; ++h) { |
312 | | - for (size_t w = 0; w < out_width; ++w) { |
313 | | - flat_output[base_idx++] = output_tensor[b][oc][h][w]; |
| 305 | + value += |
| 306 | + input_data[input_row_base + static_cast<size_t>(input_w)] * |
| 307 | + kernel_data[kernel_row_base + kw]; |
| 308 | + } |
| 309 | + } |
314 | 310 | } |
| 311 | + |
| 312 | + flat_output[output_idx] = value; |
315 | 313 | } |
316 | 314 | } |
317 | 315 | }, options); |
|
0 commit comments