text:
#include "../../precomp.hpp"

#include <opencv2/core/utils/configuration.private.hpp>

#include <string>
#include <vector>
#include <fstream>
#include <sys/stat.h>
#include "../include/common.hpp"
#include "../include/ocl4dnn.hpp"
#include "opencl_kernels_dnn.hpp"
#include "../include/math_functions.hpp"
#include "../include/default_kernel_config.hpp"
#include "opencv2/dnn/shape_utils.hpp"
#include "opencv2/core/utils/logger.hpp"

#if defined WIN32 || defined _WIN32
#include <windows.h>
#include <direct.h>
#undef min
#undef max
#endif

namespace cv { namespace dnn { namespace ocl4dnn {
static cv::Mutex kernelConfigMutex;
typedef std::map<std::string, std::string> kernel_hash_t;
static kernel_hash_t kernelConfigMap;
static bool defaultConfigLoaded = false;

static bool enableWorkaroundIDLF()
{
    static bool param = utils::getConfigurationParameterSizeT("OPENCV_OCL4DNN_WORKAROUND_IDLF", true);
    return param;
}

static bool dumpFailedResult()
{
    static bool param = utils::getConfigurationParameterSizeT("OPENCV_OCL4DNN_DUMP_FAILED_RESULT", false);
    return param;
}

static size_t testAllKernels()
{
    static size_t param = utils::getConfigurationParameterSizeT("OPENCV_OCL4DNN_TEST_ALL_KERNELS", 0);
    return param;
}

static bool raiseOnCheckError()
{
    static bool param = utils::getConfigurationParameterBool("OPENCV_OCL4DNN_TUNING_RAISE_CHECK_ERROR", false);
    return param;
}

static std::string sanitize(const std::string& s)
{
    std::string s_ = s;
    for (size_t i = 0; i < s_.size(); i++)
    {
        char c = s_[i];
        if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'))
        {
            s_[i] = '_';
        }
    }
    // TODO add hash?
    // s_ = s_ + cv::format("_%08llx", crc64((uchar*)s.c_str(), s.size()));
    return s_;
}

static void initializeGlobalBuiltinConfigurations(const std::string& cache_path)
{
    CV_Assert(defaultConfigLoaded == false);
    CV_Assert(kernelConfigMap.empty());

    /* fp32 config */
    size_t numConfigs = sizeof(default_kernel_config_intel_fp32) /
                        sizeof(default_kernel_config_intel_fp32[0]) / 2;
    for (size_t i = 0; i < numConfigs; i++)
    {
        std::string key = std::string("Intel(R) Corporation_") + default_kernel_config_intel_fp32[2 * i];
        if (!cache_path.empty())
        {
            std::string cacheFile = cache_path + sanitize(key);
            std::ifstream cachedKernel(cacheFile.c_str());
            if (cachedKernel)
                continue;  // external configuration found, skip builtin
        }
        std::pair<std::string, std::string> entry(
                key,
                default_kernel_config_intel_fp32[2 * i + 1]);
        kernelConfigMap.insert(entry);
    }

    /* fp16 config */
    numConfigs = sizeof(default_kernel_config_intel_fp16) /
                 sizeof(default_kernel_config_intel_fp16[0]) / 2;
    for (size_t i = 0; i < numConfigs; i++)
    {
        std::string key = std::string("Intel(R) Corporation_") + default_kernel_config_intel_fp16[2 * i];
        if (!cache_path.empty())
        {
            std::string cacheFile = cache_path + sanitize(key);
            std::ifstream cachedKernel(cacheFile.c_str());
            if (cachedKernel)
                continue;  // external configuration found, skip builtin
        }
        std::pair<std::string, std::string> entry(
                key,
                default_kernel_config_intel_fp16[2 * i + 1]);
        kernelConfigMap.insert(entry);
    }

    defaultConfigLoaded = true;
}

template<typename Dtype>
OCL4DNNConvSpatial<Dtype>::OCL4DNNConvSpatial(OCL4DNNConvConfig config)
{
    bias_term_ = config.bias_term;
    int dims = config.in_shape.size();
    int spatial_dims = 2;

    channels_   = config.in_shape[dims - spatial_dims - 1];
    num_output_ = config.out_shape[dims - spatial_dims - 1];
    group_ = config.group;
    CV_CheckGT(group_, 0, "");  // avoid div by zero below

    fused_activ_ = OCL4DNN_CONV_FUSED_ACTIV_NONE;
    fused_eltwise_ = false;
    power_ = 1.f;
    negative_slope_ = 0;
    min_value_ = 0;
    max_value_ = 0;
    prev_kernel_type_ = -1;
    tuned_ = false;
    use_half_ = config.use_half;

    // assumption: spatial dimension is 2.
    kernel_h_ = config.kernel.height;
    kernel_w_ = config.kernel.width;
    // pads: [pad_top, pad_bottom, pad_left, pad_right]
    pad_h_ = config.pads[0]; // pad_top
    pad_bottom_ = config.pads[1];
    pad_w_ = config.pads[2]; // pad_left
    pad_right_ = config.pads[3];
    stride_h_ = config.stride.height;
    stride_w_ = config.stride.width;
    dilation_h_ = config.dilation.height;
    dilation_w_ = config.dilation.width;
    M_ = num_output_ / group_;
    height_ = config.in_shape[dims - spatial_dims + 0];
    width_ = config.in_shape[dims - spatial_dims + 1];
    output_h_ = config.out_shape[dims - spatial_dims + 0];
    output_w_ = config.out_shape[dims - spatial_dims + 1];
    bottom_dim_ = channels_ * width_ * height_;
    top_dim_ = num_output_ * output_w_ * output_h_;

    cache_path_ = utils::getConfigurationParameterString("OPENCV_OCL4DNN_CONFIG_PATH", "");
    dwconv_ = (num_output_ == channels_ && channels_ == group_);

    use_cache_path_ = false;
    if (!cache_path_.empty())
    {
#if defined _WIN32
        struct _stat file_stat;
        use_cache_path_ = _stat(cache_path_.c_str(), &file_stat) == 0 &&
                      ((_S_IFDIR & file_stat.st_mode) != 0);
#else
        struct stat file_stat;
        use_cache_path_ = stat(cache_path_.c_str(), &file_stat) == 0 &&
                      S_ISDIR(file_stat.st_mode);
#endif
        if (!use_cache_path_)
        {
            CV_LOG_ONCE_ERROR(NULL, "OpenCV(ocl4dnn): Kernel configuration cache directory doesn't exist: " << cache_path_);
        }
    }

    run_auto_tuning_ = use_cache_path_ && !utils::getConfigurationParameterBool("OPENCV_OCL4DNN_DISABLE_AUTO_TUNING", false);
    force_auto_tuning_ = utils::getConfigurationParameterBool("OPENCV_OCL4DNN_FORCE_AUTO_TUNING", false);
}

template<typename Dtype>
OCL4DNNConvSpatial<Dtype>::~OCL4DNNConvSpatial()
{
    if (!swizzled_weights_umat.empty()) {
        swizzled_weights_umat.release();
    }
}

template<typename Dtype>
void OCL4DNNConvSpatial<Dtype>::setFusionDefine(ocl4dnnFusedActiv_t fused_activ, bool fused_eltwise)
{
    if (fused_eltwise)
        addDef("FUSED_CONV_ELTWISE", 1);

    switch (fused_activ) {
        case OCL4DNN_CONV_FUSED_ACTIV_RELU:
            addDef("FUSED_CONV_RELU", 1);
            break;
        case OCL4DNN_CONV_FUSED_ACTIV_PRELU:
            addDef("FUSED_CONV_PRELU", 1);
            break;
        case OCL4DNN_CONV_FUSED_ACTIV_POWER:
            addDef("FUSED_CONV_POWER", 1);
            break;
        case OCL4DNN_CONV_FUSED_ACTIV_TANH:
            addDef("FUSED_CONV_TANH", 1);
            break;
        case OCL4DNN_CONV_FUSED_ACTIV_RELU6:
            addDef("FUSED_CONV_RELU6", 1);
            break;
        default:
            ;
    }
    return;
}

template<typename Dtype>
void OCL4DNNConvSpatial<Dtype>::setFusionArg(ocl4dnnFusedActiv_t fused_activ, bool fused_eltwise, int fused_eltwise_offset, ocl::Kernel &kernel, cl_uint &argIdx)
{
    if (fused_eltwise)
    {
        kernel.set(argIdx++, ocl::KernelArg::PtrReadOnly(bottom_data2_));
        if (fused_eltwise_offset >= 0)
            kernel.set(argIdx++, fused_eltwise_offset);
    }

    switch (fused_activ) {
        case OCL4DNN_CONV_FUSED_ACTIV_RELU:
            kernel.set(argIdx++, (float)negative_slope_);
            break;
        case OCL4DNN_CONV_FUSED_ACTIV_PRELU:
            kernel.set(argIdx++, ocl::KernelArg::PtrReadOnly(negative_slope_umat_));
            break;
        case OCL4DNN_CONV_FUSED_ACTIV_POWER:
            kernel.set(argIdx++, (float)power_);
            break;
        case OCL4DNN_CONV_FUSED_ACTIV_RELU6:
            kernel.set(argIdx++, (float)min_value_);
            kernel.set(argIdx++, (float)max_value_);
            break;
        default:
            ;
    }
    return;
}

typedef enum {
    TYPE_FLOAT = 1,
    TYPE_HALF = 2
} ocl4dnnConvSpatialType_t;

template<typename Dtype>
void OCL4DNNConvSpatial<Dtype>::collectCommonInformation()
{
    if (use_half_)
    {
        addDef("TYPE", TYPE_HALF);
        addDef("Dtype", "half");
        addDef("Dtype2", "half2");
        addDef("Dtype4", "half4");
        addDef("Dtype8", "half8");
        addDef("Dtype16", "half16");
        addDef("as_Dtype", "as_half");
        addDef("as_Dtype2", "as_half2");
        addDef("as_Dtype4", "as_half4");
        addDef("as_Dtype8", "as_half8");
    }
    else
    {
        addDef("TYPE", TYPE_FLOAT);
        addDef("Dtype", "float");
        addDef("Dtype2", "float2");
        addDef("Dtype4", "float4");
        addDef("Dtype8", "float8");
        addDef("Dtype16", "float16");
        addDef("as_Dtype", "as_float");
        addDef("as_Dtype2", "as_float2");
        addDef("as_Dtype4", "as_float4");
        addDef("as_Dtype8", "as_float8");
    }
}

typedef enum {
    KERNEL_TYPE_INTEL_IDLF = 2,
    KERNEL_TYPE_BASIC = 4,
    KERNEL_TYPE_GEMM_LIKE = 5,
    KERNEL_TYPE_DWCONV = 6
} ocl4dnnConvSpatialKernelType_t;

template<typename Dtype>
void OCL4DNNConvSpatial<Dtype>::setupKernelDetails(int32_t kernelType,
                                                   int32_t blockM,
                                                   int32_t blockK,
                                                   int32_t blockN)
{
    std::string kernelUKey;
    int32_t simd_size;

    if (kernelType == KERNEL_TYPE_INTEL_IDLF) {
        simd_size = blockN;
        kernelUKey = generateSpecificKey(KERNEL_TYPE_INTEL_IDLF, blockM, blockK, 1);

        // kernel name
        kernel_name_ = "IDLF_";
        kernel_name_ += kernelUKey;
        if (simd_size == 16)
            kernel_name_ += "_SIMD16";
        else
            kernel_name_ += "_SIMD8";

        // options
        options_ << " -cl-fast-relaxed-math -D KERNEL_IDLF -D convolve_simd=" << kernel_name_;
        options_ << " -cl-mad-enable";
        if (clOptionSupport("-cl-no-subgroup-ifp"))
            options_ << " -cl-no-subgroup-ifp ";

        // defs
        int32_t output_block_width = blockM;
        int32_t output_block_height = blockK;
        int tile_x = (output_block_width - 1) * stride_w_ + kernel_w_ * dilation_w_;
        int tile_y = (output_block_height - 1) * stride_h_ + kernel_h_ * dilation_h_;
        int invec_size = tile_y;

        addDef("SIMD_SIZE", simd_size);
        addDef("OUT_BLOCK_WIDTH", output_block_width);
        addDef("OUT_BLOCK_HEIGHT", output_block_height);
        addDef("INPUT_DEPTH", channels_ / group_);
        addDef("TOTAL_INPUT_DEPTH_SIZE", channels_);
        addDef("TOTAL_OUTPUT_DEPTH", num_output_);
        addDef("NUM_FILTERS", M_);
        addDef("TILE_X", tile_x);
        addDef("TILE_Y", tile_y);
        addDef("INVEC_SIZE", invec_size);
        addDef("ALIGNED_NUM_FILTERS", (int)alignSize(M_, simd_size));
        addDef("OUT_BLOCK_SIZE", (output_block_width*output_block_height));
        addDef("APPLY_BIAS", bias_term_);
        addDef("WEIGHT_PREF", ((kernel_w_ * kernel_h_) == 1) ? 1 : 8);
        addDef("INPUT_PITCH", (width_ * height_));
        addDef("OUTPUT_PITCH", (output_w_ * output_h_));
        addDef("LEFT_FILTERS", ((int)alignSize(M_, simd_size) - M_));
        addDef("INPUT_WIDTH", width_);
        addDef("INPUT_HEIGHT", height_);
        addDef("FILTERS_IN_GROUP", ((int)alignSize(M_, simd_size) / simd_size));

        setFusionDefine(fused_activ_, fused_eltwise_);

        src_ = cv::ocl::dnn::conv_layer_spatial_oclsrc;
    }
    else if (kernelType == KERNEL_TYPE_BASIC)
    {
        addDef("KERNEL_BASIC");

        kernelUKey = generateSpecificKey(KERNEL_TYPE_BASIC, blockM, blockK, blockN);
        kernel_name_ = "BASIC_";
        kernel_name_ += kernelUKey;

        // opts
        options_ << " -cl-fast-relaxed-math -D ConvolveBasic=" << kernel_name_;
        if (clOptionSupport("-cl-no-subgroup-ifp"))
            options_ << " -cl-no-subgroup-ifp ";

        // defs
        addDef("CHANNELS", channels_ / group_);
        addDef("APPLY_BIAS", bias_term_);
        addDef("OUTPUT_Z", M_);
        setFusionDefine(fused_activ_, fused_eltwise_);

        src_ = cv::ocl::dnn::conv_layer_spatial_oclsrc;
    }
    else if (kernelType == KERNEL_TYPE_GEMM_LIKE)
    {
        simd_size = blockK;
        kernelUKey = generateSpecificKey(KERNEL_TYPE_GEMM_LIKE, blockM, blockK, blockN);

        kernel_name_ = "U_GEMM_LIKE_CONV_";
        kernel_name_ += kernelUKey.c_str();
        kernel_name_ += (blockK == 8) ? "_SIMD8" : "_SIMD16";
        std::stringstream kernelDef;
        kernelDef << "GEMM_LIKE_CONV_" << blockN << "_" << blockM;
        if (blockK == 16)
            kernelDef << "_SIMD16";

        // Build list of options and defines
        options_ << " -cl-fast-relaxed-math " << " -D " << kernelDef.str()
            << " -D Conv_Interleaved=" << kernel_name_.c_str();
        options_ << " -cl-mad-enable";
        if (clOptionSupport("-cl-no-subgroup-ifp"))
            options_ << " -cl-no-subgroup-ifp ";

        addDef("KERNEL_GEMM_LIKE");
        addDef("INPUT_DEPTH", channels_);
        addDef("WIDTH1", M_);
        addDef("OUT_PADDING_LEFT", 0);
        addDef("OUT_PADDING_HEIGHT", 0);
        addDef("OUT_DEPTH", M_);
        addDef("NUM_BATCHES", num_);
        addDef("DY", blockM);
        addDef("DX", blockN);
        addDef("KERNEL_WIDTH_DIV2", kernel_w_ / 2);
        addDef("KERNEL_SLICE_DIV2", (kernel_w_ * kernel_h_) / 2);
        addDef("TILE_N_LAST", M_ % 32);
        addDef("TILE_N_LAST_DIV8", (M_ % 32) / 8);
        addDef("APPLY_BIAS", bias_term_);
        setFusionDefine(fused_activ_, fused_eltwise_);
        src_ = ocl::dnn::conv_layer_spatial_oclsrc;
    }
    else if (kernelType == KERNEL_TYPE_DWCONV)
    {
        kernelUKey = generateSpecificKey(KERNEL_TYPE_DWCONV, blockM, blockK, blockN);
        kernel_name_ = "DWCONV_";
        kernel_name_ += kernelUKey.c_str();

        options_ << " -cl-fast-relaxed-math ";
        if (clOptionSupport("-cl-no-subgroup-ifp"))
            options_ << " -cl-no-subgroup-ifp ";

        addDef("KERNEL_DWCONV");
        addDef("KERNEL_SIZE", kernel_w_ * kernel_h_);
        addDef("KERNEL_W", kernel_w_);
        addDef("KERNEL_H", kernel_h_);
        addDef("APPLY_BIAS", bias_term_);
        addDef("OUTPUT_Z", num_output_ * num_);
        addDef("CHANNELS", num_output_);
        setFusionDefine(fused_activ_, fused_eltwise_);

        options_ << " -D DWCONV=" << kernel_name_;
        src_ = cv::ocl::dnn::conv_layer_spatial_oclsrc;
    }
}

template<typename Dtype>
void OCL4DNNConvSpatial<Dtype>::setupKernel()
{
    collectCommonInformation();

    addDef("KERNEL_WIDTH", kernel_w_);
    addDef("KERNEL_HEIGHT" , kernel_h_);
    addDef("STRIDE_X", stride_w_);
    addDef("STRIDE_Y", stride_h_);
    addDef("DILATION_X", dilation_w_);
    addDef("DILATION_Y", dilation_h_);
    if (kernelType_ != KERNEL_TYPE_BASIC)
    {
        addDef("INPUT_PAD_W", pad_w_);
        addDef("INPUT_PAD_H", pad_h_);
        addDef("INPUT_PAD_RIGHT", pad_right_);
        addDef("INPUT_PAD_BOTTOM", pad_bottom_);
    }

    setupKernelDetails(kernelType_, blockM_, blockK_, blockN_);
}

template<typename Dtype>
void OCL4DNNConvSpatial<Dtype>::setBias(bool bias_term)
{
    bias_term_ = bias_term;
}

template<typename Dtype>
void OCL4DNNConvSpatial<Dtype>::setActivReLU(bool fuse_activ, float slope)
{
    if ( fuse_activ )
    {
        fused_activ_ = OCL4DNN_CONV_FUSED_ACTIV_RELU;
        negative_slope_ = slope;
    }
    else
        fused_activ_ = OCL4DNN_CONV_FUSED_ACTIV_NONE;
}

template<typename Dtype>
void OCL4DNNConvSpatial<Dtype>::setActivReLU6(bool fuse_activ, float min, float max)
{
    if ( fuse_activ )
    {
        fused_activ_ = OCL4DNN_CONV_FUSED_ACTIV_RELU6;
        min_value_ = min;
        max_value_ = max;
    }
    else
        fused_activ_ = OCL4DNN_CONV_FUSED_ACTIV_NONE;
}

template<typename Dtype>
void OCL4DNNConvSpatial<Dtype>::setActivPReLU(bool fuse_activ, std::vector<float> &slope)
{
    if ( fuse_activ )
    {
        fused_activ_ = OCL4DNN_CONV_FUSED_ACTIV_PRELU;
        Mat tmpMat = Mat(num_output_, 1, CV_32FC1, (uchar*)&slope[0]);
        tmpMat.copyTo(negative_slope_umat_);
    }
    else
        fused_activ_ = OCL4DNN_CONV_FUSED_ACTIV_NONE;
}

template<typename Dtype>
void OCL4DNNConvSpatial<Dtype>::setActivPower(bool fuse_activ, float power)
{
    if ( fuse_activ )
    {
        fused_activ_ = OCL4DNN_CONV_FUSED_ACTIV_POWER;
        power_ = power;
    }
    else
        fused_activ_ = OCL4DNN_CONV_FUSED_ACTIV_NONE;
}

template<typename Dtype>
void OCL4DNNConvSpatial<Dtype>::setActivTanh(bool fuse_activ)
{
    if ( fuse_activ )
    {
        fused_activ_ = OCL4DNN_CONV_FUSED_ACTIV_TANH;
    }
    else
        fused_activ_ = OCL4DNN_CONV_FUSED_ACTIV_NONE;
}

template<typename Dtype>
bool OCL4DNNConvSpatial<Dtype>::Forward(const UMat& bottom,
                                        const UMat& bottom2,
                                        const UMat& weight,
                                        const UMat& bias,
                                        UMat& top,
                                        int32_t numImages)
{
    num_ = numImages;
    if (!bottom2.empty())
    {
        fused_eltwise_ = true;
        bottom_data2_ = bottom2;
    }
    else
    {
        fused_eltwise_ = false;
    }

    if (use_half_ && !bias.empty())
        CV_CheckTypeEQ(bias.type(), CV_16SC1, "");

    if (use_half_)
        CV_CheckTypeEQ(weight.type(), CV_16SC1, "");

    prepareKernel(bottom, top, weight, bias, numImages);
    if (bestKernelConfig.empty())
        return false;
    return convolve(bottom, top, weight, bias, numImages, bestKernelConfig);
}

template<typename Dtype>
void OCL4DNNConvSpatial<Dtype>::calculateBenchmark(const UMat &bottom, UMat &verifyTop,
                                                   const UMat &weight, const UMat &bias,
                                                   int32_t numImages)
{
    options_.str(""); options_.clear(); // clear contents and state flags
    createBasicKernel(1, 1, 1);
    CV_Assert(!kernelQueue.empty());  // basic kernel must be available
    kernel_index_ = kernelQueue.size() - 1;
    convolve(bottom, verifyTop, weight, bias, numImages, kernelQueue[kernel_index_]);
    CV_Assert(phash.find(kernelQueue[kernel_index_]->kernelName) != phash.end());
    //unloadProgram(kernelQueue[kernel_index_]->kernelName);
    kernelQueue.pop_back();
    return;
}

// For large enough input size, we do not need to tune kernels for different
// size. The reason is with large input size, there will be enough work items
// to feed al the EUs.
// FIXME for the gemm like convolution, switch back to exact image size.

#define TUNING_SIZE(x) ((x) > 256 ? 256 : (alignSize(x, 16)))

template<typename Dtype>
void OCL4DNNConvSpatial<Dtype>::generateKey()
{
    std::string precision = (use_half_) ? "FP16" : "FP32";
    std::stringstream keyBuilder;
    // FIXME: to support fuse?
    keyBuilder << "k" << kernel_w_ << "x" << kernel_h_ << "_"
               << "cn" << channels_ << "_"
               << "g" << group_ << "_"
               << "s" << stride_w_ << "x" << stride_h_ << "_"
               << "d" << dilation_w_ << "x" << dilation_h_ << "_"
               << "b" << bias_term_ << "_"
               << "in" << TUNING_SIZE(width_) << "x" << TUNING_SIZE(height_) << "_"
               << "p" << pad_w_ << "x" << pad_h_ << "_"
               << "num" << num_ << "_"
               << "M" << M_ << "_"
               << "activ" << (int)fused_activ_ << "_"
               << "eltwise" << fused_eltwise_ << "_"
               << precision;

    key_ = ocl::Device::getDefault().vendorName() + "_EU" + cv::format("%d", ocl::Device::getDefault().maxComputeUnits()) + "_" + keyBuilder.str();
    key_sanitized_ = sanitize(key_);
    short_key_ = keyBuilder.str();
}

template<typename Dtype>
std::string OCL4DNNConvSpatial<Dtype>::generateSpecificKey(int32_t type, int32_t blockWidth,
                                                           int32_t blockHeight, int32_t blockDepth)
{
    std::stringstream keyBuilder;
    keyBuilder << short_key_
               << "_" << type
               << "_" << blockWidth
               << "_" << blockHeight
               << "_" << blockDepth;

    return keyBuilder.str();
}

template<typename Dtype>
void interleaveMatrix(Dtype* mem_dst, const Dtype *mem,
                      int r, int c, int interleavedRows, int nonInterleavedRows,
                      int blockWidth, int rowAlignment )
{
    CV_Check(interleavedRows, interleavedRows % 2 == 0, "interleaveMatrix only supports even values for interleavedRows.");

    size_t memSize = r * c * sizeof(float);
    size_t dstSize = memSize *
                     (interleavedRows + nonInterleavedRows * 2) /
                     (interleavedRows + nonInterleavedRows);
    memset(mem_dst, 0, dstSize);    // NOLINT

    const int xStride = blockWidth;
    const int yStride = c * 2;
    const Dtype *pSrc = mem;
    Dtype* pDst = mem_dst;
    for (int y = 0; y < r;)
    {
        for (int rows = 0; rows < interleavedRows; rows += 2)
        {
            if (y >= r)
                break;
            if ((c % xStride) == 0) {
                for (int x = 0; x < c / xStride; x++) {
                    memcpy(pDst + x * xStride * 2,                         // NOLINT
                           pSrc + x * xStride,     xStride * sizeof(Dtype));
                    memcpy(pDst + x * xStride * 2 + xStride,               // NOLINT
                           pSrc + x * xStride + c, xStride * sizeof(Dtype));
                }
            } else {
                const int count = c / xStride;
                int x = 0;
                for (; x < count - 1; x++) {
                    memcpy(pDst + x * xStride * 2,                          // NOLINT
                           pSrc + x * xStride, xStride * sizeof(Dtype));
                    memcpy(pDst + x * xStride * 2 + xStride,                // NOLINT
                           pSrc + x * xStride + c, xStride * sizeof(Dtype));
                }
                memcpy(pDst + x * xStride * 2,                            // NOLINT
                       pSrc + x * xStride, xStride * sizeof(Dtype));
            }
            pSrc += yStride;
            pDst += yStride;
            y += 2;
        }

        for (int rows = 0; rows < nonInterleavedRows; rows++)
        {
            if (y >= r)
                break;
            const int stride = rowAlignment;
            int remaining = c;
            for (int x = 0; x < c; x += stride)
            {
                if (remaining >= stride) {
                    memcpy(pDst + x * 2, pSrc + x, stride * sizeof(Dtype));    // NOLINT
                    remaining -=stride;
                } else {
                    memcpy(pDst + x * 2, pSrc + x, remaining * sizeof(Dtype));  // NOLINT
                }
            }
            pSrc += yStride / 2;
            pDst += yStride;
            y++;
        }
    }
}

template<typename Dtype>
bool OCL4DNNConvSpatial<Dtype>::swizzleWeight(const UMat &weight,
                                              int32_t swizzled_factor,
                                              bool interleave)
{
    // Simply skip the weight swizzle if we already got a swizzled_weights_
    // in test phase and not in auto tuning
    // This requires we always call convolve again with the winner configuration
    // during the auto tuning stage.
    if (tuned_ && !swizzled_weights_umat.empty())
        return true;

    if (swizzled_weights_umat.empty())
        swizzled_weights_umat.create(1, (int)alignSize(num_output_, 16) * channels_ *
                                     kernel_h_ * (int)alignSize(kernel_w_, 2),
                                     (use_half_) ? CV_16SC1 : CV_32FC1);

    if (!interleave) {
        int32_t channels = channels_ / group_;

        ocl::Kernel oclk_copy_weight(
            use_half_ ? "copyWeightsSwizzled_half" : "copyWeightsSwizzled_float",
            cv::ocl::dnn::conv_spatial_helper_oclsrc,
            use_half_ ? "-DHALF_SUPPORT=1 -DDtype=half" : "-DDtype=float"
        );
        if (oclk_copy_weight.empty())
            return false;

        oclk_copy_weight.args(
            ocl::KernelArg::PtrReadOnly(weight),
            ocl::KernelArg::PtrWriteOnly(swizzled_weights_umat),
            kernel_w_,
            kernel_h_,
            channels,
            num_output_,
            swizzled_factor
        );

        size_t global_work_size_copy[1] = { (size_t)(alignSize(num_output_, swizzled_factor) * channels * kernel_w_ * kernel_h_) };

        if (!oclk_copy_weight.run_(1, global_work_size_copy, NULL, false))
        {
            CV_LOG_ERROR(NULL, "DNN/OpenCL: Swizzle kernel run failed");
            return false;
        }
    } else {
        // assumption: kernel dimension is 2
        Mat weightMat;
        Mat swizzledWeightMat;
        UMat weight_tmp; // FP32 in half mode, TODO implement FP16 repack
        if (use_half_)
        {
            CV_CheckTypeEQ(weight.type(), CV_16SC1, "");
            convertFp16(weight, weight_tmp);
            weightMat = weight_tmp.getMat(ACCESS_READ);
            swizzledWeightMat.create(shape(swizzled_weights_umat), CV_32F);
        }
        else
        {
            weightMat = weight.getMat(ACCESS_READ);
            swizzledWeightMat = swizzled_weights_umat.getMat(ACCESS_WRITE);
        }

        CV_CheckTypeEQ(weightMat.type(), CV_32FC1, "");
        Dtype* cpu_weight = (Dtype *)weightMat.ptr<float>();
        Dtype* cpu_swizzled_weight = (Dtype *)swizzledWeightMat.ptr<float>();

        int interleavedRows = (kernel_w_ / 2) * 2;
        int nonInterleavedRows = kernel_w_ % 2;
        int blockWidth = swizzled_factor;  // should equal to simd size.
        int rowAlignment = 32;
        size_t interleaved_filter_size = M_ * kernel_w_ * kernel_h_ * channels_ * sizeof(Dtype);
        cv::AutoBuffer<Dtype, 0> tmpSwizzledWeight(interleaved_filter_size);
        for (int od = 0; od < M_; od++)
            for (int id = 0; id < channels_; id++)
                for (int r = 0; r < kernel_h_; r++)
                    for (int c = 0; c < kernel_w_; c++)
                        tmpSwizzledWeight[((id * kernel_h_ + r)* kernel_w_ + c) * M_ + od] =
                            cpu_weight[((od * channels_ + id) * kernel_h_ + r)*kernel_w_+c];

        interleaveMatrix(cpu_swizzled_weight,
                         tmpSwizzledWeight.data(),
                         kernel_w_ * kernel_h_ * channels_, M_,
                         interleavedRows,
                         nonInterleavedRows,
                         blockWidth,
                         rowAlignment);

        // unmap OpenCL buffers
        weightMat.release();

        if (use_half_)
            convertFp16(swizzledWeightMat, swizzled_weights_umat);
    }

    return true;
}

template<>
bool OCL4DNNConvSpatial<float>::createBasicKernel(int32_t blockWidth,
                                                  int32_t blockHeight, int32_t blockDepth)
{
    kernelType_ = KERNEL_TYPE_BASIC;
    blockM_ = blockWidth;
    blockK_ = blockHeight;
    blockN_ = blockDepth;
    setupKernel();

    ocl::Program program = compileKernel();
    if (program.ptr())
    {
        int32_t workItemOutput[3] = { 1, 1, 1 };
        size_t globalSize[3] = { (size_t)output_w_, (size_t)output_h_, (size_t)M_ };
        kernelQueue.push_back(makePtr<kernelConfig>(kernel_name_, &globalSize[0], (const size_t*)NULL, &workItemOutput[0],
                                                    false, KERNEL_TYPE_BASIC));
        return true;
    }
    else
        return false;
}

template<>
bool OCL4DNNConvSpatial<float>::convolve(const UMat &bottom, UMat &top,
                                         const UMat &weight, const UMat &bias,
                                         int32_t numImages, kernelConfig* config)
{
    ocl::Program program;
    phash_t::iterator it = phash.find(config->kernelName);
    if (it != phash.end())
        program = it->second;
    else
        return false;

    int32_t bias_offset;
    int32_t element_size = use_half_ ? sizeof(short) : sizeof(float);

    if (config->kernelType == KERNEL_TYPE_INTEL_IDLF) {
        if (!swizzleWeight(weight, config->workItem_output[2], false))
            return false;
#if 0
        size_t total_bottom_size = bottom_dim_ * numImages;
        size_t total_kernel_size = kernel_h_ * kernel_w_ * channels_ * M_;
        size_t total_bias_size = M_ * group_;
        size_t total_top_size = top_dim_ * numImages;
#endif
        for (int32_t g = 0; g < group_; ++g) {
            bias_offset = M_ * g;
            int32_t image_offset = width_ * height_ * (channels_ / group_) * g;
            int32_t output_image_offset = output_w_ * output_h_ * M_ * g;
            int32_t kernel_offset = kernel_h_ * kernel_w_ * (channels_ / group_) * M_ * g;

            ocl::Kernel kernel(config->kernelName.c_str(), program);
            if (kernel.empty())
                return false;

            cl_uint argIdx = 0;
            setFusionArg(fused_activ_, fused_eltwise_, output_image_offset, kernel, argIdx);

            kernel.set(argIdx++, ocl::KernelArg::PtrReadOnly(bottom));
            kernel.set(argIdx++, image_offset);

            kernel.set(argIdx++, ocl::KernelArg::PtrReadOnly(swizzled_weights_umat));
            kernel.set(argIdx++, kernel_offset);

            if (bias_term_)
            {
                kernel.set(argIdx++, ocl::KernelArg::PtrReadOnly(bias));
                kernel.set(argIdx++, bias_offset);
            }

            kernel.set(argIdx++, ocl::KernelArg::PtrWriteOnly(top));
            kernel.set(argIdx++, (int)(top.offset / element_size) + output_image_offset);

            kernel.set(argIdx++, (uint16_t)width_);
            kernel.set(argIdx++, (uint16_t)height_);
            kernel.set(argIdx++, (uint16_t)output_w_);
            kernel.set(argIdx++, (uint16_t)output_h_);
            if (!kernel.run_(3, config->global_work_size, config->local_work_size, false))
            {
                CV_LOG_ERROR(NULL, "DNN/OpenCL: IDLF kernel run failed");
                return false;
            }
        }
    } else if (config->kernelType == KERNEL_TYPE_GEMM_LIKE) {
        if (!swizzleWeight(weight, config->workItem_output[1], true))
            return false;
#if 0
        size_t total_bottom_size = bottom_dim_ * numImages;
        size_t total_kernel_size = kernel_h_ * kernel_w_ * channels_ * M_;
        size_t total_bias_size = M_ * group_;
#endif
        size_t total_top_size = top_dim_ * numImages;
        for (int32_t g = 0; g < group_; ++g) {
            bias_offset = M_ * g;
            int32_t image_offset = width_ * height_ * (channels_ / group_) * g;
            int32_t output_image_offset = output_w_ * output_h_ * M_ * g;
            int32_t kernel_offset = kernel_h_ * kernel_w_ * (channels_ / group_) * M_ * g;

            ocl::Kernel kernel(config->kernelName.c_str(), program);
            if (kernel.empty())
                return false;

            cl_uint argIdx = 0;
            setFusionArg(fused_activ_, fused_eltwise_, output_image_offset, kernel, argIdx);

            kernel.set(argIdx++, ocl::KernelArg::PtrReadOnly(bottom));
            kernel.set(argIdx++, (int)image_offset);
            kernel.set(argIdx++, (int)(bottom.total() - image_offset));

            kernel.set(argIdx++, ocl::KernelArg::PtrReadOnly(swizzled_weights_umat));
            kernel.set(argIdx++, (int)kernel_offset);
            kernel.set(argIdx++, (int)(swizzled_weights_umat.total() - kernel_offset));

            if (bias_term_)
            {
                kernel.set(argIdx++, ocl::KernelArg::PtrReadOnly(bias));
                kernel.set(argIdx++, (int)bias_offset);
            }

            kernel.set(argIdx++, ocl::KernelArg::PtrWriteOnly(top));
            kernel.set(argIdx++, (int)(top.offset / element_size) + output_image_offset);
            kernel.set(argIdx++, (int)total_top_size - (int)(top.offset / element_size));

            kernel.set(argIdx++, (uint16_t)width_);
            kernel.set(argIdx++, (uint16_t)height_);
            kernel.set(argIdx++, (uint16_t)output_w_);
            kernel.set(argIdx++, (uint16_t)output_h_);

            int out_pitch_y = output_w_ * output_h_;
            int out_pitch_z = out_pitch_y * M_;
            int aligned_input_size = height_ * width_ * channels_ / group_;
            int slice_pitch = width_ * height_;
            kernel.set(argIdx++, (uint32_t)out_pitch_y);
            kernel.set(argIdx++, (uint32_t)out_pitch_z);
            kernel.set(argIdx++, (uint32_t)aligned_input_size);
            kernel.set(argIdx++, (uint32_t)slice_pitch);

            int blockM = config->workItem_output[0];
            int blockK = config->workItem_output[1];
            int blockN = config->workItem_output[2];
            int alignedFilterWidth = alignSize(M_, blockN);
            int alignedExpandHeight = alignSize(output_w_ * output_h_, blockM);
            int globalWorkSizeDX = blockN;
            int globalWorkSizeDY = blockM;
            size_t sgemm_m = alignedExpandHeight;
            size_t sgemm_n = alignedFilterWidth;
            size_t gx = divUp(sgemm_n, globalWorkSizeDX);
            size_t gy = divUp(sgemm_m, globalWorkSizeDY);
            gy = alignSize(gy, blockK);
            size_t global_size[3] = { gx, gy, config->global_work_size[2] };

            if (!kernel.run_(3, global_size, config->local_work_size, false))
            {
                CV_LOG_ERROR(NULL, "DNN/OpenCL: GEMM like kernel run failed");
                return false;
            }
        }
    } else if (config->kernelType == KERNEL_TYPE_DWCONV) {
        ocl::Kernel kernel(config->kernelName.c_str(), program);
        if (kernel.empty())
            return false;

        cl_uint argIdx = 0;
        setFusionArg(fused_activ_, fused_eltwise_, -1, kernel, argIdx);
        kernel.set(argIdx++, ocl::KernelArg::PtrReadOnly(bottom));
        kernel.set(argIdx++, ocl::KernelArg::PtrReadOnly(weight));
        if (bias_term_)
            kernel.set(argIdx++, ocl::KernelArg::PtrReadOnly(bias));
        kernel.set(argIdx++, ocl::KernelArg::PtrWriteOnly(top));
        kernel.set(argIdx++, (int)(top.offset / element_size));
        kernel.set(argIdx++, (uint16_t)width_);
        kernel.set(argIdx++, (uint16_t)height_);
        kernel.set(argIdx++, (uint16_t)output_w_);
        kernel.set(argIdx++, (uint16_t)output_h_);

        size_t wgs = kernel.workGroupSize();
        if (!wgs)
        {
            CV_LOG_ERROR(NULL, "DNN/OpenCL: Can't query workGroupSize of DWCONV kernel");
            return false;
        }
        size_t lws[1] = { wgs };
        size_t gws[1] = { roundUp((size_t)output_w_ * output_h_ * num_output_ * num_, (unsigned)lws[0]) };
        if (!kernel.run_(1, gws, lws, false))
        {
            CV_LOG_ERROR(NULL, "DNN/OpenCL: DWCONV kernel run failed");
            return false;
        }
    } else {
        for (int32_t n = 0; n < numImages; ++n) {
            for (int32_t g = 0; g < group_; ++g) {
                bias_offset = M_ * g;
                int32_t image_offset = n * bottom_dim_
                    + width_ * height_ * (channels_ / group_) * g;
                int32_t output_image_offset = n * top_dim_
                    + output_w_ * output_h_ * M_ * g;

                int32_t kernel_offset = kernel_h_ * kernel_w_ *
                                       (channels_ / group_) * M_
                                       * g;

                ocl::Kernel kernel(config->kernelName.c_str(), program);
                if (kernel.empty())
                    return false;

                cl_uint argIdx = 0;
                setFusionArg(fused_activ_, fused_eltwise_, -1, kernel, argIdx);
                kernel.set(argIdx++, ocl::KernelArg::PtrReadOnly(bottom));
                kernel.set(argIdx++, image_offset);
                kernel.set(argIdx++, ocl::KernelArg::PtrReadOnly(weight));
                kernel.set(argIdx++, kernel_offset);
                if (bias_term_)
                    kernel.set(argIdx++, ocl::KernelArg::PtrReadOnly(bias));
                else
                    kernel.set(argIdx++, (void *)NULL);
                kernel.set(argIdx++, bias_offset);
                kernel.set(argIdx++, ocl::KernelArg::PtrWriteOnly(top));
                kernel.set(argIdx++, (int)(top.offset / element_size));
                kernel.set(argIdx++, output_image_offset);
                kernel.set(argIdx++, (uint16_t)width_);
                kernel.set(argIdx++, (uint16_t)height_);
                kernel.set(argIdx++, (uint16_t)output_w_);
                kernel.set(argIdx++, (uint16_t)output_h_);
                kernel.set(argIdx++, (uint16_t)pad_w_);
                kernel.set(argIdx++, (uint16_t)pad_h_);

                size_t wgs = kernel.workGroupSize();
                if (!wgs)
                {
                    CV_LOG_ERROR(NULL, "DNN/OpenCL: Can't query workGroupSize of Basic kernel");
                    return false;
                }
                size_t lws[1] = { wgs };
                size_t gws[1] = { roundUp((size_t)output_w_ * output_h_ * M_, (unsigned)lws[0]) };
                if (!kernel.run_(1, gws, lws, false))
                {
                    CV_LOG_ERROR(NULL, "DNN/OpenCL: Basic kernel run failed");
                    return false;
                }
            }
        }
    }

    return true;
}

template<>
float OCL4DNNConvSpatial<float>::timedConvolve(const UMat &bottom, UMat &top,
                                               const UMat &weight, const UMat &bias,
                                               int32_t numImages, kernelConfig* config)
{
    cv::ocl::Queue queue;
    try
    {
        queue = cv::ocl::Queue::getDefault();
    }
    catch (const std::exception& e)
    {
        CV_LOG_ONCE_ERROR(NULL, "OpenCV(ocl4dnn): Can't get OpenCL default queue for auto-tuning: " << e.what());
        return 1e6;
    }

    // warm up.
    bool saved_tuned = tuned_;
    tuned_ = false;
    convolve(bottom, top, weight, bias, numImages, config);

    cv::ocl::Timer timer(queue);
    timer.start();
    bool res = true;;
    CV_LOG_INFO(NULL, "Benchmarking kernel: " << config->kernelName);
    tuned_ = true;
    int loop_cnt = 4;
    for (int i = 0; i < loop_cnt; i++) {
        res = convolve(bottom, top, weight, bias, numImages, config);
        if (!res)
            break;
    }
    tuned_ = saved_tuned;
    timer.stop();
    if (!res) {
        config->tested = true;
        config->verified = false;
        return 1e5;
    }

    float elapsedTime = timer.durationNS() * 1e-6 / loop_cnt;
    double out_w = output_w_;
    double out_h = output_h_;
    double out_z = M_;
    double k_w = kernel_w_;
    double k_h = kernel_h_;
    double k_z = channels_;
    double totalFlops = ((k_w*k_h*k_z -1)*2)*(out_w*out_h*out_z)*num_;
    CV_LOG_INFO(NULL, "\tEstimated Gflops:" << (totalFlops * 1e-9));
    CV_LOG_INFO(NULL, "\tEstimated GFLOPS/S: " << ((totalFlops * 1e-9)*(1000.0/elapsedTime)));
    return elapsedTime;
}

template<>
bool OCL4DNNConvSpatial<float>::verifyResult(const UMat &bottom,
                                             UMat &top,
                                             const UMat &weight,
                                             const UMat &bias,
                                             int32_t numImages,
                                             kernelConfig* config,
                                             UMat &verifyTop)
{
    if (config->verified)
        return true;
    else if (config->tested)
        return false;

    //int32_t sz[4] = {numImages, num_output_, output_h_, output_w_};
    CV_CheckEQ(top.total(), (size_t)numImages * num_output_ * output_h_ * output_w_, "");
    CV_CheckTypeEQ(top.type(), (use_half_) ? CV_16SC1 : CV_32FC1, "");
    top.setTo(Scalar::all(0));

    bool saved_tuned = tuned_;
    tuned_ = false;
    convolve(bottom, top, weight, bias, numImages, config);
    tuned_ = saved_tuned;

    config->tested = true;

    UMat new_top, new_verify_top;
    Mat mat_top, mat_verify_top;
    if (use_half_)
    {
        convertFp16(top, new_top);
        convertFp16(verifyTop, new_verify_top);

        mat_top = new_top.getMat(ACCESS_READ);
        mat_verify_top = new_verify_top.getMat(ACCESS_READ);
    }
    else
    {
        mat_top = top.getMat(ACCESS_READ);
        mat_verify_top = verifyTop.getMat(ACCESS_READ);
    }
    const float* data = mat_top.ptr<float>();
    const float* verify_data = mat_verify_top.ptr<float>();

    int error_slice_offset = 0;
    int error_slice = 0;
    float relative_eps = use_half_ ? 0.1f : 0.01f;

    size_t errors = 0;

    double rel_err = norm(mat_top.reshape(1, 1), mat_verify_top.reshape(1, 1), NORM_L1 | NORM_RELATIVE);
    if (rel_err >= relative_eps)
    {
        for (int32_t n = 0; n < num_; ++n) {
            for (int32_t g = 0; g < group_; ++g) {
                int32_t output_image_offset = n * top_dim_ + output_w_ * output_h_ * M_ * g;
                for (int out_ch = 0; out_ch < M_; out_ch++)
                    for (int h = 0; h < output_h_; h++)
                        for (int w = 0; w < output_w_; w++) {
                            size_t offset = output_image_offset + out_ch * output_w_ * output_h_ + h * output_w_ + w;

                            bool has_error = !(data[offset] == data[offset]);  // is NaN
                            if (!has_error)
                            {
                                float error_factor = std::fabs(data[offset] - verify_data[offset]);
                                float base_value_abs = std::max(1e-3f, std::fabs(verify_data[offset]));
                                has_error = error_factor > relative_eps * base_value_abs;
                            }
                            if (has_error)
                            {
                                if (errors == 0)
                                {
                                    error_slice = (int)(offset / (output_w_ * output_h_));
                                    error_slice_offset = (int)(offset % (output_w_ * output_h_));
                                    CV_LOG_ERROR(NULL, "Kernel: " << config->kernelName);
                                }
                                if (errors < 10)
                                    CV_LOG_ERROR(NULL, "test verification failed @ image " << n << " group " << g
                                            << " out_ch " << out_ch << " h " << h << " w " << w
                                            << " (offset: " << offset << ")"
                                            << " got " << data[offset] << " expected " << verify_data[offset]);
                                errors++;
                            }
                        }
            }
        }
    }

    if (errors)
    {
        if (dumpFailedResult())
        {
            try
            {
                int n_outputs = (int)(mat_top.size[0]*mat_top.size[1]);
                int slice_size = (int)(mat_top.total() / n_outputs);
                Rect roi(0, 0, slice_size, n_outputs);
                roi.width = std::min(roi.width, 32);
                roi.height = std::min(roi.height, 16);
                roi.x = std::max(0, std::min(slice_size - roi.width, error_slice_offset - roi.width/2));
                roi.y = std::max(0, std::min(n_outputs - roi.height, error_slice - roi.height/2));
                std::cout << "roi = " << roi << " errors=" << errors << std::endl;
                std::cout << "mat_top = " << shape(mat_top) << std::endl
                          << mat_top.reshape(1, 1).reshape(1, n_outputs)(roi) << std::endl;
                std::cout << "verify_top = " << shape(mat_verify_top) << std::endl
                          << mat_verify_top.reshape(1, 1).reshape(1, n_outputs)(roi) << std::endl;
            }
            catch (const std::exception& e)
            {
                CV_LOG_ERROR(NULL, "Results dump failed: " << e.what());
            }
            catch (...)
            {
                CV_LOG_ERROR(NULL, "Results dump failed")
            }
        }

        if (raiseOnCheckError())
            CV_Error_(Error::StsError, ("ocl4dnn tuning verification failed: %s (errors %lld)", config->kernelName.c_str(), (long long int)errors));
        return false;
    }
    else
    {
        config->verified = true;
        return true;
    }
}

template<typename Dtype>
void OCL4DNNConvSpatial<Dtype>::unloadProgram(const std::string& kernelName)
{
    ocl::Program program;
    phash_t::iterator it = phash.find(kernelName);
    if (it != phash.end())
    {
        program = it->second;
        it->second = ocl::Program();
    }
    else
        return;

    ocl::Context ctx = ocl::Context::getDefault();
    ctx.unloadProg(program);
}

template<typename Dtype>
ocl::Program OCL4DNNConvSpatial<Dtype>::compileKernel()
{
    phash_t::iterator it = phash.find(kernel_name_);
    if (it != phash.end())
    {
        return it->second;
    }

    String errmsg;
    ocl::Context ctx = ocl::Context::getDefault();
    std::string options = options_.str();
    CV_Assert(options.size() != 0);
    ocl::Program program = ctx.getProg(src_, options, errmsg);

    phash.insert(std::pair<std::string, ocl::Program>(kernel_name_, program));
    if (!program.ptr())
    {
        CV_LOG_WARNING(NULL, "DNN/OpenCL: Failed to compile kernel: " << kernel_name_
            << ", buildflags: '" << options << "', errmsg: '" << errmsg << "'"
        );
    }
    return program;
}

template<>
bool OCL4DNNConvSpatial<float>::createGEMMLikeConvKernel(int32_t blockM,
                                                         int32_t blockK,
                                                         int32_t blockN)
{
    int32_t simd_size = blockK;

    int workItemOutput[3] = { blockM, blockK, blockN };
    size_t gx = (size_t)divUp(M_, blockN);
    size_t gy = (size_t)divUp(output_w_ * output_h_, blockM);
    gy = alignSize(gy, simd_size);
    size_t gz = num_;
    size_t global_size[3] = { gx, gy, gz };
    size_t local_size[3] = { 1, static_cast<size_t>(simd_size), 1 };

    kernelType_ = KERNEL_TYPE_GEMM_LIKE;
    blockM_ = blockM;
    blockK_ = blockK;
    blockN_ = blockN;
    setupKernel();

    ocl::Program program = compileKernel();
    if (program.ptr())
    {
        ocl::Kernel kernel(kernel_name_.c_str(), program);
        if (kernel.empty())
            return false;

        kernelQueue.push_back(makePtr<kernelConfig>(kernel_name_, &global_size[0], &local_size[0], &workItemOutput[0],
                                                    true, KERNEL_TYPE_GEMM_LIKE));
        return true;
    }
    else
        return false;
}

template<>
bool OCL4DNNConvSpatial<float>::createIDLFKernel(int32_t blockWidth,
                                                 int32_t blockHeight,
                                                 int32_t simd_size)
{
    int32_t workItemOutput[3] = { blockWidth, blockHeight, simd_size };
    const int32_t num_output_maps = M_;
    int32_t output_width = output_w_;
    int32_t output_height = output_h_;
    int32_t output_block_width = blockWidth;
    int32_t output_block_height = blockHeight;
    int32_t num_batches = num_;

    size_t global_size[3] = {
        (size_t)divUp(output_width, output_block_width),
        (size_t)divUp(output_height, output_block_height),
        (size_t)num_batches * alignSize(num_output_maps, simd_size) };
    size_t local_size[3] = { 1, 1, static_cast<size_t>(simd_size) };

    kernelType_ = KERNEL_TYPE_INTEL_IDLF;
    blockM_ = blockWidth;
    blockK_ = blockHeight;
    blockN_ = simd_size;

    setupKernel();

    if (enableWorkaroundIDLF() && ocl::Device::getDefault().intelSubgroupsSupport())
    {
        // Issues are observed with these kernels: 3x1 (covered by tests), 2x1, 4x1, 5x1, 3x2
        // kernels 1x3, 3x3, 2x3 are good
        if (pad_h_ != 0 && kernel_w_ <= simd_size && kernel_h_ <= 2)
        {
            CV_LOG_INFO(NULL, "DNN(workaround): skip IDLF kernel: " << kernel_name_);
            return false;
        }
    }

    ocl::Program program = compileKernel();
    if (program.ptr())
    {
        ocl::Kernel kernel(kernel_name_.c_str(), program);
        if (kernel.empty())
            return false;

        kernelQueue.push_back(makePtr<kernelConfig>(kernel_name_, &global_size[0], &local_size[0], &workItemOutput[0],
                                                    true, KERNEL_TYPE_INTEL_IDLF));
        return true;
    }
    else
        return false;
}

template<>
bool OCL4DNNConvSpatial<float>::createDWConvKernel(int32_t blockWidth,
                                                   int32_t blockHeight,
                                                   int32_t blockDepth)
{
    if (!dwconv_)
        return false;

    int workItemOutput[3] = { 1, 1, 1 };
    size_t local_size[3] = { 1, 1, 1 };
    size_t global_size[3];
    global_size[0] = divUp(output_w_, workItemOutput[0]);
    global_size[1] = divUp(output_h_, workItemOutput[1]);
    global_size[2] = divUp(M_ * num_, workItemOutput[2]);

    kernelType_ = KERNEL_TYPE_DWCONV;
    blockM_ = blockWidth;
    blockK_ = blockHeight;
    blockN_ = blockDepth;

    setupKernel();

    ocl::Program program = compileKernel();
    if (program.ptr())
    {
        kernelQueue.push_back(makePtr<kernelConfig>(kernel_name_, &global_size[0], &local_size[0],
                              &workItemOutput[0], false, KERNEL_TYPE_DWCONV));
        return true;
    }
    else
        return false;
}

template<>
bool OCL4DNNConvSpatial<float>::createConvolutionKernel(int32_t kernelType,
                                                        int32_t blockWidth,
                                                        int32_t blockHeight,
                                                        int32_t blockDepth)
{
    kernelType_ = kernelType;
    options_.str(""); options_.clear(); // clear contents and state flags
    src_ = ocl::ProgramSource();

    if (kernelType == KERNEL_TYPE_INTEL_IDLF)
        return createIDLFKernel(blockWidth, blockHeight, blockDepth);
    else if (kernelType == KERNEL_TYPE_BASIC)
        return createBasicKernel(blockWidth, blockHeight, blockDepth);
    else if (kernelType == KERNEL_TYPE_GEMM_LIKE)
        return createGEMMLikeConvKernel(blockWidth, blockHeight, blockDepth);
    else if (kernelType == KERNEL_TYPE_DWCONV)
        return createDWConvKernel(blockWidth, blockHeight, blockDepth);
    else
        CV_Assert(0 && "Internal error");
    return false;
}

template<>
void OCL4DNNConvSpatial<float>::generate_gemmlike_tuneritems(std::vector< cv::Ptr<tunerParam> > &tunerItems,
                                                             int blockM, int blockK, int blockN)
{
    if (group_ != 1 || ((M_ % 8 != 0) || (M_ % 32 == 24)))
        return;

    if (blockM != 1 && blockM != 2)
        return;

    if (blockN != 32)
        return;

    if (blockK != 8 && blockK != 16)
        return;

    if (blockK == 16)
    {
        if ((blockM == 1 && (kernel_w_ > 4)) || M_ % 32 != 0)
            return;
        if ((blockM == 2) || M_ % 32 != 0)
            return;
    }

    tunerItems.push_back(makePtr<tunerParam>(KERNEL_TYPE_GEMM_LIKE, blockM, blockK, blockN));
}

template<>
void OCL4DNNConvSpatial<float>::generate_idlf_tuneritems(std::vector< cv::Ptr<tunerParam> > &tunerItems,
                                                         int blockM, int blockK, int simd_size)
{
    int max_compute_units = ocl::Device::getDefault().maxComputeUnits();

    if (simd_size != 8 && simd_size != 16)
        return;

    if (simd_size == 8 && !((group_ == 1 || M_ % 8 == 0)))
        return;

    if (simd_size == 16 && !(group_ == 1 || M_ % 16 == 0))
        return;

    int width_max, height_max, block_size_max;
    width_max = 14;
    height_max = 14;
    block_size_max = 32;

    if (blockM > width_max)
        return;
    if (blockK > height_max)
        return;

    if (blockM > output_w_)
        return;
    if (blockK > output_h_)
        return;

    // Only when the work items count is less than the device
    // max work items or the M_ is less than 16, we will tune
    // for simd 8.
    if (simd_size == 8 &&  M_ >= 16 &&
        ((num_ * M_ * output_w_ * output_h_ / static_cast<float>(blockM * blockK)) >=
        max_compute_units * 7 * 16))
        return;

    int actual_tile_x = kernel_w_ * dilation_w_ + (blockM - 1) * stride_w_ ;
    int tile_x = alignSize(actual_tile_x, simd_size);
    if (tile_x > simd_size)
        return;

    if (blockM * blockK > block_size_max)
        return;

    tunerItems.push_back(makePtr<tunerParam>(KERNEL_TYPE_INTEL_IDLF, blockM, blockK, simd_size));
}

template<>
void OCL4DNNConvSpatial<float>::generate_dwconv_tuneritems(std::vector< cv::Ptr<tunerParam> > &tunerItems,
                                                           int blockM, int blockK, int blockN)
{
    if (!dwconv_)
        return;

    tunerItems.push_back(makePtr<tunerParam>(KERNEL_TYPE_DWCONV, blockM, blockK, blockN));
}

template<>
void OCL4DNNConvSpatial<float>::generateTunerItems(std::vector< cv::Ptr<tunerParam> > &tunerItems)
{
    if (ocl::Device::getDefault().intelSubgroupsSupport())
    {
        // depthwise kernel
        generate_dwconv_tuneritems(tunerItems, 1, 1, 1);
        if (tunerItems.size() > 0 && group_ > 8)
            return;

        // gemm like kernel
        generate_gemmlike_tuneritems(tunerItems, 1, 8, 32);
        generate_gemmlike_tuneritems(tunerItems, 2, 8, 32);
        generate_gemmlike_tuneritems(tunerItems, 1, 16, 32);
        generate_gemmlike_tuneritems(tunerItems, 2, 16, 32);

        // idlf kernel
        for (int simd_size = 8; simd_size <= 16; simd_size += 8)
        {
            int width_max, height_max;
            width_max = 14;
            height_max = 14;
            for (uint32_t width = width_max; width > 0; width--)
            {
                for (uint32_t height = height_max; height > 0; height--)
                {
                    generate_idlf_tuneritems(tunerItems, width, height, simd_size);
                }
            }
        }
    }
}

template<>
void OCL4DNNConvSpatial<float>::useFirstAvailable(const UMat &bottom,
                                                  UMat &top,
                                                  const UMat &weight,
                                                  const UMat &bias,
                                                  int32_t numImages,
                                                  UMat &verifyTop)
{
    std::vector< cv::Ptr<tunerParam> > tunerItems;
    generateTunerItems(tunerItems);
    tunerItems.push_back(makePtr<tunerParam>(KERNEL_TYPE_BASIC, 1, 1, 1));

    for (int i = 0; i < tunerItems.size(); i++)
    {
        if (createConvolutionKernel(tunerItems[i]->kernelType,
                                    tunerItems[i]->blockWidth,
                                    tunerItems[i]->blockHeight,
                                    tunerItems[i]->blockDepth))
        {
            CV_Assert(!kernelQueue.empty());  // basic kernel must be available
            int kernelIdx = kernelQueue.size() - 1;
            kernelConfig* config = kernelQueue[kernelIdx].get();
            bool failed = false;
            const size_t testCount = testAllKernels();
            for(int t = 0; t < testCount; t++)
            {
                try
                {
                    config->tested = false;
                    config->verified = false;
                    if (!verifyResult(bottom, top, weight, bias, numImages, config, verifyTop))
                    {
                        CV_LOG_ERROR(NULL, "Failed on test iteration: " << t);
                        failed = true;
                        break;
                    }
                }
                catch (...)
                {
                    CV_LOG_ERROR(NULL, "Failed on test iteration: " << t);
                    throw;
                }
            }
            if (!failed && verifyResult(bottom, top, weight, bias, numImages, config, verifyTop))
            {
                bestKernelConfig = kernelQueue[kernelIdx];
                if (bestKernelConfig->kernelType != KERNEL_TYPE_INTEL_IDLF &&
                    bestKernelConfig->kernelType != KERNEL_TYPE_GEMM_LIKE)
                    if (!swizzled_weights_umat.empty())
                        swizzled_weights_umat.release();

                for (int32_t j = 0; j < kernelIdx; j++) {
                    CV_Assert(phash.find(kernelQueue[j]->kernelName) != phash.end());
                    unloadProgram(kernelQueue[j]->kernelName);
                }
                kernelQueue.clear();
                tuned_ = true;
                break;
            }
        }
    }
}

template<>
void OCL4DNNConvSpatial<float>::cacheTunedConfig()
{
    if (tuned_)
    {
        cv::AutoLock lock(kernelConfigMutex);
        std::stringstream outputKernel;
        outputKernel << bestKernelConfig->workItem_output[0] << " "
                     << bestKernelConfig->workItem_output[1] << " "
                     << bestKernelConfig->workItem_output[2] << " "
                     << bestKernelConfig->kernelType << " "
                     << bestKernelConfig->local_work_size[0] << " "
                     << bestKernelConfig->local_work_size[1] << " "
                     << bestKernelConfig->local_work_size[2] << " "
                     << bestKernelConfig->swizzle_weights << " "
                     << bestKernelConfig->use_null_local << " ";
        kernelConfigMap.insert(std::pair<std::string, std::string>(key_, outputKernel.str()));
    }
}

template<>
void OCL4DNNConvSpatial<float>::setupConvolution(const UMat &bottom,
                                                 UMat &top,
                                                 const UMat &weight,
                                                 const UMat &bias,
                                                 int32_t numImages,
                                                 UMat &verifyTop)
{
    std::vector< cv::Ptr<tunerParam> > tunerItems;

    generateTunerItems(tunerItems);
    for (int i = 0; i < tunerItems.size(); i++)
        createConvolutionKernel(tunerItems[i]->kernelType,
                                tunerItems[i]->blockWidth,
                                tunerItems[i]->blockHeight,
                                tunerItems[i]->blockDepth);

    const size_t testCount = testAllKernels();
    for (int32_t x = 0; x < kernelQueue.size(); x++)
    {
        kernelConfig* config = kernelQueue[x];
        config->executionTime = timedConvolve(bottom, top, weight, bias, numImages, config);
        for(int t = 0; t < testCount; t++)
        {
            try
            {
                config->tested = false;
                config->verified = false;
                bool verified = verifyResult(bottom, top, weight, bias, numImages, config, verifyTop);
                if (verified == false)
                {
                    CV_LOG_ERROR(NULL, "Kernel " << config->kernelName << " failed verification");
                    CV_LOG_ERROR(NULL, "workItem="
                         << config->workItem_output[0] << ","
                         << config->workItem_output[1] << ","
                         << config->workItem_output[2] << " "
                         << "kernelType: " << config->kernelType << " "
                         << "global_work_size="
                         << config->global_work_size[0] << ","
                         << config->global_work_size[1] << ","
                         << config->global_work_size[2] << " "
                         << "local_work_size="
                         << config->local_work_size[0] << ","
                         << config->local_work_size[1] << ","
                         << config->local_work_size[2] << " "
                         << config->swizzle_weights << " "
                         << config->use_null_local);
                }
                else
                {
                    CV_LOG_VERBOSE(NULL, 0, "Kernel " << config->kernelName << " pass verification");
                }
            }
            catch (...)
            {
                CV_LOG_ERROR(NULL, "Failed on test iteration: " << t);
                throw;
            }
        }
    }

    int32_t failures = 0;
    bool verification = false;
    if (kernelQueue.size()) {
        while (failures < kernelQueue.size()) {
            int32_t fastestKernel = -1;
            float fastestTime = std::numeric_limits<float>::infinity();

            for (int32_t x = 0; x < kernelQueue.size(); x++) {
                if (kernelQueue[x]->executionTime < fastestTime &&
                    kernelQueue[x]->tested == false) {
                    fastestKernel = x;
                    fastestTime = kernelQueue[x]->executionTime;
                }
            }
            if (fastestKernel < 0)
                break;
            // Test fastest kernel
            bool verified = verifyResult(bottom, top, weight, bias, numImages, kernelQueue[fastestKernel], verifyTop);
            if (verified == true) {
                kernel_index_ = fastestKernel;
                verification = true;
                break;
            } else {
                CV_LOG_ERROR(NULL, "Kernel " << kernelQueue[fastestKernel]->kernelName <<
                             " failed verification");
                failures++;
            }
        }
    }
    if (verification) {
        CV_LOG_INFO(NULL, "Kernel <" << kernelQueue[kernel_index_]->kernelName <<
                    "> passed verification");
        CV_LOG_INFO(NULL, "Convolution Time:" << kernelQueue[kernel_index_]->executionTime);
        double out_w = output_w_;
        double out_h = output_h_;
        double out_z = M_;
        double k_w = kernel_w_;
        double k_h = kernel_h_;
        double k_z = channels_;
        float elapsedTime = kernelQueue[kernel_index_]->executionTime;
        double totalFlops = ((k_w*k_h*k_z -1)*2)*(out_w*out_h*out_z)*num_;
        CV_LOG_INFO(NULL, "\tEstimated Gflops:" << (totalFlops * 1e-9));
        CV_LOG_INFO(NULL, "\tEstimated GFLOPS/S: " << ((totalFlops * 1e-9)*(1000.0/elapsedTime)));
    } else {
        CV_LOG_INFO(NULL, "fallback to basic kernel");
        options_.str(""); options_.clear(); // clear contents and state flags
        createBasicKernel(1, 1, 1);
        CV_Assert(!kernelQueue.empty());  // basic kernel must be available
        kernel_index_ = kernelQueue.size() - 1;
    }
    this->bestKernelConfig = kernelQueue[kernel_index_];

    if (bestKernelConfig->kernelType != KERNEL_TYPE_INTEL_IDLF && bestKernelConfig->kernelType != KERNEL_TYPE_GEMM_LIKE)
        if (!swizzled_weights_umat.empty())
            swizzled_weights_umat.release();

    for (int32_t x = 0; x < kernelQueue.size(); x++) {
        if (x != kernel_index_) {
            CV_Assert(phash.find(kernelQueue[x]->kernelName) != phash.end());
            unloadProgram(kernelQueue[x]->kernelName);
        }
    }
    kernelQueue.clear();
    tuned_ = true;
    saveTunedConfig();
}

template<typename Dtype>
void OCL4DNNConvSpatial<Dtype>::saveTunedConfig()
{
    CV_Assert(tuned_);
    if (!use_cache_path_ || cache_path_.empty())
        return;

    std::string outputFile;
    outputFile = cache_path_ + "/" + key_sanitized_;
    std::ofstream outputKernel;
    outputKernel.open(outputFile.c_str());
    outputKernel << bestKernelConfig->workItem_output[0] << " "
                 << bestKernelConfig->workItem_output[1] << " "
                 << bestKernelConfig->workItem_output[2] << " "
                 << bestKernelConfig->kernelType << " "
                 << bestKernelConfig->local_work_size[0] << " "
                 << bestKernelConfig->local_work_size[1] << " "
                 << bestKernelConfig->local_work_size[2] << " "
                 << bestKernelConfig->swizzle_weights << " "
                 << bestKernelConfig->use_null_local << " ";
    outputKernel.close();
}

template<typename Dtype>
void OCL4DNNConvSpatial<Dtype>::prepareKernel(const UMat &bottom, UMat &top,
                                              const UMat &weight, const UMat &bias,
                                              int32_t numImages)
{
    std::string previous_key = key_;

    generateKey();
    if (key_.compare(previous_key) == 0 && bestKernelConfig)
        return;

    if (bestKernelConfig)
    {
        prev_kernel_type_ = bestKernelConfig->kernelType;
        CV_Assert(phash.find(bestKernelConfig->kernelName) != phash.end());
        phash.erase(bestKernelConfig->kernelName);
        bestKernelConfig.release();
    }

    if (loadCachedConfig()) // check in-memory cache
        return;

    if (loadTunedConfig())  // check external storage
        return;

    UMat benchData(1, numImages * top_dim_, (use_half_) ? CV_16SC1 : CV_32FC1);

    calculateBenchmark(bottom, benchData, weight, bias, numImages);

    if (run_auto_tuning_ || force_auto_tuning_)
    {
        setupConvolution(bottom, top, weight, bias, numImages, benchData);
    }
    else
    {
        useFirstAvailable(bottom, top, weight, bias, numImages, benchData);
    }
    cacheTunedConfig();
}

template<typename Dtype>
bool OCL4DNNConvSpatial<Dtype>::loadCachedConfig()
{
    cv::AutoLock lock(kernelConfigMutex);
    if (!defaultConfigLoaded && !force_auto_tuning_)
        initializeGlobalBuiltinConfigurations((use_cache_path_ && !cache_path_.empty()) ? (cache_path_ + '/') : std::string());

    kernel_hash_t::iterator it = kernelConfigMap.find(key_);
    if (it != kernelConfigMap.end())
    {
        int32_t x, y, z, type, lx, ly, lz;
        bool swizzle, nullLocal;
        std::stringstream cachedKernel(it->second);
        if (cachedKernel)
        {
            cachedKernel >> x;
            cachedKernel >> y;
            cachedKernel >> z;
            cachedKernel >> type;
            cachedKernel >> lx;
            cachedKernel >> ly;
            cachedKernel >> lz;
            cachedKernel >> swizzle;
            cachedKernel >> nullLocal;
            if (setupKernelByConfig(x, y, z, type, lx, ly, lz, swizzle, nullLocal)) {
                tuned_ = true;
                return true;
            }
        }
    }
    return false;
}

template<typename Dtype>
bool OCL4DNNConvSpatial<Dtype>::setupKernelByConfig(int x, int y, int z, int type,
                                                    int lx, int ly, int lz,
                                                    bool swizzle, bool nullLocal)
{
    if (type == KERNEL_TYPE_INTEL_IDLF)
    {
        if (z == 1)
            z = 16;
        CV_Check(z, z == 16 || z == 8, "DNN/OpenCL: IDLF - invalid SIMD size");
    }
    kernelQueue.clear();
    createConvolutionKernel(type, x, y, z);
    if (kernelQueue.size() != 1)
    {
        CV_LOG_ERROR(NULL, "DNN/OpenCL: Failed setup kernel by config: "
            << " x = " << x
            << " y = " << y
            << " z = " << z
            << " type = " << type
        );
        return false;
    }
    bestKernelConfig = kernelQueue[0];
    kernelQueue.clear();
    bestKernelConfig->local_work_size[0] = lx;
    bestKernelConfig->local_work_size[1] = ly;
    bestKernelConfig->local_work_size[2] = lz;
    bestKernelConfig->swizzle_weights = swizzle;
    bestKernelConfig->use_null_local = nullLocal;
    // If kernel type changed to type 2 or 4, we need to reset the swizzled
    // weights pointer to invalidate the previous swizzled weights data.
    if (prev_kernel_type_ != bestKernelConfig->kernelType &&
        (bestKernelConfig->kernelType == KERNEL_TYPE_INTEL_IDLF ||
        bestKernelConfig->kernelType == KERNEL_TYPE_GEMM_LIKE))
    {
        if (!swizzled_weights_umat.empty())
            swizzled_weights_umat.release();
    }
    return true;
}

template<typename Dtype>
bool OCL4DNNConvSpatial<Dtype>::loadTunedConfig()
{
    if (force_auto_tuning_)
        return false;  // don't load results from external storage

    if (!use_cache_path_)
    {
        if (cache_path_.empty())
        {
            CV_LOG_ONCE_WARNING(NULL, "OpenCV(ocl4dnn): consider to specify kernel configuration cache directory "
                "through OPENCV_OCL4DNN_CONFIG_PATH parameter."
            );
        }
        return false;
    }

    int32_t x, y, z, type, lx, ly, lz;
    bool swizzle, nullLocal;

    // Find cached kernel configuration from file
    std::string cacheFile = cache_path_ + "/" + key_sanitized_;
    std::ifstream cachedKernel(cacheFile.c_str());
    if (cachedKernel)
    {
        cachedKernel >> x;
        cachedKernel >> y;
        cachedKernel >> z;
        cachedKernel >> type;
        cachedKernel >> lx;
        cachedKernel >> ly;
        cachedKernel >> lz;
        cachedKernel >> swizzle;
        cachedKernel >> nullLocal;
        if (setupKernelByConfig(x, y, z, type, lx, ly, lz, swizzle, nullLocal)) {
            tuned_ = true;
            return true;
        }
    }
    return false;
}

template class OCL4DNNConvSpatial<float>;

}}} // namespace cv::dnn::ocl4dnn


