/// <summary>
        /// Executes kernel
        /// </summary>
        /// <param name="n">Work size</param>
        /// <returns>Current instance of kernel</returns>
        public OpenCLKernel Run(int n)
        {
#if ENABLE_PROFILING
            Console.WriteLine("Kernel \"" + kernel.FunctionName + "\" binding took " + profilingStopwatch.ElapsedMilliseconds + " ms");
            profilingStopwatch.Restart();
#endif

            NOpenCL.CommandQueue commandQueue = kernelSet.OwnerDevice.CommandQueue;

            using (Event perfEvent = commandQueue.EnqueueNDRangeKernel(kernel, new[] { (IntPtr)n }, null)) {
#if ENABLE_PROFILING
                Event.WaitAll(perfEvent);

                ulong queuedToEnd = (perfEvent.CommandEndTime - perfEvent.CommandQueuedTime) / 1000000;
                ulong startToEnd  = (perfEvent.CommandEndTime - perfEvent.CommandStartTime) / 1000000;

                Console.WriteLine("Kernel \"" + kernel.FunctionName + "\" execution took " + startToEnd + " ms (" + queuedToEnd + " ms)");
#endif
            }

#if ENABLE_PROFILING
            Console.WriteLine("Kernel \"" + kernel.FunctionName + "\" enqueue took " + profilingStopwatch.ElapsedMilliseconds + " ms");
            profilingStopwatch.Restart();
#endif

            return(this);
        }
        /// <summary>
        /// Cleans up resources after execution
        /// </summary>
        public void Finish()
        {
            NOpenCL.CommandQueue commandQueue = kernelSet.OwnerDevice.CommandQueue;

            commandQueue.Finish();

            // Dispose all created buffers
            for (int i = 0; i < createdBuffers.Count; i++)
            {
                if (createdBuffers[i].HasOwnership)
                {
                    continue;
                }

                createdBuffers[i].Synchronize();
                createdBuffers[i].Dispose();
            }

            createdBuffers.Clear();

            currentArg = 0;
        }