         cudnnStatus_t err = CUDNN_STATUS_SUCCESS;
         callCudnnIfNoErr(err, cudnnSetStream, *handle, *context.getCudaStream());
 
-        cudnnCTCLossDescriptor_t  ctcLossDesc;
-        cudnnTensorDescriptor_t probsDesc = nullptr;
-        cudnnTensorDescriptor_t gradsDesc = nullptr;
-        callCudnnIfNoErr(err, cudnnCreateCTCLossDescriptor, &ctcLossDesc);
-        callCudnnIfNoErr(err, cudnnSetCTCLossDescriptorEx, ctcLossDesc, CUDNN_DATA_FLOAT, CUDNN_LOSS_NORMALIZATION_SOFTMAX, CUDNN_PROPAGATE_NAN);
-        callCudnnIfNoErr(err, cudnnCreateTensorDescriptor, &probsDesc);
-        callCudnnIfNoErr(err, cudnnSetTensorNdDescriptor, probsDesc, cudnnDataType(probs.dataType()), probs.rankOf() , dims, strides);
-        if(!grads.isEmpty()){
+        CTCLossDesc  ctcLossDesc;
+        CudnnTensor probsDesc, gradsDesc(false);
+        bool calcGrads = !grads.isEmpty();
+        ctcLossDesc.set( err, CUDNN_DATA_FLOAT, CUDNN_LOSS_NORMALIZATION_SOFTMAX, CUDNN_PROPAGATE_NAN);
+        probsDesc.set( err, cudnnDataType(probs.dataType()), probs.rankOf() , dims, strides);
+
+        if(calcGrads){
+            gradsDesc.create();
             const int gradStrides[] = {(int)grads.strideAt(0), (int)grads.strideAt(1), (int)grads.strideAt(2)};
-            callCudnnIfNoErr(err, cudnnCreateTensorDescriptor, &gradsDesc);
-            callCudnnIfNoErr(err, cudnnSetTensorNdDescriptor, gradsDesc, cudnnDataType(grads.dataType()), grads.rankOf() , dims, gradStrides);
+            gradsDesc.set( err, cudnnDataType(grads.dataType()), grads.rankOf() , dims, gradStrides);
         }
 
         size_t tempWorkSpaceSize=0;

namespace platforms {



    template<typename Op, typename ...Args>
    void callCudnnIfNoErr(cudnnStatus_t &err, Op op, Args&&... args){
        if(err==CUDNN_STATUS_SUCCESS){
            err = op(std::forward<Args>(args)...);
            if(err){
                nd4j_printf("Cudnn error code %s\n",cudnnGetErrorString(err));
            }
        }
    }

    template <typename T>
    const T* bufferInHost( const NDArray &array)  {
        array.syncToHost();
        return reinterpret_cast<const T*>(array.buffer());
    }

    std::vector<int> getConcatTargets(const NDArray &targetLabels, const NDArray &targetLabelLengths){
                //concatenate target labels

        auto logitInputLengths = INPUT_VARIABLE(3);
        auto outputGradients = OUTPUT_VARIABLE(0);
        auto context = block.launchContext();
        REQUIRE_TRUE(outputGradients->isSameShape(logitInput), 0, "CtcLoss Gradient: wrong shape of output array, expected is %s but got %s instead !", ShapeUtils::shapeAsString(logitInput).c_str(), ShapeUtils::shapeAsString(outputGradients).c_str());
        //in Cudnn Batch is in the middle dimension
        logitInput->permutei({1,0,2});
        outputGradients->permutei({1,0,2});