text:
Bjørnholt Skov er en godt 66 hektar stor skov, beliggende kun ca. 1 km øst for Nordskoven mellem Sejs og Hårup, på begge sider af Borgedalsvej, og er en del af Silkeborgskovene.  Bjørnholt Skov blev overtaget af Silkeborg Statsskovdistrikt fra det daværende Fredningsplanudvalg i 1976. Mellem Nordskoven og Bjørnholt ligger de fredede lyngbakker Sindbjerg og Stoubjerg. Bjørnholt Skovligger  i et "Område af særlig landskabelig interesse"  og er levested for rødlistede (truede og næsten truede) arter; Okkergul pletvinge (sommerfugle), smal blomsterbuk (insekter) som er kritisk truet og vokshatte-arter; gulfodet-, tyndbladet-, sortdugget- og stinkende vokshat og er hovedsagelig granskov. De §3-beskyttede naturarealer i Bjørnholt Skov skal naturligvis drives, så de bevares som sådanne. Der findes følgende særlige anlæg for friluftslivet: Pistol-skydebane og træningsbane for gravhunde. I nordenden af skoven ligger et overdrev, og langs vejen er der rester af gammel stævningsskov. I bakkerne er der rester af tidligere råstofgrave, hvor der er gravet grus, men i begyndelsen af 1900-tallet blev der udvundet brunkul.

