@@ -223,6 +223,71 @@ inline void matxDirectConv1DInternal(OutputType &o, const InType &i,
223223#endif
224224}
225225
226+ template <typename OutputType, typename InType, typename FilterType, typename Executor>
227+ requires is_host_executor_v<Executor>
228+ inline void matxDirectConv1DInternal (OutputType &o, const InType &i,
229+ const FilterType &filter, matxConvCorrMode_t mode,
230+ [[maybe_unused]] const Executor &exec)
231+ {
232+ MATX_NVTX_START (" " , matx::MATX_NVTX_LOG_INTERNAL )
233+
234+ MATX_STATIC_ASSERT (OutputType::Rank () == InType::Rank (), matxInvalidDim);
235+ MATX_STATIC_ASSERT (OutputType::Rank () == FilterType::Rank (), matxInvalidDim);
236+
237+ constexpr int Rank = OutputType::Rank ();
238+ const index_t signal_len = i.Size (Rank - 1 );
239+ const index_t filter_len = filter.Size (Rank - 1 );
240+ [[maybe_unused]] const index_t full_len = signal_len + filter_len - 1 ;
241+
242+ MATX_ASSERT_STR (mode != MATX_C_MODE_FULL || o.Size (Rank - 1 ) == full_len,
243+ matxInvalidSize, " Output size for FULL convolution incorrect" );
244+ MATX_ASSERT_STR (mode != MATX_C_MODE_SAME || o.Size (Rank - 1 ) == signal_len,
245+ matxInvalidSize, " Output size for SAME convolution incorrect" );
246+ MATX_ASSERT_STR (mode != MATX_C_MODE_VALID || o.Size (Rank - 1 ) == signal_len - filter_len + 1 ,
247+ matxInvalidSize, " Output size for VALID convolution incorrect" );
248+
249+ index_t start = 0 ;
250+ if (mode == MATX_C_MODE_SAME ) {
251+ start = (filter_len & 1 ) ? (filter_len - 1 ) / 2 : filter_len / 2 - 1 ;
252+ }
253+ else if (mode == MATX_C_MODE_VALID ) {
254+ start = filter_len - 1 ;
255+ }
256+
257+ const index_t total = static_cast <index_t >(TotalSize (o));
258+
259+ #ifdef MATX_EN_OMP
260+ #pragma omp parallel for num_threads(exec.GetNumThreads()) if(exec.GetNumThreads() > 1)
261+ #endif
262+ for (index_t abs = 0 ; abs < total; abs++) {
263+ auto odims = GetIdxFromAbs (o, abs);
264+ const index_t full_idx = odims[Rank - 1 ] + start;
265+
266+ typename OutputType::value_type sum{0 };
267+ for (index_t f = 0 ; f < filter_len; f++) {
268+ const index_t sig_idx = full_idx - f;
269+ if (sig_idx >= 0 && sig_idx < signal_len) {
270+ auto idims = odims;
271+ auto fdims = odims;
272+ idims[Rank - 1 ] = sig_idx;
273+ fdims[Rank - 1 ] = f;
274+
275+ const auto ival = cuda::std::apply ([&](auto &&...args ) {
276+ return i.operator ()(args...);
277+ }, idims);
278+ const auto fval = cuda::std::apply ([&](auto &&...args ) {
279+ return filter.operator ()(args...);
280+ }, fdims);
281+ sum = detail::madd (ival, fval, sum);
282+ }
283+ }
284+
285+ cuda::std::apply ([&](auto &&...args ) {
286+ o.operator ()(args...) = sum;
287+ }, odims);
288+ }
289+ }
290+
226291
227292template <typename OutputType, typename In1Type, typename In2Type>
228293void matxDirectConv2DInternal (OutputType &o, In1Type &in1,
@@ -256,6 +321,82 @@ void matxDirectConv2DInternal(OutputType &o, In1Type &in1,
256321 Conv2D<OutputType, In1Type, In2Type, BLOCK_X , BLOCK_Y , FILTER_SHARED_X , FILTER_SHARED_Y , FILTER_REG_X , FILTER_REG_Y , ILPY ><<<blocks, threads, 0 , stream>>>(o, in1, in2, mode, num_batch);
257322#endif
258323}
324+
325+ template <typename OutputType, typename In1Type, typename In2Type, typename Executor>
326+ requires is_host_executor_v<Executor>
327+ void matxDirectConv2DInternal (OutputType &o, const In1Type &in1,
328+ const In2Type &in2, matxConvCorrMode_t mode,
329+ [[maybe_unused]] const Executor &exec)
330+ {
331+ MATX_NVTX_START (" " , matx::MATX_NVTX_LOG_INTERNAL )
332+
333+ MATX_STATIC_ASSERT (OutputType::Rank () == In1Type::Rank (), matxInvalidDim);
334+ MATX_STATIC_ASSERT (OutputType::Rank () == In2Type::Rank (), matxInvalidDim);
335+ MATX_STATIC_ASSERT (OutputType::Rank () >= 2 , matxInvalidDim);
336+
337+ constexpr int Rank = OutputType::Rank ();
338+
339+ const index_t i1N = in1.Size (Rank - 2 );
340+ const index_t i1M = in1.Size (Rank - 1 );
341+ const index_t i2N = in2.Size (Rank - 2 );
342+ const index_t i2M = in2.Size (Rank - 1 );
343+
344+ index_t dy = 0 ;
345+ index_t dx = 0 ;
346+ if (mode == MATX_C_MODE_SAME ) {
347+ dy = i2N / 2 ;
348+ dx = i2M / 2 ;
349+ }
350+ else if (mode == MATX_C_MODE_FULL ) {
351+ dy = i2N - 1 ;
352+ dx = i2M - 1 ;
353+ }
354+
355+ const index_t total = static_cast <index_t >(TotalSize (o));
356+
357+ #ifdef MATX_EN_OMP
358+ #pragma omp parallel for num_threads(exec.GetNumThreads()) if(exec.GetNumThreads() > 1)
359+ #endif
360+ for (index_t abs = 0 ; abs < total; abs++) {
361+ auto odims = GetIdxFromAbs (o, abs);
362+ const index_t oy = odims[Rank - 2 ];
363+ const index_t ox = odims[Rank - 1 ];
364+
365+ typename OutputType::value_type sum{0 };
366+ for (index_t fy = 0 ; fy < i2N; fy++) {
367+ const index_t iy = oy + fy - dy;
368+ if (iy < 0 || iy >= i1N) {
369+ continue ;
370+ }
371+
372+ for (index_t fx = 0 ; fx < i2M; fx++) {
373+ const index_t ix = ox + fx - dx;
374+ if (ix < 0 || ix >= i1M) {
375+ continue ;
376+ }
377+
378+ auto i1dims = odims;
379+ auto i2dims = odims;
380+ i1dims[Rank - 2 ] = iy;
381+ i1dims[Rank - 1 ] = ix;
382+ i2dims[Rank - 2 ] = i2N - 1 - fy;
383+ i2dims[Rank - 1 ] = i2M - 1 - fx;
384+
385+ const auto i1val = cuda::std::apply ([&](auto &&...args ) {
386+ return in1.operator ()(args...);
387+ }, i1dims);
388+ const auto i2val = cuda::std::apply ([&](auto &&...args ) {
389+ return in2.operator ()(args...);
390+ }, i2dims);
391+ sum = detail::madd (i1val, i2val, sum);
392+ }
393+ }
394+
395+ cuda::std::apply ([&](auto &&...args ) {
396+ o.operator ()(args...) = sum;
397+ }, odims);
398+ }
399+ }
259400} // end namespace detail
260401
261402template <typename OutputType, typename In1Type, typename In2Type, typename Executor>
@@ -401,42 +542,55 @@ inline void conv1d_impl(OutputType o, const In1Type &i1, const In2Type &i2,
401542 * @param in1 First input operator
402543 * @param in2 Second input operator
403544 * @param mode Convolution mode
404- * @param stream CUDA stream
545+ * @param exec Executor
405546 */
406- template <typename OutputType, typename In1Type, typename In2Type>
547+ template <typename OutputType, typename In1Type, typename In2Type, typename Executor >
407548inline void conv2d_impl (OutputType o, const In1Type in1, const In2Type in2,
408- matxConvCorrMode_t mode, cudaStream_t stream = 0 )
549+ matxConvCorrMode_t mode, const Executor &exec )
409550{
410551 MATX_NVTX_START (" " , matx::MATX_NVTX_LOG_API )
411552 constexpr int Rank1 = In1Type::Rank ();
412553 constexpr int Rank2 = In2Type::Rank ();
413554
414555 if constexpr (In1Type::Rank () == In2Type::Rank ()) {
415- index_t size1 = in1.Size (Rank1-1 ) * in1.Size (Rank1-2 );
416- index_t size2 = in2.Size (Rank2-1 ) * in2.Size (Rank2-2 );
417- // smaller size is the filter, set it as second input
418- if (size1 >= size2) {
419- detail::matxDirectConv2DInternal (o, in1, in2, mode, stream);
420- } else { // swap in1/in2
421- detail::matxDirectConv2DInternal (o, in2, in1, mode, stream);
422- }
423- // These branches clone the inputs to match in rank
424- } else if constexpr (In1Type::Rank () <In2Type::Rank ()) {
425- // in1 is smaller so clone it to match in2
426- auto shape = in2.Shape ();
427- int d = Rank2 - Rank1;
428- for (int i = 0 ; i < Rank1; i++) {
429- shape[i+d] = matxKeepDim;
556+ auto run_direct = [&](const auto &signal, const auto &filter) {
557+ if constexpr (is_cuda_executor_v<Executor>) {
558+ detail::matxDirectConv2DInternal (o, signal, filter, mode, exec.getStream ());
430559 }
431- conv2d_impl (o, clone<Rank2>(in1, shape), in2, mode, stream);
432- } else {
433- // in1 is smaller so clone it to match in2
434- auto shape = in1.Shape ();
435- int d = Rank1 - Rank2;
436- for (int i = 0 ; i < Rank2; i++) {
437- shape[i+d] = matxKeepDim;
560+ else if constexpr (is_host_executor_v<Executor>) {
561+ detail::matxDirectConv2DInternal (o, signal, filter, mode, exec);
562+ }
563+ else {
564+ static_assert (is_cuda_executor_v<Executor> || is_host_executor_v<Executor>,
565+ " conv2d() only supports CUDA and host executors currently" );
438566 }
439- conv2d_impl (o, in1, clone<Rank1>(in2, shape), mode, stream);
567+ };
568+
569+ index_t size1 = in1.Size (Rank1-1 ) * in1.Size (Rank1-2 );
570+ index_t size2 = in2.Size (Rank2-1 ) * in2.Size (Rank2-2 );
571+ // smaller size is the filter, set it as second input
572+ if (size1 >= size2) {
573+ run_direct (in1, in2);
574+ } else { // swap in1/in2
575+ run_direct (in2, in1);
576+ }
577+ // These branches clone the inputs to match in rank
578+ } else if constexpr (In1Type::Rank () < In2Type::Rank ()) {
579+ // in1 is smaller so clone it to match in2
580+ auto shape = in2.Shape ();
581+ int d = Rank2 - Rank1;
582+ for (int i = 0 ; i < Rank1; i++) {
583+ shape[i+d] = matxKeepDim;
584+ }
585+ conv2d_impl (o, clone<Rank2>(in1, shape), in2, mode, exec);
586+ } else {
587+ // in1 is smaller so clone it to match in2
588+ auto shape = in1.Shape ();
589+ int d = Rank1 - Rank2;
590+ for (int i = 0 ; i < Rank2; i++) {
591+ shape[i+d] = matxKeepDim;
592+ }
593+ conv2d_impl (o, in1, clone<Rank1>(in2, shape), mode, exec);
440594 }
441595}
442596
0 commit comments