aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGian Marco Iodice <gianmarco.iodice@arm.com>2019-07-26 15:31:02 +0100
committerGian Marco Iodice <gianmarco.iodice@arm.com>2019-07-29 12:29:07 +0000
commitbd9097db81f229c2d7bbafc2bcf392b7c1c49b58 (patch)
treeb86cda1c686e4466ce7927b66aba15e49b0c6139
parent44f5572f3d6ba8e39c4a18a991049992d590ce39 (diff)
downloadComputeLibrary-bd9097db81f229c2d7bbafc2bcf392b7c1c49b58.tar.gz
COMPMID-2336: Rename the new generic depthwise convolution on NEON
Change-Id: I45cacf75b08bb9d867343037507e56f200ad6ac0 Signed-off-by: Gian Marco Iodice <gianmarco.iodice@arm.com> Reviewed-on: https://review.mlplatform.org/c/1637 Tested-by: Arm Jenkins <bsgcomp@arm.com> Reviewed-by: Giorgio Arena <giorgio.arena@arm.com> Comments-Addressed: Arm Jenkins <bsgcomp@arm.com>
-rw-r--r--arm_compute/core/NEON/NEKernels.h2
-rw-r--r--arm_compute/core/NEON/kernels/NEDepthwiseConvolutionLayerNativeKernel.h (renamed from arm_compute/core/NEON/kernels/NEDepthwiseConvolutionLayerKernel.h)28
-rw-r--r--arm_compute/runtime/NEON/functions/NEDepthwiseConvolutionLayer.h6
-rw-r--r--src/core/NEON/kernels/NEDepthwiseConvolutionLayerNativeKernel.cpp (renamed from src/core/NEON/kernels/NEDepthwiseConvolutionLayerKernel.cpp)22
-rw-r--r--src/runtime/NEON/functions/NEDepthwiseConvolutionLayer.cpp2
-rw-r--r--tests/validation/NEON/DepthwiseConvolutionNativeLayer.cpp (renamed from tests/validation/NEON/DepthwiseConvolutionLayerKernel.cpp)73
-rw-r--r--tests/validation/fixtures/DepthwiseConvolutionLayerFixture.h2
7 files changed, 82 insertions, 53 deletions
diff --git a/arm_compute/core/NEON/NEKernels.h b/arm_compute/core/NEON/NEKernels.h
index 8fddec2c5f..bf77e576ad 100644
--- a/arm_compute/core/NEON/NEKernels.h
+++ b/arm_compute/core/NEON/NEKernels.h
@@ -53,7 +53,7 @@
#include "arm_compute/core/NEON/kernels/NEDepthConvertLayerKernel.h"
#include "arm_compute/core/NEON/kernels/NEDepthToSpaceLayerKernel.h"
#include "arm_compute/core/NEON/kernels/NEDepthwiseConvolutionLayer3x3Kernel.h"
-#include "arm_compute/core/NEON/kernels/NEDepthwiseConvolutionLayerKernel.h"
+#include "arm_compute/core/NEON/kernels/NEDepthwiseConvolutionLayerNativeKernel.h"
#include "arm_compute/core/NEON/kernels/NEDepthwiseIm2ColKernel.h"
#include "arm_compute/core/NEON/kernels/NEDepthwiseVectorToTensorKernel.h"
#include "arm_compute/core/NEON/kernels/NEDepthwiseWeightsReshapeKernel.h"
diff --git a/arm_compute/core/NEON/kernels/NEDepthwiseConvolutionLayerKernel.h b/arm_compute/core/NEON/kernels/NEDepthwiseConvolutionLayerNativeKernel.h
index 63635b3a6c..5db79f8bf7 100644
--- a/arm_compute/core/NEON/kernels/NEDepthwiseConvolutionLayerKernel.h
+++ b/arm_compute/core/NEON/kernels/NEDepthwiseConvolutionLayerNativeKernel.h
@@ -21,8 +21,8 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
-#ifndef __ARM_COMPUTE_NEDEPTHWISECONVOLUTIONKERNEL_H__
-#define __ARM_COMPUTE_NEDEPTHWISECONVOLUTIONKERNEL_H__
+#ifndef __ARM_COMPUTE_NEDEPTHWISECONVOLUTIONLAYERNATIVEKERNEL_H__
+#define __ARM_COMPUTE_NEDEPTHWISECONVOLUTIONLAYERNATIVEKERNEL_H__
#include "arm_compute/core/NEON/INEKernel.h"
@@ -31,24 +31,24 @@ namespace arm_compute
// Forward declarations
class ITensor;
-/** Interface for the kernel to run a depthwise convolution on a tensor. */
-class NEDepthwiseConvolutionLayerKernel : public INEKernel
+/** Interface for the kernel to run a depthwise convolution native on a tensor. */
+class NEDepthwiseConvolutionLayerNativeKernel : public INEKernel
{
public:
const char *name() const override
{
- return "NEDepthwiseConvolutionLayerKernel";
+ return "NEDepthwiseConvolutionLayerNativeKernel";
}
/** Default constructor */
- NEDepthwiseConvolutionLayerKernel();
+ NEDepthwiseConvolutionLayerNativeKernel();
/** Prevent instances of this class from being copied (As this class contains pointers) */
- NEDepthwiseConvolutionLayerKernel(const NEDepthwiseConvolutionLayerKernel &) = delete;
+ NEDepthwiseConvolutionLayerNativeKernel(const NEDepthwiseConvolutionLayerNativeKernel &) = delete;
/** Prevent instances of this class from being copied (As this class contains pointers) */
- NEDepthwiseConvolutionLayerKernel &operator=(const NEDepthwiseConvolutionLayerKernel &) = delete;
+ NEDepthwiseConvolutionLayerNativeKernel &operator=(const NEDepthwiseConvolutionLayerNativeKernel &) = delete;
/** Default Move Constructor. */
- NEDepthwiseConvolutionLayerKernel(NEDepthwiseConvolutionLayerKernel &&) = default;
+ NEDepthwiseConvolutionLayerNativeKernel(NEDepthwiseConvolutionLayerNativeKernel &&) = default;
/** Default move assignment operator */
- NEDepthwiseConvolutionLayerKernel &operator=(NEDepthwiseConvolutionLayerKernel &&) = default;
+ NEDepthwiseConvolutionLayerNativeKernel &operator=(NEDepthwiseConvolutionLayerNativeKernel &&) = default;
/** Initialize the function's source, destination and parameters.
*
* @note Supported data layouts: NHWC
@@ -64,7 +64,7 @@ public:
*/
void configure(const ITensor *input, const ITensor *weights, const ITensor *biases, ITensor *output, const PadStrideInfo &conv_info, unsigned int depth_multiplier = 1,
const Size2D &dilation = Size2D(1U, 1U));
- /** Static function to check if given info will lead to a valid configuration of @ref NEDepthwiseConvolutionLayerKernel
+ /** Static function to check if given info will lead to a valid configuration of @ref NEDepthwiseConvolutionLayerNativeKernel
*
* @note Supported data layouts: NHWC
*
@@ -89,11 +89,11 @@ private:
template <typename T, int S, bool has_biases>
void run_depthwise(const Window &window);
- /** Common signature for all the specialised depthwise convolution functions
+ /** Common signature for all the specialised depthwise convolution native functions
*
* @param[in] window Region on which to execute the kernel.
*/
- using DepthwiseFunctionPtr = void (NEDepthwiseConvolutionLayerKernel::*)(const Window &window);
+ using DepthwiseFunctionPtr = void (NEDepthwiseConvolutionLayerNativeKernel::*)(const Window &window);
DepthwiseFunctionPtr _func;
BorderSize _border_size;
@@ -106,4 +106,4 @@ private:
Size2D _dilation;
};
} // namespace arm_compute
-#endif /* __ARM_COMPUTE_NEDEPTHWISECONVOLUTIONKERNEL_H__ */
+#endif /* __ARM_COMPUTE_NEDEPTHWISECONVOLUTIONLAYERNATIVEKERNEL_H__ */
diff --git a/arm_compute/runtime/NEON/functions/NEDepthwiseConvolutionLayer.h b/arm_compute/runtime/NEON/functions/NEDepthwiseConvolutionLayer.h
index 5b0d1bafcd..87405fdb14 100644
--- a/arm_compute/runtime/NEON/functions/NEDepthwiseConvolutionLayer.h
+++ b/arm_compute/runtime/NEON/functions/NEDepthwiseConvolutionLayer.h
@@ -25,7 +25,7 @@
#define __ARM_COMPUTE_NEDEPTHWISECONVOLUTION_H__
#include "arm_compute/core/NEON/kernels/NEDepthwiseConvolutionLayer3x3Kernel.h"
-#include "arm_compute/core/NEON/kernels/NEDepthwiseConvolutionLayerKernel.h"
+#include "arm_compute/core/NEON/kernels/NEDepthwiseConvolutionLayerNativeKernel.h"
#include "arm_compute/core/NEON/kernels/NEDepthwiseIm2ColKernel.h"
#include "arm_compute/core/NEON/kernels/NEDepthwiseVectorToTensorKernel.h"
#include "arm_compute/core/NEON/kernels/NEDepthwiseWeightsReshapeKernel.h"
@@ -282,7 +282,7 @@ private:
/** Basic function to execute a generic depthwise convolution. This function calls the following NEON kernels:
*
* If data type is F32 and data layout is NHWC:
- * -# @ref NEDepthwiseConvolutionLayerKernel
+ * -# @ref NEDepthwiseConvolutionLayerNativeKernel
*
* Otherwise:
* -# @ref NEDepthwiseIm2ColKernel
@@ -344,7 +344,7 @@ private:
NEDepthwiseIm2ColKernel _im2col_kernel;
NEDepthwiseWeightsReshapeKernel _weights_reshape_kernel;
NEGEMMMatrixVectorMultiplyKernel _v2mm_kernel;
- NEDepthwiseConvolutionLayerKernel _depthwise_conv_kernel;
+ NEDepthwiseConvolutionLayerNativeKernel _depthwise_conv_kernel;
NEDepthwiseVectorToTensorKernel _vector_to_tensor_kernel;
NEDirectConvolutionLayerOutputStageKernel _output_stage_kernel;
NEFillBorderKernel _fill_border;
diff --git a/src/core/NEON/kernels/NEDepthwiseConvolutionLayerKernel.cpp b/src/core/NEON/kernels/NEDepthwiseConvolutionLayerNativeKernel.cpp
index feb2071d47..aafdb2e8a4 100644
--- a/src/core/NEON/kernels/NEDepthwiseConvolutionLayerKernel.cpp
+++ b/src/core/NEON/kernels/NEDepthwiseConvolutionLayerNativeKernel.cpp
@@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
-#include "arm_compute/core/NEON/kernels/NEDepthwiseConvolutionLayerKernel.h"
+#include "arm_compute/core/NEON/kernels/NEDepthwiseConvolutionLayerNativeKernel.h"
#include "arm_compute/core/AccessWindowStatic.h"
#include "arm_compute/core/NEON/wrapper/traits.h"
@@ -252,18 +252,18 @@ std::pair<Status, Window> validate_and_configure_window(ITensorInfo *input, ITen
return std::make_pair(err, win);
}
-NEDepthwiseConvolutionLayerKernel::NEDepthwiseConvolutionLayerKernel()
+NEDepthwiseConvolutionLayerNativeKernel::NEDepthwiseConvolutionLayerNativeKernel()
: _func(), _border_size(0), _input(), _weights(), _biases(), _output(), _conv_info(), _depth_multiplier(1), _dilation()
{
}
-BorderSize NEDepthwiseConvolutionLayerKernel::border_size() const
+BorderSize NEDepthwiseConvolutionLayerNativeKernel::border_size() const
{
return _border_size;
}
-void NEDepthwiseConvolutionLayerKernel::configure(const ITensor *input, const ITensor *weights, const ITensor *biases, ITensor *output,
- const PadStrideInfo &conv_info, unsigned int depth_multiplier, const Size2D &dilation)
+void NEDepthwiseConvolutionLayerNativeKernel::configure(const ITensor *input, const ITensor *weights, const ITensor *biases, ITensor *output,
+ const PadStrideInfo &conv_info, unsigned int depth_multiplier, const Size2D &dilation)
{
ARM_COMPUTE_ERROR_ON_NULLPTR(input, weights, output);
ARM_COMPUTE_ERROR_THROW_ON(validate_arguments(input->info(), weights->info(), (biases != nullptr) ? biases->info() : nullptr, output->info(), conv_info, depth_multiplier, dilation));
@@ -280,7 +280,7 @@ void NEDepthwiseConvolutionLayerKernel::configure(const ITensor *input, const IT
switch(_input->info()->data_type())
{
case DataType::F32:
- _func = (biases != nullptr) ? &NEDepthwiseConvolutionLayerKernel::run_depthwise<float, 2, true> : &NEDepthwiseConvolutionLayerKernel::run_depthwise<float, 2, false>;
+ _func = (biases != nullptr) ? &NEDepthwiseConvolutionLayerNativeKernel::run_depthwise<float, 2, true> : &NEDepthwiseConvolutionLayerNativeKernel::run_depthwise<float, 2, false>;
break;
default:
ARM_COMPUTE_ERROR("Data type not supported");
@@ -292,9 +292,9 @@ void NEDepthwiseConvolutionLayerKernel::configure(const ITensor *input, const IT
INEKernel::configure(win_config.second);
}
-Status NEDepthwiseConvolutionLayerKernel::validate(const ITensorInfo *input, const ITensorInfo *weights, const ITensorInfo *biases, const ITensorInfo *output, const PadStrideInfo &conv_info,
- unsigned int depth_multiplier,
- const Size2D &dilation)
+Status NEDepthwiseConvolutionLayerNativeKernel::validate(const ITensorInfo *input, const ITensorInfo *weights, const ITensorInfo *biases, const ITensorInfo *output, const PadStrideInfo &conv_info,
+ unsigned int depth_multiplier,
+ const Size2D &dilation)
{
ARM_COMPUTE_RETURN_ON_ERROR(validate_arguments(input, weights, biases, output, conv_info, depth_multiplier, dilation));
ARM_COMPUTE_RETURN_ON_ERROR(validate_and_configure_window(input->clone().get(), weights->clone().get(), (biases != nullptr) ? biases->clone().get() : nullptr, output->clone().get(), conv_info,
@@ -303,7 +303,7 @@ Status NEDepthwiseConvolutionLayerKernel::validate(const ITensorInfo *input, con
return Status{};
}
-void NEDepthwiseConvolutionLayerKernel::run(const Window &window, const ThreadInfo &info)
+void NEDepthwiseConvolutionLayerNativeKernel::run(const Window &window, const ThreadInfo &info)
{
ARM_COMPUTE_UNUSED(info);
ARM_COMPUTE_ERROR_ON_UNCONFIGURED_KERNEL(this);
@@ -313,7 +313,7 @@ void NEDepthwiseConvolutionLayerKernel::run(const Window &window, const ThreadIn
}
template <typename T, int S, bool has_biases>
-void NEDepthwiseConvolutionLayerKernel::run_depthwise(const Window &window)
+void NEDepthwiseConvolutionLayerNativeKernel::run_depthwise(const Window &window)
{
ARM_COMPUTE_ERROR_ON_UNCONFIGURED_KERNEL(this);
ARM_COMPUTE_ERROR_ON_INVALID_SUBWINDOW(INEKernel::window(), window);
diff --git a/src/runtime/NEON/functions/NEDepthwiseConvolutionLayer.cpp b/src/runtime/NEON/functions/NEDepthwiseConvolutionLayer.cpp
index c2ed901169..cdd278b2f1 100644
--- a/src/runtime/NEON/functions/NEDepthwiseConvolutionLayer.cpp
+++ b/src/runtime/NEON/functions/NEDepthwiseConvolutionLayer.cpp
@@ -947,7 +947,7 @@ Status NEDepthwiseConvolutionLayer::validate(const ITensorInfo *input, const ITe
}
else
{
- ARM_COMPUTE_RETURN_ON_ERROR(NEDepthwiseConvolutionLayerKernel::validate(input, weights, biases, output, conv_info, depth_multiplier, dilation));
+ ARM_COMPUTE_RETURN_ON_ERROR(NEDepthwiseConvolutionLayerNativeKernel::validate(input, weights, biases, output, conv_info, depth_multiplier, dilation));
}
// Validate Activation Layer
diff --git a/tests/validation/NEON/DepthwiseConvolutionLayerKernel.cpp b/tests/validation/NEON/DepthwiseConvolutionNativeLayer.cpp
index 3af835855b..a44c2dc3c9 100644
--- a/tests/validation/NEON/DepthwiseConvolutionLayerKernel.cpp
+++ b/tests/validation/NEON/DepthwiseConvolutionNativeLayer.cpp
@@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
-#include "arm_compute/core/NEON/kernels/NEDepthwiseConvolutionLayerKernel.h"
+#include "arm_compute/core/NEON/kernels/NEDepthwiseConvolutionLayerNativeKernel.h"
#include "tests/NEON/Accessor.h"
#include "tests/NEON/Helper.h"
#include "tests/framework/Macros.h"
@@ -38,11 +38,11 @@ namespace validation
using namespace arm_compute::misc::shape_calculator;
// Create function for NEDepthwiseConvolutionLayerKernel
-using NEDepthwiseConvolutionLayer = NESynthetizeFunctionWithZeroConstantKernelBorder<NEDepthwiseConvolutionLayerKernel>;
+using NEDepthwiseConvolutionLayerNative = NESynthetizeFunctionWithZeroConstantKernelBorder<NEDepthwiseConvolutionLayerNativeKernel>;
// Fixture for NEDepthwiseConvolutionLayerKernel
template <typename T>
-using NEDepthwiseConvolutionLayerKernelFixture = DepthwiseConvolutionLayerKernelValidationFixture<Tensor, Accessor, NEDepthwiseConvolutionLayer, T>;
+using NEDepthwiseConvolutionLayerNativeFixture = DepthwiseConvolutionLayerNativeValidationFixture<Tensor, Accessor, NEDepthwiseConvolutionLayerNative, T>;
namespace
{
@@ -52,24 +52,36 @@ RelativeTolerance<float> rel_tolerance_f32(0.001f);
constexpr float abs_tolerance_f32(0.0001f);
/** Width values to test - Precommit */
-const auto width_values = framework::dataset::make("width", { 17U, 47U } );
+const auto width_values_precommit = framework::dataset::make("width", { 17U } );
+
+/** Width values to test - Nightly */
+const auto width_values_nightly = framework::dataset::make("width", { 53U, 47U } );
/** Height values to test - Precommit */
-const auto height_values = framework::dataset::make("height", { 19U, 43U } );
+const auto height_values_precommit = framework::dataset::make("height", { 19U } );
+
+/** Height values to test - Nightly */
+const auto height_values_nightly = framework::dataset::make("height", { 39U, 43U } );
/** Channel values to test - Precommit */
-const auto channel_values = framework::dataset::make("channels", { 32U, 128U });
+const auto channel_values_precommit = framework::dataset::make("channels", { 15U });
+
+/** Channel values to test - Nightly */
+const auto channel_values_nightly = framework::dataset::make("channels", { 33U, 19U });
/** Batch values to test - Precommit */
-const auto batch_values = framework::dataset::make("batch", { 1U, 3U });
+const auto batch_values_precommit = framework::dataset::make("batch", { 1U, 2U });
+
+/** Batch values to test - Nightly */
+const auto batch_values_nightly = framework::dataset::make("batch", { 1U, 3U });
-/** Kernel size values to test - Precommit */
+/** Kernel size values to test - All */
const auto kernel_sz_values = framework::dataset::make("kernel_size", { Size2D(3U, 5U), Size2D(5U, 3U) });
-/** Depth multiplier values to test - Precommit */
+/** Depth multiplier values to test - All */
const auto depth_multiplier_values = framework::dataset::make("depth_multiplier", { 1U, 3U });
-/** Dilation values to test - Precommit */
+/** Dilation values to test - All */
const auto dilation_values = framework::dataset::make("dilation", { Size2D(1U, 1U), Size2D(3U, 3U) });
/** Stride values to test - All */
@@ -129,19 +141,19 @@ void validate_configuration(size_t width_value, size_t height_value, size_t chan
ARM_COMPUTE_EXPECT(dst.info()->is_resizable(), framework::LogLevel::ERRORS);
// Create and configure function
- NEDepthwiseConvolutionLayer dwc;
+ NEDepthwiseConvolutionLayerNative dwc;
dwc.configure(&src, &weights, &biases, &dst, conv_info, depth_multiplier_value, dilation_value);
}
} // namespace
TEST_SUITE(NEON)
-TEST_SUITE(DepthwiseConvolutionLayer)
+TEST_SUITE(DepthwiseConvolutionLayerNative)
TEST_SUITE(Float)
TEST_SUITE(FP32)
-DATA_TEST_CASE(Configuration, framework::DatasetMode::ALL, combine(combine(combine(combine(combine(combine(combine(combine(combine(combine(width_values,
- height_values),
- channel_values),
- batch_values),
+DATA_TEST_CASE(Configuration, framework::DatasetMode::ALL, combine(combine(combine(combine(combine(combine(combine(combine(combine(combine(width_values_precommit,
+ height_values_precommit),
+ channel_values_precommit),
+ batch_values_precommit),
kernel_sz_values),
depth_multiplier_values),
dilation_values),
@@ -154,11 +166,28 @@ width_value, height_value, channel_value, batch_value, kernel_sz_value, depth_mu
validate_configuration(width_value, height_value, channel_value, batch_value, kernel_sz_value, depth_multiplier_value, dilation_value, stride_value, padding_valid_value, data_type_value, data_layout_value);
}
-FIXTURE_DATA_TEST_CASE(RunSmall, NEDepthwiseConvolutionLayerKernelFixture<float>, framework::DatasetMode::ALL,
- combine(combine(combine(combine(combine(combine(combine(combine(combine(combine(width_values,
- height_values),
- channel_values),
- batch_values),
+FIXTURE_DATA_TEST_CASE(RunSmall, NEDepthwiseConvolutionLayerNativeFixture<float>, framework::DatasetMode::ALL,
+ combine(combine(combine(combine(combine(combine(combine(combine(combine(combine(width_values_precommit,
+ height_values_precommit),
+ channel_values_precommit),
+ batch_values_precommit),
+ kernel_sz_values),
+ depth_multiplier_values),
+ dilation_values),
+ stride_values),
+ padding_valid_values),
+ data_type_values),
+ data_layout_values))
+{
+ // Validate output
+ validate(Accessor(_target), _reference, rel_tolerance_f32, 0.f, abs_tolerance_f32);
+}
+
+FIXTURE_DATA_TEST_CASE(RunLarge, NEDepthwiseConvolutionLayerNativeFixture<float>, framework::DatasetMode::NIGHTLY,
+ combine(combine(combine(combine(combine(combine(combine(combine(combine(combine(width_values_nightly,
+ height_values_nightly),
+ channel_values_nightly),
+ batch_values_nightly),
kernel_sz_values),
depth_multiplier_values),
dilation_values),
@@ -173,7 +202,7 @@ FIXTURE_DATA_TEST_CASE(RunSmall, NEDepthwiseConvolutionLayerKernelFixture<float>
TEST_SUITE_END() // FP32
TEST_SUITE_END() // Float
-TEST_SUITE_END() // DepthwiseConvolutionLayer
+TEST_SUITE_END() // DepthwiseConvolutionLayerNative
TEST_SUITE_END() // NEON
} // namespace validation
} // namespace test
diff --git a/tests/validation/fixtures/DepthwiseConvolutionLayerFixture.h b/tests/validation/fixtures/DepthwiseConvolutionLayerFixture.h
index 30b8df9da5..a3ac49eef1 100644
--- a/tests/validation/fixtures/DepthwiseConvolutionLayerFixture.h
+++ b/tests/validation/fixtures/DepthwiseConvolutionLayerFixture.h
@@ -193,7 +193,7 @@ public:
};
template <typename TensorType, typename AccessorType, typename FunctionType, typename T>
-class DepthwiseConvolutionLayerKernelValidationFixture : public DepthwiseConvolutionLayerValidationGenericFixture<TensorType, AccessorType, FunctionType, T>
+class DepthwiseConvolutionLayerNativeValidationFixture : public DepthwiseConvolutionLayerValidationGenericFixture<TensorType, AccessorType, FunctionType, T>
{
public:
template <typename...>