Kilder

Eksterne henvisninger
 Bente Rytter:  Hårupskoven

Skove i Region Midtjylland
Silkeborg Kommunes geografi

redpajama_set_name: RedPajamaWikipedia


Freshly divorced, Navy aircraft electrician Tessa Christy is ready to embrace her newfound freedom on her six-month deployment to Iceland.
The last thing Tess wants or expects is to meet someone… irresistible.
For the love of a man.
For the love of a woman.
For the love of politics.
the country and the globe.
of ways at the most peculiar of hours.
Join bestselling and award-winning authors as they explore the mysterious, tantalizing, colorful and sometimes zany phenomena that is love.
From the kind of love worth fighting for, to love kissed with Cupid's arrow, this collection offers something for everyone.
Just Love, a Crazy Ink LGBTQ romance anthology, celebrates love in all its hues and shades.
Join my VIP Reader's Lounge!


Benefit Roller Lash - $24.00: This product I will be repurchasing. The formula is dark and buildable so I can apply two coats for the day and an extra coat for a nighttime look without my lashes getting clumpy. The wand is curved which gives my lashes a voluminous curl at the lash line and upwards length. And it doesn't smudge during the day.
BareMinerals Complexion Rescue - $29.50: This product is similar to a BB cream or light tinted moisturizer in that the formula is really smooth and light. In fact, it so light it feels like naked skin. It's not as buildable as I would like so it doesn't offer me the exact coverage I'm looking for. It gives me a dewy shine which I like but if I don't set it, I toe the line of dewy and oily by the end of the work day. I love this product for light weekends.
BareMinerals BareSkin Complete Coverage Concealer - $21.00: I use this on weekdays or nighttime when I need a bit more coverage than the Complexion Rescue offers. The color is perfect and formula thick though it blends better with my foundation than the Complexion Rescue. A little goes a long way so I see this lasting a while. Good bang for your buck.
BareMinerals Moxie Lip Gloss in Rebel - $18.00: I will buy this again and again and again. This is the perfect just pink enough color for everyday wear and high shine. It's not too sticky and lasts hours. Aside from the color, the applicator is a dream. It doesn't pick up too much formula and is narrow and long enough to evenly apply the gloss without extending past your lips.
RCMA No Color Powder - $12.00: This is it, you guys. The holy grail-est of all holy grail products. I could never bring myself to spend $28 on the Laura Mercier setting powder all of youtube swears by so I can't compare it, but this is the best setting power I have ever used. Ever. It is completely transparent so don't be afraid of looking like Casper and it sets your make up all day. No shine and no budging and it's not drying at all. The best part? It's only $12 for 3oz!
Marc Jacobs Beauty Air Blush - $42.00: I go back and forth on this product. The color is really pretty (though its not as pigmented) as I would like and it applies very easily. It is one of the most blendable blushes I've used. The downside is that it doesn't have great staying power. That being said, I still use it most days but don't plan to rebuy it. With a $42 price tag, I expect a little more lasting power. I got this product in an Influenster box earlier this summer so while I like it, I'm glad I didn't pay for it.
Physicians Formula Mineral Wear Airbrushing Bronzer - $13.99: This is a drug store bronzer to add to your arsenal. The price point is on the higher end for drugstore products but it is worth it. The formula is really pigmented and super blendable. A little goes a long way. This is a good hue for contouring as its really warm and not orange giving you a sunkissed glow.
Roller lash is one of my faves! I got it in a birchbox and was the only product I ever decided I needed the full size of!
Thanks for being so helpful for all those who are finding job these days. My husband is one of them who is searching for a job while having one.Will definitely convey your these suggestions to him.


