部分排序算法,为什么会有这样的性能差异?

时间:2013-07-17 10:47:19

标签: c# sorting cudafy.net

我有一个随机生成的数字列表,其中包含1900个数字,我想获得前190个数字的排序列表。我编写了两个版本的部分排序算法,第一个是cpu版本,第二个是编写的,所以它可以在Cudafy.net上运行,但是它们之间的执行时间差别很大,在CPU上运行时,我想知道是否有人可以解释为什么+可以进一步提高第二版本。

注意:第二个算法将在gpu上运行,所以我不能使用linq或任何不能在c上运行的因为我将使用cudafy.net来运行代码。不幸的是,cudafy.net也不支持锯齿状阵列。

版本1:

    /// <summary>
    /// Sequentially runs through all the values in the array and identifies if 
    /// the current number is less than the highest number in the sorted list.
    /// </summary>
    /// <param name="numbers"> Unsorted array of numbers.</param>
    /// <param name="sortedNumbers"> Array used to hold the partial list of sorted numbers.</param>
    public static void NewSorter(int[] numbers, int[] sortedNumbers)
    {
        for (int i = 0; i < numbers.Length; i++)
        {
            if (sortedNumbers[sortedNumbers.Length - 1] > numbers[i])
            {
                //Update numbers
                IdentifyPosition(sortedNumbers, numbers[i]);
            }
        }
    }

    /// <summary>
    /// Identifies the position the number should be placed in the partial list of sorted numbers.
    /// </summary>
    /// <param name="sortedNumbers"> Array used to hold the partial list of sorted numbers.</param>
    /// <param name="NewNumber"> Number to be inserted.</param>
    static void IdentifyPosition(int[] sortedNumbers, int NewNumber)
    {
        for (int i = 0; i < sortedNumbers.Length; i++)
        {
            if (NewNumber < sortedNumbers[i])
            {
                //Offset and add.
                ArrayShifter(sortedNumbers, i, NewNumber);
                break;
            }
        }
    }

    /// <summary>
    /// Moves all the elements to the right of a point up one and 
    /// then places the new number in the specified point.
    /// </summary>
    /// <param name="SortedNumbers"> Array used to hold the partial list of sorted numbers.</param>
    /// <param name="position"> Position in the array where the new number should be place.</param>
    /// <param name="NewNumber"> Number to include in the array.</param>
    static void ArrayShifter(int[] SortedNumbers, int position, int NewNumber)
    {
        for (int i = SortedNumbers.Length - 1; i > position; i--)
        {
            SortedNumbers[i] = SortedNumbers[i - 1];
        }

        SortedNumbers[position] = NewNumber;
    }

上述版本在~0.65毫秒内执行

第2版:

    /// <summary>
    /// Sequentially runs through all the values in the array and identifies if 
    /// the current number is less than the highest number in the sorted list.
    /// </summary>
    /// <param name="unsortedNumbers"> Unsorted numbers.</param>
    /// <param name="lookbackCount"> Length of the array.</param>
    /// <param name="sortedNumbers"> Array which will contain the partial list of sorted numbers.</param>
    [Cudafy]
    public static void CudaSorter(GThread thread, long[,] unsortedNumbers, int[] lookbackCount, long[,] sortedNumbers)
    {
        int threadIndex = thread.threadIdx.x;
        int blockIndex = thread.blockIdx.x;
        int threadsPerBlock = thread.blockDim.x;
        int gpuThread = (threadIndex + (blockIndex * threadsPerBlock));

        if (gpuThread < 32)
        {
            int maxIndex = (lookbackCount[gpuThread] * 10) / 100;
            int maxLookback = lookbackCount[gpuThread];

            for (int i = 0; i < maxLookback; i++)
            {
                if (sortedNumbers[gpuThread, maxIndex] > unsortedNumbers[gpuThread, i])
                {
                    //Update numbers
                    IdentifyPosition2(sortedNumbers, unsortedNumbers[gpuThread, i], maxIndex, gpuThread);
                }
            }
        }
    }


    /// <summary>
    /// Identifies the position in the sortedNumbers array where the new number should be placed.
    /// </summary>
    /// <param name="sortedNumbers"> Sorted numbers.</param>
    /// <param name="newNumber"> Number to be included in the sorted array.</param>
    /// <param name="maxIndex"> length of sortedNumbers array. </param>
    /// <param name="gpuThread"> GPU thread index.</param>
    [Cudafy(eCudafyType.Device)]
    public static void CudaIdentifyPosition(long[,] sortedNumbers, long newNumber, int maxIndex, int gpuThread)
    {
        for (int i = 0; i < maxIndex; i++)
        {
            if (newNumber < sortedNumbers[gpuThread, i])
            {
                //Offset and add.
                ArrayShifter2(sortedNumbers, i, newNumber, maxIndex, gpuThread);
                break;
            }
        }
    }


    /// <summary>
    /// Shifts all the elements to the right of the specified position, 1 position
    /// to the right, and insert the new number in the specified position.
    /// </summary>
    /// <param name="sortedNumbers"> Sorted Numbers.</param>
    /// <param name="position"> Where the new number needs to be inserted.</param>
    /// <param name="newNumber"> New number to insert.</param>
    /// <param name="maxIndex"> Length of sortedNumbers array.</param>
    /// <param name="gpuThread"> GPU thread index.</param>
    [Cudafy(eCudafyType.Device)]
    public static void CudaArrayShifter(long[,] sortedNumbers, int position, long newNumber, int maxIndex, int gpuThread)
    {
        for (int i = maxIndex - 1; i > position; i--)
        {
            sortedNumbers[gpuThread, i] = sortedNumbers[gpuThread, i - 1];
        }

        sortedNumbers[gpuThread, position] = newNumber;
    }

以上执行时间为2.8毫秒,即慢4倍。

我已经尝试了以下内容:

  1. 声明maxLookBack计数的局部变量,并在for循环中使用它=&gt;没有得到改善。
  2. 将数据类型从long [,]更改为int [,] =&gt; 2.6毫秒(这不可行,因为我需要长时间使用。)
  3. 将int [,]更改为int [] =&gt; 1.3毫秒(这不可行,因为我需要将多个阵列传递给GPU以保持其占用。)我很惊讶这影响了时间。
  4. 编辑:由于Henk的评论,我修改了代码。我现在在gpu上使用unsortedNumbers [32,1900]与cpu排序1数组上的单个线程运行gpu版本。即使我将cpu时间乘以32,它仍然远低于gpu的时间。

1 个答案:

答案 0 :(得分:0)

在这里感到失望之后,我决定阅读一些有关该任务的信息,以了解它的含义。

因此,您需要从一个大型数组中选择一个最小数目的子数组,然后对其进行排序。最好不要为CPU提供一个选项:遍历数组,选择低点,然后通过移动元素将它们立即插入最终数组中。显然,将在选择过程中进行排序。

但是,我无法想象如何并行采样!此外,您需要使用并行度很好的排序算法。否则,如果按顺序解决任务,则图形内核肯定会失去CPU内核的速度,而CPU内核的频率更高,数据访问速度更快!

我认为合并排序可以帮助您。只是不要取低点然后进行排序,而是立即对所有内容进行排序!然后选择N个第一个或最后一个元素。

很遗憾,我现在还无法编辑您的代码。但我希望至少有一点用。