xref: /aosp_15_r20/external/XNNPACK/src/operators/global-average-pooling-nwc.c (revision 4bdc94577ba0e567308109d787f7fec7b531ce36)
1 // Copyright (c) Facebook, Inc. and its affiliates.
2 // All rights reserved.
3 //
4 // Copyright 2019 Google LLC
5 //
6 // This source code is licensed under the BSD-style license found in the
7 // LICENSE file in the root directory of this source tree.
8 
9 #include <assert.h>
10 #include <math.h>
11 #include <stddef.h>
12 #include <stdint.h>
13 #include <stdlib.h>
14 
15 #include <fp16.h>
16 
17 #include <xnnpack.h>
18 #include <xnnpack/allocator.h>
19 #include <xnnpack/log.h>
20 #include <xnnpack/operator.h>
21 #include <xnnpack/microparams-init.h>
22 #include <xnnpack/params.h>
23 
24 
create_global_average_pooling_nwc(size_t channels,size_t input_stride,size_t output_stride,uint32_t flags,uint32_t log2_element_size,size_t params_offset,const void * params,size_t params_size,uint32_t datatype_init_flags,enum xnn_operator_type operator_type,xnn_operator_t * global_average_pooling_op_out)25 static enum xnn_status create_global_average_pooling_nwc(
26     size_t channels,
27     size_t input_stride,
28     size_t output_stride,
29     uint32_t flags,
30     uint32_t log2_element_size,
31     size_t params_offset,
32     const void* params,
33     size_t params_size,
34     uint32_t datatype_init_flags,
35     enum xnn_operator_type operator_type,
36     xnn_operator_t* global_average_pooling_op_out)
37 {
38   xnn_operator_t global_average_pooling_op = NULL;
39   enum xnn_status status = xnn_status_uninitialized;
40 
41   if ((xnn_params.init_flags & XNN_INIT_FLAG_XNNPACK) == 0) {
42     xnn_log_error("failed to create %s operator: XNNPACK is not initialized",
43       xnn_operator_type_to_string(operator_type));
44     goto error;
45   }
46 
47   status = xnn_status_unsupported_hardware;
48 
49   if ((xnn_params.init_flags & datatype_init_flags) == 0) {
50     xnn_log_error("failed to create %s operator: operations on data type are not supported",
51       xnn_operator_type_to_string(operator_type));
52     goto error;
53   }
54 
55   status = xnn_status_invalid_parameter;
56 
57   if (channels == 0) {
58     xnn_log_error(
59       "failed to create %s operator with %zu channels: number of channels must be non-zero",
60       xnn_operator_type_to_string(operator_type), channels);
61     goto error;
62   }
63 
64   if (input_stride < channels) {
65     xnn_log_error(
66       "failed to create %s operator with input element stride of %zu: "
67       "stride must be at least as large as the number of channels (%zu)",
68       xnn_operator_type_to_string(operator_type), input_stride, channels);
69     goto error;
70   }
71 
72   if (output_stride < channels) {
73     xnn_log_error(
74       "failed to create %s operator with output element stride of %zu: "
75       "stride must be at least as large as the number of channels (%zu)",
76       xnn_operator_type_to_string(operator_type), output_stride, channels);
77     goto error;
78   }
79 
80   status = xnn_status_out_of_memory;
81 
82   global_average_pooling_op = xnn_allocate_zero_simd_memory(sizeof(struct xnn_operator));
83   if (global_average_pooling_op == NULL) {
84     xnn_log_error(
85       "failed to allocate %zu bytes for %s operator descriptor",
86       sizeof(struct xnn_operator), xnn_operator_type_to_string(operator_type));
87     goto error;
88   }
89 
90   const size_t zero_size = (channels << log2_element_size) + XNN_EXTRA_BYTES;
91   void* zero_buffer = xnn_allocate_zero_simd_memory(zero_size);
92   if (zero_buffer == NULL) {
93     xnn_log_error(
94       "failed to allocate %zu bytes for %s operator zero padding",
95       zero_size, xnn_operator_type_to_string(operator_type));
96     goto error;
97   }
98   global_average_pooling_op->zero_buffer = zero_buffer;
99 
100   global_average_pooling_op->channels = channels;
101   global_average_pooling_op->input_pixel_stride = input_stride;
102   global_average_pooling_op->output_pixel_stride = output_stride;
103   memcpy((void*) ((uintptr_t) global_average_pooling_op + params_offset), params, params_size);
104 
105   global_average_pooling_op->type = operator_type;
106   global_average_pooling_op->flags = flags;
107 
108   global_average_pooling_op->state = xnn_run_state_invalid;
109 
110   *global_average_pooling_op_out = global_average_pooling_op;
111   return xnn_status_success;
112 
113 error:
114   xnn_delete_operator(global_average_pooling_op);
115   return status;
116 }
117 
setup_global_average_pooling_nwc(xnn_operator_t global_average_pooling_op,size_t batch_size,size_t width,const void * input,void * output,size_t log2_element_size,const struct gavgpool_parameters gavgpool[restrict XNN_MIN_ELEMENTS (1)],uint32_t datatype_init_flags,enum xnn_operator_type expected_operator_type,const void * params,size_t params_size,void (* update_params)(xnn_operator_t,size_t),pthreadpool_t threadpool)118 static enum xnn_status setup_global_average_pooling_nwc(
119     xnn_operator_t global_average_pooling_op,
120     size_t batch_size,
121     size_t width,
122     const void* input,
123     void* output,
124     size_t log2_element_size,
125     const struct gavgpool_parameters gavgpool[restrict XNN_MIN_ELEMENTS(1)],
126     uint32_t datatype_init_flags,
127     enum xnn_operator_type expected_operator_type,
128     const void* params,
129     size_t params_size,
130     void (*update_params)(xnn_operator_t, size_t),
131     pthreadpool_t threadpool)
132 {
133   if (global_average_pooling_op->type != expected_operator_type) {
134     xnn_log_error("failed to setup operator: operator type mismatch (expected %s, got %s)",
135       xnn_operator_type_to_string(expected_operator_type),
136       xnn_operator_type_to_string(global_average_pooling_op->type));
137     return xnn_status_invalid_parameter;
138   }
139   global_average_pooling_op->state = xnn_run_state_invalid;
140 
141   if ((xnn_params.init_flags & XNN_INIT_FLAG_XNNPACK) == 0) {
142     xnn_log_error("failed to setup %s operator: XNNPACK is not initialized",
143       xnn_operator_type_to_string(global_average_pooling_op->type));
144     return xnn_status_uninitialized;
145   }
146 
147   if ((xnn_params.init_flags & datatype_init_flags) == 0) {
148     xnn_log_error("failed to setup %s operator: operations on data type are not supported",
149       xnn_operator_type_to_string(global_average_pooling_op->type));
150     return xnn_status_unsupported_hardware;
151   }
152 
153   if (width == 0) {
154     xnn_log_error("failed to setup %s operator with width %zu: width must be non-zero",
155       xnn_operator_type_to_string(global_average_pooling_op->type), width);
156     return xnn_status_invalid_parameter;
157   }
158 
159   if (batch_size == 0) {
160     global_average_pooling_op->state = xnn_run_state_skip;
161     return xnn_status_success;
162   }
163 
164   global_average_pooling_op->batch_size = batch_size;
165   global_average_pooling_op->input_width = width;
166   global_average_pooling_op->input = input;
167   global_average_pooling_op->output = output;
168 
169   update_params(global_average_pooling_op, width);
170 
171   assert(gavgpool->row_tile != 0);
172 
173   const size_t input_stride_in_bytes = global_average_pooling_op->input_pixel_stride << log2_element_size;
174   const size_t channels = global_average_pooling_op->channels;
175   global_average_pooling_op->context.global_average_pooling_nwc = (struct global_average_pooling_nwc_context) {
176       .input = input,
177       .zero = global_average_pooling_op->zero_buffer,
178       .input_pixel_stride = input_stride_in_bytes,
179       .input_batch_stride = input_stride_in_bytes * width,
180       .input_elements = width,
181       .channels = channels,
182       .output = output,
183       .output_batch_stride = (global_average_pooling_op->output_pixel_stride << log2_element_size),
184   };
185   memcpy(&global_average_pooling_op->context.global_average_pooling_nwc.params, params, params_size);
186   global_average_pooling_op->compute.type = xnn_parallelization_type_1d;
187   global_average_pooling_op->compute.range[0] = batch_size;
188 
189   if (width <= gavgpool->row_tile) {
190     global_average_pooling_op->compute.task_1d = (pthreadpool_task_1d_t) xnn_compute_global_average_pooling_nwc_unipass;
191     global_average_pooling_op->context.global_average_pooling_nwc.unipass_ukernel = gavgpool->unipass;
192   } else {
193     global_average_pooling_op->compute.task_1d = (pthreadpool_task_1d_t) xnn_compute_global_average_pooling_nwc_multipass;
194     global_average_pooling_op->context.global_average_pooling_nwc.multipass_ukernel = gavgpool->multipass;
195   }
196   global_average_pooling_op->state = xnn_run_state_ready;
197 
198   return xnn_status_success;
199 }
200 
xnn_create_global_average_pooling_nwc_qu8(size_t channels,size_t input_stride,size_t output_stride,uint8_t input_zero_point,float input_scale,uint8_t output_zero_point,float output_scale,uint8_t output_min,uint8_t output_max,uint32_t flags,xnn_operator_t * global_average_pooling_op_out)201 enum xnn_status xnn_create_global_average_pooling_nwc_qu8(
202     size_t channels,
203     size_t input_stride,
204     size_t output_stride,
205     uint8_t input_zero_point,
206     float input_scale,
207     uint8_t output_zero_point,
208     float output_scale,
209     uint8_t output_min,
210     uint8_t output_max,
211     uint32_t flags,
212     xnn_operator_t* global_average_pooling_op_out)
213 {
214   if (input_scale <= 0.0f || !isnormal(input_scale)) {
215     xnn_log_error(
216       "failed to create %s operator with %.7g input scale: scale must be finite, normalized, and positive",
217       xnn_operator_type_to_string(xnn_operator_type_global_average_pooling_nwc_qu8), input_scale);
218     return xnn_status_invalid_parameter;
219   }
220 
221   if (output_scale <= 0.0f || !isnormal(output_scale)) {
222     xnn_log_error(
223       "failed to create %s operator with %.7g output scale: scale must be finite, normalized, and positive",
224       xnn_operator_type_to_string(xnn_operator_type_global_average_pooling_nwc_qu8), output_scale);
225     return xnn_status_invalid_parameter;
226   }
227 
228   if (output_min >= output_max) {
229     xnn_log_error(
230       "failed to create %s operator with [%" PRIu8 ", %" PRIu8 "] output range: range min must be below range max",
231       xnn_operator_type_to_string(xnn_operator_type_global_average_pooling_nwc_qu8), output_min, output_max);
232     return xnn_status_invalid_parameter;
233   }
234 
235   const float input_output_scale = input_scale / output_scale;
236   if (input_output_scale < 0x1.0p-8f || input_output_scale >= 0x1.0p+8f) {
237     xnn_log_error(
238       "failed to create %s operator with %.7g input-to-output scale ratio: scale ratio must be in [2**-8, 2**8) range",
239       xnn_operator_type_to_string(xnn_operator_type_global_average_pooling_nwc_qu8), input_output_scale);
240     return xnn_status_unsupported_parameter;
241   }
242 
243   union xnn_qu8_avgpool_minmax_params params;
244   if (xnn_params.qu8.gavgpool.init.qu8 != NULL) {
245     xnn_params.qu8.gavgpool.init.qu8(&params,
246       0 /* bias */, 1.0f /* scale */, output_zero_point, output_min, output_max);
247   }
248   const enum xnn_status status = create_global_average_pooling_nwc(
249     channels, input_stride, output_stride, flags,
250     0 /* log2(sizeof(uint8_t)) */,
251     offsetof(struct xnn_operator, params.qu8_gavgpool),
252     &params, sizeof(params),
253     XNN_INIT_FLAG_QU8,
254     xnn_operator_type_global_average_pooling_nwc_qu8,
255     global_average_pooling_op_out);
256   if (status == xnn_status_success) {
257     xnn_operator_t global_average_pooling_op = *global_average_pooling_op_out;
258     global_average_pooling_op->input_zero_point = (int32_t) (uint32_t) input_zero_point;
259     global_average_pooling_op->input_scale = input_scale;
260     global_average_pooling_op->output_scale = output_scale;
261   }
262   return status;
263 }
264 
xnn_create_global_average_pooling_nwc_qs8(size_t channels,size_t input_stride,size_t output_stride,int8_t input_zero_point,float input_scale,int8_t output_zero_point,float output_scale,int8_t output_min,int8_t output_max,uint32_t flags,xnn_operator_t * global_average_pooling_op_out)265 enum xnn_status xnn_create_global_average_pooling_nwc_qs8(
266     size_t channels,
267     size_t input_stride,
268     size_t output_stride,
269     int8_t input_zero_point,
270     float input_scale,
271     int8_t output_zero_point,
272     float output_scale,
273     int8_t output_min,
274     int8_t output_max,
275     uint32_t flags,
276     xnn_operator_t* global_average_pooling_op_out)
277 {
278   if (input_scale <= 0.0f || !isnormal(input_scale)) {
279     xnn_log_error(
280       "failed to create %s operator with %.7g input scale: scale must be finite, normalized, and positive",
281       xnn_operator_type_to_string(xnn_operator_type_global_average_pooling_nwc_qs8), input_scale);
282     return xnn_status_invalid_parameter;
283   }
284 
285   if (output_scale <= 0.0f || !isnormal(output_scale)) {
286     xnn_log_error(
287       "failed to create %s operator with %.7g output scale: scale must be finite, normalized, and positive",
288       xnn_operator_type_to_string(xnn_operator_type_global_average_pooling_nwc_qs8), output_scale);
289     return xnn_status_invalid_parameter;
290   }
291 
292   if (output_min >= output_max) {
293     xnn_log_error(
294       "failed to create %s operator with [%" PRId8 ", %" PRId8 "] output range: range min must be below range max",
295       xnn_operator_type_to_string(xnn_operator_type_global_average_pooling_nwc_qs8), output_min, output_max);
296     return xnn_status_invalid_parameter;
297   }
298 
299   const float input_output_scale = input_scale / output_scale;
300   if (input_output_scale < 0x1.0p-8f || input_output_scale >= 0x1.0p+8f) {
301     xnn_log_error(
302       "failed to create %s operator with %.7g input-to-output scale ratio: scale ratio must be in [2**-8, 2**8) range",
303       xnn_operator_type_to_string(xnn_operator_type_global_average_pooling_nwc_qs8), input_output_scale);
304     return xnn_status_unsupported_parameter;
305   }
306 
307   union xnn_qs8_avgpool_minmax_params params;
308   if (xnn_params.qs8.gavgpool.init.qs8 != NULL) {
309     xnn_params.qs8.gavgpool.init.qs8(&params,
310       0 /* bias */, 1.0f /* scale */, output_zero_point, output_min, output_max);
311   }
312   const enum xnn_status status = create_global_average_pooling_nwc(
313     channels, input_stride, output_stride, flags,
314     0 /* log2(sizeof(int8_t)) */,
315     offsetof(struct xnn_operator, params.qs8_gavgpool),
316     &params, sizeof(params),
317     XNN_INIT_FLAG_QS8,
318     xnn_operator_type_global_average_pooling_nwc_qs8,
319     global_average_pooling_op_out);
320   if (status == xnn_status_success) {
321     xnn_operator_t global_average_pooling_op = *global_average_pooling_op_out;
322     global_average_pooling_op->input_zero_point = (int32_t) input_zero_point;
323     global_average_pooling_op->input_scale = input_scale;
324     global_average_pooling_op->output_scale = output_scale;
325   }
326   return status;
327 }
328 
xnn_create_global_average_pooling_nwc_f16(size_t channels,size_t input_stride,size_t output_stride,float output_min,float output_max,uint32_t flags,xnn_operator_t * global_average_pooling_op_out)329 enum xnn_status xnn_create_global_average_pooling_nwc_f16(
330     size_t channels,
331     size_t input_stride,
332     size_t output_stride,
333     float output_min,
334     float output_max,
335     uint32_t flags,
336     xnn_operator_t* global_average_pooling_op_out)
337 {
338   if (isnan(output_min)) {
339     xnn_log_error(
340       "failed to create %s operator with NaN output lower bound: lower bound must be non-NaN",
341       xnn_operator_type_to_string(xnn_operator_type_global_average_pooling_nwc_f16));
342     return xnn_status_invalid_parameter;
343   }
344 
345   if (isnan(output_max)) {
346     xnn_log_error(
347       "failed to create %s operator with NaN output upper bound: upper bound must be non-NaN",
348       xnn_operator_type_to_string(xnn_operator_type_global_average_pooling_nwc_f16));
349     return xnn_status_invalid_parameter;
350   }
351 
352   if (fp16_ieee_to_fp32_value(fp16_ieee_from_fp32_value(output_min)) >= fp16_ieee_to_fp32_value(fp16_ieee_from_fp32_value(output_max))) {
353     xnn_log_error(
354       "failed to create %s operator with [%.7g, %.7g] output range: lower bound must be below upper bound",
355       xnn_operator_type_to_string(xnn_operator_type_global_average_pooling_nwc_f16),
356       fp16_ieee_to_fp32_value(fp16_ieee_from_fp32_value(output_min)),
357       fp16_ieee_to_fp32_value(fp16_ieee_from_fp32_value(output_max)));
358     return xnn_status_invalid_parameter;
359   }
360 
361   union xnn_f16_scaleminmax_params params;
362   if (xnn_params.f16.gavgpool.init.f16 != NULL) {
363     xnn_params.f16.gavgpool.init.f16(&params,
364       0 /* scale */, fp16_ieee_from_fp32_value(output_min), fp16_ieee_from_fp32_value(output_max));
365   }
366   return create_global_average_pooling_nwc(
367     channels, input_stride, output_stride, flags,
368     1 /* log2(sizeof(uint16_t)) */,
369     offsetof(struct xnn_operator, params.f16_scaleminmax),
370     &params, sizeof(params),
371     XNN_INIT_FLAG_F16,
372     xnn_operator_type_global_average_pooling_nwc_f16,
373     global_average_pooling_op_out);
374 }
375 
xnn_create_global_average_pooling_nwc_f32(size_t channels,size_t input_stride,size_t output_stride,float output_min,float output_max,uint32_t flags,xnn_operator_t * global_average_pooling_op_out)376 enum xnn_status xnn_create_global_average_pooling_nwc_f32(
377     size_t channels,
378     size_t input_stride,
379     size_t output_stride,
380     float output_min,
381     float output_max,
382     uint32_t flags,
383     xnn_operator_t* global_average_pooling_op_out)
384 {
385   if (isnan(output_min)) {
386     xnn_log_error(
387       "failed to create %s operator with NaN output lower bound: lower bound must be non-NaN",
388       xnn_operator_type_to_string(xnn_operator_type_global_average_pooling_nwc_f32));
389     return xnn_status_invalid_parameter;
390   }
391 
392   if (isnan(output_max)) {
393     xnn_log_error(
394       "failed to create %s operator with NaN output upper bound: upper bound must be non-NaN",
395       xnn_operator_type_to_string(xnn_operator_type_global_average_pooling_nwc_f32));
396     return xnn_status_invalid_parameter;
397   }
398 
399   if (output_min >= output_max) {
400     xnn_log_error(
401       "failed to create %s operator with [%.7g, %.7g] output range: lower bound must be below upper bound",
402       xnn_operator_type_to_string(xnn_operator_type_global_average_pooling_nwc_f32), output_min, output_max);
403     return xnn_status_invalid_parameter;
404   }
405 
406   union xnn_f32_scaleminmax_params params;
407   if (xnn_params.f32.gavgpool.init.f32 != NULL) {
408     xnn_params.f32.gavgpool.init.f32(&params,
409       0.0f /* scale */, output_min, output_max);
410   }
411   return create_global_average_pooling_nwc(
412     channels, input_stride, output_stride, flags,
413     2 /* log2(sizeof(float)) */,
414     offsetof(struct xnn_operator, params.f32_scaleminmax),
415     &params, sizeof(params),
416     XNN_INIT_FLAG_F32,
417     xnn_operator_type_global_average_pooling_nwc_f32,
418     global_average_pooling_op_out);
419 }
420 
update_params_qu8(xnn_operator_t global_average_pooling_op,size_t width)421 static void update_params_qu8(
422   xnn_operator_t global_average_pooling_op,
423   size_t width)
424 {
425   const int32_t bias = -((int32_t) width * global_average_pooling_op->input_zero_point);
426   const float scale = global_average_pooling_op->input_scale / (global_average_pooling_op->output_scale * (float) width);
427   xnn_params.qu8.gavgpool.update.qu8(&global_average_pooling_op->params.qu8_gavgpool, bias, scale);
428 }
429 
xnn_setup_global_average_pooling_nwc_qu8(xnn_operator_t global_average_pooling_op,size_t batch_size,size_t width,const uint8_t * input,uint8_t * output,pthreadpool_t threadpool)430 enum xnn_status xnn_setup_global_average_pooling_nwc_qu8(
431     xnn_operator_t global_average_pooling_op,
432     size_t batch_size,
433     size_t width,
434     const uint8_t* input,
435     uint8_t* output,
436     pthreadpool_t threadpool)
437 {
438   return setup_global_average_pooling_nwc(
439     global_average_pooling_op,
440     batch_size, width,
441     input, output,
442     0 /* log2(sizeof(uint8_t)) */,
443     &xnn_params.qu8.gavgpool,
444     XNN_INIT_FLAG_QU8,
445     xnn_operator_type_global_average_pooling_nwc_qu8,
446     &global_average_pooling_op->params.qu8_gavgpool,
447     sizeof(global_average_pooling_op->params.qu8_gavgpool),
448     update_params_qu8,
449     threadpool);
450 }
451 
update_params_qs8(xnn_operator_t global_average_pooling_op,size_t width)452 static void update_params_qs8(
453   xnn_operator_t global_average_pooling_op,
454   size_t width)
455 {
456   const int32_t bias = -((int32_t) width * global_average_pooling_op->input_zero_point);
457   const float scale = global_average_pooling_op->input_scale / (global_average_pooling_op->output_scale * (float) width);
458   xnn_params.qs8.gavgpool.update.qs8(&global_average_pooling_op->params.qs8_gavgpool, bias, scale);
459 }
460 
xnn_setup_global_average_pooling_nwc_qs8(xnn_operator_t global_average_pooling_op,size_t batch_size,size_t width,const int8_t * input,int8_t * output,pthreadpool_t threadpool)461 enum xnn_status xnn_setup_global_average_pooling_nwc_qs8(
462     xnn_operator_t global_average_pooling_op,
463     size_t batch_size,
464     size_t width,
465     const int8_t* input,
466     int8_t* output,
467     pthreadpool_t threadpool)
468 {
469   return setup_global_average_pooling_nwc(
470     global_average_pooling_op,
471     batch_size, width,
472     input, output,
473     0 /* log2(sizeof(int8_t)) */,
474     &xnn_params.qs8.gavgpool,
475     XNN_INIT_FLAG_QS8,
476     xnn_operator_type_global_average_pooling_nwc_qs8,
477     &global_average_pooling_op->params.qs8_gavgpool,
478     sizeof(global_average_pooling_op->params.qs8_gavgpool),
479     update_params_qs8,
480     threadpool);
481 }
482 
update_params_f16(xnn_operator_t global_average_pooling_op,size_t width)483 static void update_params_f16(
484   xnn_operator_t global_average_pooling_op,
485   size_t width)
486 {
487   xnn_params.f16.gavgpool.update.f16(
488     &global_average_pooling_op->params.f16_scaleminmax,
489     fp16_ieee_from_fp32_value(1.0f / (float) width));
490 }
491 
xnn_setup_global_average_pooling_nwc_f16(xnn_operator_t global_average_pooling_op,size_t batch_size,size_t width,const void * input,void * output,pthreadpool_t threadpool)492 enum xnn_status xnn_setup_global_average_pooling_nwc_f16(
493     xnn_operator_t global_average_pooling_op,
494     size_t batch_size,
495     size_t width,
496     const void* input,
497     void* output,
498     pthreadpool_t threadpool)
499 {
500   return setup_global_average_pooling_nwc(
501     global_average_pooling_op,
502     batch_size, width,
503     input, output,
504     1 /* log2(sizeof(uint16_t)) */,
505     &xnn_params.f16.gavgpool,
506     XNN_INIT_FLAG_F16,
507     xnn_operator_type_global_average_pooling_nwc_f16,
508     &global_average_pooling_op->params.f16_scaleminmax,
509     sizeof(global_average_pooling_op->params.f16_scaleminmax),
510     update_params_f16,
511     threadpool);
512 }
513 
update_params_f32(xnn_operator_t global_average_pooling_op,size_t width)514 static void update_params_f32(
515   xnn_operator_t global_average_pooling_op,
516   size_t width)
517 {
518   xnn_params.f32.gavgpool.update.f32(&global_average_pooling_op->params.f32_scaleminmax, 1.0f / (float) width);
519 }
520 
xnn_setup_global_average_pooling_nwc_f32(xnn_operator_t global_average_pooling_op,size_t batch_size,size_t width,const float * input,float * output,pthreadpool_t threadpool)521 enum xnn_status xnn_setup_global_average_pooling_nwc_f32(
522     xnn_operator_t global_average_pooling_op,
523     size_t batch_size,
524     size_t width,
525     const float* input,
526     float* output,
527     pthreadpool_t threadpool)
528 {
529   return setup_global_average_pooling_nwc(
530     global_average_pooling_op,
531     batch_size, width,
532     input, output,
533     2 /* log2(sizeof(float)) */,
534     &xnn_params.f32.gavgpool,
535     XNN_INIT_FLAG_F32,
536     xnn_operator_type_global_average_pooling_nwc_f32,
537     &global_average_pooling_op->params.f32_scaleminmax,
538     sizeof(global_average_pooling_op->params.f32_scaleminmax),
539     update_params_f32,
540     threadpool);
541 }
542