Prefer to discuss the features of Wolf appliances with an expert?
See the complete line of Wolf appliances at your nearest Sub-Zero, Wolf, and Cove showroom.
Unequivocal Wolf performance and design, now smaller and quicker. Combines convection with microwaves for scrumptiously speedy meals.
Custom cycle options, adjustable interiors, customizable exterior finishes, and near-silent operation ensure spotless results—every time.
Earn an extra full year of warranty coverage and ensure a flawless fit with Factory Certified Installation.


text:
/ IoT Zone
How Autonomous Vehicles Might Reshape Our Cities
See the latest research, combined with robotics and AI, to examine how driverless cars will impact urban development in the near future.
Adi Gaskell
May. 01, 18 · IoT Zone ·
The last year or so has seen a number of interesting studies exploring our transition to a driverless world in terms of the costs and benefits the technology might bring. We've had studies exploring productivity benefits, environmental benefits, and congestion benefits.
The latest, from the University of Toronto, explores the impact autonomous vehicles (AVs) could have in terms of the parking space that could be freed up in our towns and cities.
"In a parking lot full of AVs, you don't need to open the doors, so they can park with very little space in between," the authors say. "You also don't need to leave space for each car to drive out, because you can signal the surrounding AVs to move out of the way."
Islands of Cars
Traditionally, parking lots are designed for islands of cars that are able to pull into and out of parking spots. By contrast, parking lots for autonomous vehicles could be designed more like solid grids that would see outer cars moving aside to let inner cars enter and exit. The researchers tested this grid layout to determine the optimal size for storage whilst requiring the fewest moves to extract a given car.
"There's a trade-off," the authors explain. "If you have a very large grid, it leads to a lot of relocations, which means that it takes longer on average to retrieve your vehicle. On the other hand, if you have a number of smaller grids, it wastes a lot of space."
The researchers developed a model that simulated the various possible layouts for an AV parking lot. AI was then used to optimize the layout for the fewest relocations and the maximum parking capacity. This optimization revealed that a well-designed lot could accommodate 62% more AV cars than a conventional parking lot. Indeed, in optimum scenarios, they were able to accommodate 87% more cars.
They also believe that such a system would be infinitely more flexible than current methods so that supply can adapt to meet changing demand due to the ability to rearrange cars to create more space.
Parking in Action
Last year a trial was conducted at Charles de Gaulle Airport in Paris that showed the potential for smarter parking. It wasn't using autonomous vehicles, but rather a 'robotic valet' system developed by French startup Stanley Robotics.
The system is designed to remove the frustrating carousel we embark upon when trying to find a parking space at the terminal. Instead, passengers drop off their cars at the dedicated terminal, before inputting in their flight details. The robot (called Stan) will then collect the car, clamping the wheels before lifting the vehicle into a secure parking spot. The system then logs the travel arrangements of the cars owner and can ensure the vehicle is ready and waiting when their flight lands.
Whichever approach is adopted, it seems that parking is an area ripe for disruption, which creates the tantalizing prospect of large chunks of urban space being freed up for more effective use.
"Right now, our downtown cores have giant municipal parking lots next to major attractions," the UoT researchers say. "AVs could allow us to both shrink and relocate these parking lots, opening up valuable space in cities."
What The Street provides a glimpse into the possibilities at stake. They document the amount of space taken up by parking in 23 cities around the world, and the numbers are mind boggling. For instance, in London the site estimates that there are 66,000 cars parked on any given day, with similar amounts in transit. This correlates to roughly 26 Hyde Park's worth of parking spaces, or 68,288,000 square meters of space. Using the UoT data therefore, it's easy to see the millions of square meters of space that could be put to more productive use.
Your IoT Predictions for 2018 (Part 1)
Top 10 Applications of IoT
Will Driverless Cars Increase Traffic Congestion?
iot ,autonomous cars ,smart cities
Published at DZone with permission of Adi Gaskell , DZone MVB. See the original article here.
IoT Partner Resources

