@@ -34,7 +34,15 @@ Tensor& quantized_mul_out(
3434 const Scalar& output_shift,
3535 Tensor& out) {
3636 // Validate tensor types and quantization parameters
37- validate_cmsis_nn_tensor_requirements (input1_int8, input2_int8, out);
37+
38+ bool channel_broadcast = is_channel_broadcast (input1_int8, input2_int8);
39+ validate_cmsis_nn_tensor_requirements (
40+ input1_int8,
41+ input2_int8,
42+ out,
43+ ScalarType::Char,
44+ /* require_channels_last=*/ channel_broadcast,
45+ /* require_same_sizes=*/ !channel_broadcast);
3846
3947 const Scalar kIdentityMultiplier (/* value=*/ 1 );
4048 const Scalar kZeroShift (/* value=*/ 0 );
@@ -51,12 +59,26 @@ Tensor& quantized_mul_out(
5159 out);
5260
5361 // Extract quantization parameters
54- const int32_t zp1 = extractScalarToInt32 (input1_zero_point);
55- const int32_t zp2 = extractScalarToInt32 (input2_zero_point);
62+ int8_t * input1_ptr = input1_int8.data_ptr <int8_t >();
63+ int8_t * input2_ptr = input2_int8.data_ptr <int8_t >();
64+ int32_t zp1 = extractScalarToInt32 (input1_zero_point);
65+ int32_t zp2 = extractScalarToInt32 (input2_zero_point);
5666 const int32_t out_zp = extractScalarToInt32 (output_zero_point);
5767 const int32_t output_mult = extractScalarToInt32 (output_multiplier);
5868 const int32_t output_shift_val = extractScalarToInt32 (output_shift);
5969
70+ int32_t muls_per_loop = 0 ;
71+
72+ if (channel_broadcast) {
73+ if (input1_int8.numel () < input2_int8.numel ()) {
74+ std::swap<int32_t >(zp1, zp2);
75+ std::swap<int8_t *>(input1_ptr, input2_ptr);
76+ }
77+
78+ muls_per_loop = input1_int8.size (1 );
79+ } else {
80+ muls_per_loop = out.numel ();
81+ }
6082 // Note 1: The CMSIS-NN kernel implementation uses offsets which are always
6183 // added to the data, whereas zero_points are subtracted when dequantizing
6284 // (for the inputs) and added when quantizing (for the output). Hence the
@@ -72,29 +94,31 @@ Tensor& quantized_mul_out(
7294 // effective_scale = (scale_in1 * scale_in2 / scale_out)
7395 // Hence no input quantization params required here.
7496
75- // Call CMSIS-NN elementwise multiply kernel
76- arm_cmsis_nn_status status = arm_elementwise_mul_s8 (
77- input1_int8.const_data_ptr <int8_t >(),
78- input2_int8.const_data_ptr <int8_t >(),
79- -static_cast <int32_t >(zp1),
80- -static_cast <int32_t >(zp2),
81- out.mutable_data_ptr <int8_t >(),
82- static_cast <int32_t >(out_zp),
83- output_mult,
84- output_shift_val,
85- kInt8ActivationMin ,
86- kInt8ActivationMax ,
87- static_cast <int32_t >(out.numel ()));
88-
89- if (status != ARM_CMSIS_NN_SUCCESS ) {
90- ET_LOG (
91- Error,
92- " quantized_mul_out: arm_elementwise_mul_s8 failed with status [%d]" ,
93- status);
94- context.fail (Error::Internal);
95- return out;
96- }
97+ for (int32_t broadcast_offset = 0 ; broadcast_offset < out.numel ();
98+ broadcast_offset += muls_per_loop) {
99+ // Call CMSIS-NN elementwise multiply kernel
100+ arm_cmsis_nn_status status = arm_elementwise_mul_s8 (
101+ input1_ptr + broadcast_offset,
102+ input2_ptr,
103+ -static_cast <int32_t >(zp1),
104+ -static_cast <int32_t >(zp2),
105+ out.mutable_data_ptr <int8_t >() + broadcast_offset,
106+ static_cast <int32_t >(out_zp),
107+ output_mult,
108+ output_shift_val,
109+ kInt8ActivationMin ,
110+ kInt8ActivationMax ,
111+ muls_per_loop);
97112
113+ if (status != ARM_CMSIS_NN_SUCCESS ) {
114+ ET_LOG (
115+ Error,
116+ " quantized_mul_out: arm_elementwise_mul_s8 failed with status [%d]" ,
117+ status);
118+ context.fail (Error::Internal);
119+ return out;
120+ }
121+ }
98122 return out;
99123}
100124
0 commit comments