redpajama_set_name: RedPajamaCommonCrawl


If launching, please re-confirm your launch date by calling on the Marina Office number 21337049, during office hours three days before coming down. Do not call the Security or office staff mobiles with your launch date, as they may not be in a position to record your date.
Strong East and North-Easterly (Grigal) winds are expected over the next few days. Yacht owners are advised to check their ropes and ensure that canopies, sails and other loose items are properly secured.
Pontoon F is now complete and the Marina Office is contacting owners of boats to move back to their berths. For your safety and convenience, please do not move until invited to do so.
The renovation of Pontoons A, B and C is well under way.
Very strong southeasterly winds are expected between 21:00 and 02:00 tonight. Owners of boats facing out of the marina should ensure that their ropes are secure, and sails and canopies are properly stowed.
The marina office will be closed on the 2nd April 2013 between 13:30 and 15:30 due to a family funeral. The office will open again as normal after 15:30.


Charoite is called a Stone of Transformation and is a stone of power and a Stone for Courage. In particular it transforms negative emotions such as anger and fear into positive feelings. As part of the transformation process it allows release of those negative emotions. Charoite is also a stone of inspiration, bringing enhanced creativity, spiritual growth, and showing new possibilities even in old situations. This is a stone that allows us to see clearly which brings with it enhanced self-esteem and helps release one from victimization. Charoite can enhance or bring courage, inner strength, and appropriate assertiveness. In the psychic realm, chaorite is used for entity release or rescue work, clairvoyance, and prophesy. Physically, chaorite is helpful for speeding up healing of all kinds, headaches, liver problems, alcohol detoxification, cramps, heart problems, eye problems, and nerves. Color: The colors in each piece of Charoite range from bright lavender, violet and lilac to dark purple with both white, gray and black veining. It forms a swirling pattern of interlocking crystals. Royal lavendar flows with chatoyant streaks of white around bits of black, peach and opalescent gray.
Charoite is an unsual mineral and of rare occurence. Healing ability: Put charoite beneath your pillow, especially if you are prone to disturbed sleep or fears which surface in dreams. In combination with amethyst, it will help to release these fears. Mystical power: Charoite is said to be useful for the cleansing and purification of one's energy body as well as for the transmutation of negative energies within oneself. Charoite transmutes negativity and cleanses the aura. Charoite facilitates the release of fear and dissolution of negative energies encountered in the process of aligning with your soul's purpose. Work with Charoite to foster a synthesis between the heart and crown chakra, walk your spiritual path with awareness, and manifest your soul's wisdom and light in daily life. The purple color of Charoite, among its other roles, helps to bring us closer to spirit by showing us that everything, even the thoughts we hold as true, can be dissolved and replaced with more empowering energies. The color black in this stone grounds the purple ray, and enables us to explore the nature of our fears; while white represents the connection to spirit. Charoite is found in only one location - along the Chary River at Aldan in Russia. Associated with the 3rd Eye, Crown, Solar plexus and Root chakras.
These are a dream! I love the patterns and plum color. ..


There are numerous different ways that impact the social capital in Concord has. First, Concord has many different organizations that engage the residents. The Concord Community Chest is the largest community organization that the residents take part in. This organization hosts different types of events to raise money. Each year 1,300 residents donate various amounts of money (CCC 2015). This money that gets donated is used to provide a wide variety of service to the community. These services include family and individual counseling, legal assistance, job retraining, substance abuse programs, and many more (CCC 2015). The Concord Community Chest helps over 1,000 different residents a year with many different services. There are many other community organizations that connect the Concord residents. The Concord Chamber of Commerce is a key resource for the community. This organization is dedicated to the local businesses, and maintaining a strong economy. The chamber of Commerce organizes the local businesses and making sure they have all the resources necessary to have a successful business and contribute to the economy. Other than the organizations, Concord residents are always active with each other. There are many different neighborhoods that have close ties with each one another and have block parties, BBQs, and other neighborly events. These different aspects of the social capital in Concord make the community highly desirable to live in.


With HART, additional parameterization and diagnostic functions, as well as process data, are available in addition to the 4 to 20 mA signal.
The HART product portfolio of Phoenix Contact offers expertise in Ethernet, Modbus TCP/IP, PROFINET, wireless and HART protocol technology. Our active involvement in various key standards committees enables us to guarantee you a state-of-the-art product portfolio. Discover our product range now. Find out how you can make your future automation projects even more successful.
With over two decades of experience in the harsh world of process technologies, HART (Highway Addressable Remote Transducer) no longer needs to prove its position as a leader. Combined with the solutions from Phoenix Contact, we can easily prepare your application for digital progress in the decades to come.
Across the world, 40 million devices with the integrated HART signal have already been installed. To a great extent, this signal is not only used to increase availability. Our affordable solution now makes it worth your while to permanently monitor your critical devices and instrument.
The Wired HART infrastructure products are designed for use in the control cabinet or the marshalling area in the vicinity of the control system. In addition to maintenance directly on the device, permanent monitoring is possible without influencing the ongoing process.
Furthermore, we also make it possible for you to monitor your HART data directly in the control room. This costs you a fraction of on site deployment.
Our portfolio offers you a range of functionalities which are essential for continuously high availability of your installation.
WirelessHART is the modern solution for digital access to your field devices: developed with Industrial Wireless expertise, together with leading companies in the process industry. Based on the latest in license-free spectrum, and 128 bit AES security, the solution is inexpensive, reliable and secure.
Thanks to the adapter, the functionality of each existing HART device can be enhanced with wireless functionality. The adapted devices are compatible with all other WirelessHART devices. They can be operated together in a WirelessHART mesh network without any problems.
WirelessHART can attribute its robustness and reliability to the underlying TSMP protocol and the 2.4 GHz frequency band (according to IEEE 802.15.4).
The standard used with 10 mW wireless transceiver makes WirelessHART a system that can be used across the world, with low power consumption.


Patients throughout the Baltimore region seek the expertise of The Urology Specialists of Maryland at Mercy Medical Center. Our urologists treat renal failure, also known as kidney failure, by working closely with nephrologists to provide patients with comprehensive care.
Kidneys filter waste from the blood. When they cannot perform their function, the body is in kidney failure and waste accumulates to dangerous levels. Acute kidney failure mostly occurs in patients who have already been diagnosed with medical conditions and have been hospitalized. Kidney failure can be reversed, however, and depending on the patient's overall health, kidneys can return to normal.
If the kidney failure cannot be reversed, a kidney transplant may be needed.


text:
Elizabeth Osborne
Floating Landscapes 1971-1979
Manchester Islands, 1978
Passage, 1972
Delos, 1972
Emerald Sea, 1975
Pinon, 1979
August 7, 2006, Philadelphia, PA—An exhibition of early landscape paintings by Elizabeth Osborne will be on view at Locks Gallery from September 8 through October 14, 2006. A catalogue of the exhibition with an essay by curator Robert Cozzolino will be available.
The New England coastal landscape, an ongoing subject for the artist since the 1960s, and the desert landscape of New Mexico are reinterpreted throughout the 1970s. Robert Cozzolino describes these paintings as "living landscapes [that] integrate complex methods of seeing and representing nature." The series becomes increasingly more ambitious as the artist continues to experiment with and master a new technique. Included in the present exhibition is the largest canvas of the artist's career, 1974's expansive Bluewater.
The technique used in this series, controlled pours and stains on unprimed canvas, became a vital breakthrough for the artist. Aware of a similar technique pioneered by painters such as Helen Frankenthaler and Morris Louis, Osborne was initially interested in capturing the light and transparency of her watercolors and translating those qualities to canvas. Anne d'Harnoncourt wrote of this series in 1976 "...the large, lyrical paintings approach abstraction without abandoning a sense of spatial depth...Compositions [are] built up of large, brilliant expanses of yellow, orange, blue and flaming red, under remote skies of lavender or aquamarine. "
This exhibition marks the first time this pivotal, early series will be shown together since the 1970s. All of the paintings on exhibit have been borrowed from private and museum collections.
A graduate of the Pennsylvania Academy of the Fine Arts and the University of Pennsylvania, Osborne has taught at the Academy since 1961. Osborne's work is in many public and private collections including the Philadelphia Museum of Art; the Pennsylvania Academy of the Fine Arts; and the Delaware Art Museum; among others. Elizabeth Osborne was born in 1936 in Philadelphia, PA. She currently works and lives in Philadelphia.
View all Artists Publications
Elizabeth Osborne: Luminous Gestures
Text by Carter Ratcliff 2013
Elizabeth Osborne: The Color of Light
Text by Robert Cozzolino 2009
Elizabeth Osborne: Floating Landscapes (1971-1979)
Elizabeth Osborne: Vantage
Text by Donna Gustafson 2000
Text by C.K. Williams 1997
Text by Alexandra Anderson-Spivy 1992


2 Bedroom Terraced House For Sale in Warrington for Offers in the region of £100,000.
A fabulous opportunity for both first time buyers or investors, the property has recently been refurbished throughout and is marketed with the added benefit of being sold with no onward chain. The property briefly comprises of lounge, fully fitted kitchen, and a three piece bathroom with a shower over the bath. Upstairs there are two good sized double bedrooms. Externally there is on-street parking at the front of the property and a paved yard to the rear.


text:
Q: c++ make parts of template class into header and cpp I have a list_sorter class which gets a T type list and makes it sorted.
There is my problem: it is working in one cpp file. When I try to decompose it to header file and another cpp file, I get undefined reference to `list_sorter::list_sorter(std::__cxx11::list >)'.
I've read many examples, but cannot solve this.
Could somebody help me, what am I doing wrong? What should I change?
Please see my files and codes lower:
listsort.h:
#ifndef listsort
#define listsort
#include<list>

template <typename T>
class list_sorter{
    private:
    std::list<T> adat;

    public:
    list_sorter(std::list<T> be);
    void add(std::list<T> uj);
    void print();
};
#endif

list_sorter.cpp:
#include<list>
#include"listsort.h"
template <typename T>
class list_sorter{
    private:
    std::list<T> adat;

    public:
    list_sorter(std::list<T> be): adat(be) { adat.sort();}
    void add(std::list<T> uj){
        for(auto a:uj) adat.push_back(a);
        adat.sort();
    }
    void print(){for(auto a:adat) cout<<a<<" ";}
};

try.cpp:
#include"listsort.h"
#include<iostream>
#include<list>

using namespace std;

int main(){
    list<int> tombocske;

    tombocske.push_back(70);
    tombocske.push_back(7);
    tombocske.push_back(2);
    tombocske.push_back(12);

    list_sorter<int> rendezo(tombocske);
    rendezo.print(); cout<<endl<<endl;

    list<int> tili;
    tili.push_back(45);
    tili.push_back(1);
    tili.push_back(32);
    tili.push_back(7);

    rendezo.add(tili);
    rendezo.print();

    list<string> tali;
    tali.push_back("gyozo");
    tali.push_back("kave");
    tali.push_back("alma");
    tali.push_back("pipa");

    list_sorter<string> rendezo2(tali);

    rendezo2.print();

}

redpajama_set_name: RedPajamaStackExchange


text:
Alt-Right Leader Richard Spencer Turns Against Trump: 'It's a Betrayal of His Campaign'
By Michael van der Galien 2017-04-27T09:01:37
In a video he uploaded to YouTube earlier this month, alt-right leader (and white supremacist) Richard Spencer turned against Donald Trump, whom he supported rather passionately during last year's presidential campaign.
The reason Spencer is angry with Trump is obvious: the president had the audacity to carry out airstrikes against Assad's forces in Syria. Since Assad is an ally of Vladimir Putin -- whom the alt-right worships -- this is apparently a deadly sin.
"This is a betrayal of the meaning of his candidacy," Spencer complains. "He ran on America first, we're not going to do all that crap of Barack Obama, Libya, all that kind of stuff. We're not going to do all those disastrous wars of George W. Bush. No, it's different. It's America first," Spencer summarizes Trump's campaign message.
According to Spencer, Trump broke that promise by bombing Assad's forces.
The controversial Putin cheerleader continues by saying that this proves there is a grand conspiracy going on!
I kid you not.
This proves that Donald Trump is not in control. And no, I'm not saying that this is some kind of conspiracy theory [which he actually does], that the lizards are in control... I mean that Donald Trump is not fully in control of his presidency. He is controlled by some -- perhaps the best scenario -- he is controlled by some extremely short-sighted calculus that he's going to wag the dog, create some war, distract people, or seem tough, or destroy the Russian narrative, or whatever.
And then he starts on the globalists, although he seems to confuse "being in control of" with "being controlled by," which he does several times in the video, after which he corrects himself a few times:
He's in control of the globalist international community that has been yammering about the removal of Assad for years. He's being controlled by neoconservative operatives. I have to say, I thought these guys has been humiliated.
But no. Those darned neocons (which is alt-right speak for Jews) are back:
I feel like I'm in a *** timewarp when I see Paul Wolfowitz [a Jewish foreign policy expert] is writing op-eds for the Wall Street Journal again. I mean, what in the hell happened?
Next, Spencer takes aim at Rex Tillerson and Nikki Haley, both of whom he apparently considers to be evil incarnate. Trump's son-in-law, Jared Kushner, is also blamed for Trump's Syria policy. As everybody knows, Kushner, like Wolfowitz, is Jewish, which -- of course -- makes him an ideal target for the antisemitic alt-right movement.
https://pjmedia.com/trending/2017/04/27/alt-right-leader-richard-spencer-turns-against-trump-its-a-betrayal-of-his-campaign/
Related: Donald Trump


The Argentinian Football Association (AFA) have confirmed that they will not be extending Diego Maradona's contract as coach of the national team, due to a dispute over his back-room team.
Maradona had stated that he was keen to stay on, but refused to agree to the AFA's demands that wholesale changes were to be made to his support staff. The AFA executive committee then voted not to renew his deal.
Estudiantes coach Alejandro Sabella and Racing Club boss Miguel Angel Rosso have also been mooted as potential successors to Maradona.