对于我的项目,我首先从文件加载图像,然后将每个像素放入2D pixels[,]数组中。然后,我想检查每个像素,并根据它们的颜色将它们分成“bin”,然后对每个bin进行排序。因此,我有一个Bin对象,该对象封装了List<Pixel>,并且我有一个List<Bin>,其中包含(相对较小)数量的容器。

我的问题是,当我尝试从非常大的图像(例如1920x1200 = 230万像素)填充这些垃圾箱时,我使用的算法比我想要的算法慢,并且我将问题归结为某些C#特定于语言的功能所花费的时间似乎比我预期的要长。我想要一些有关如何更好地使用C#消除这些瓶颈的建议。

加载图像后,我调用一个名为“fillBinsFromSource”的函数,该函数获取一个可枚举的像素列表,找到它们所属的bin,然后将其放置在其中:

public void fillBinsFromSource(IEnumerable<Pixel> source)
    {
        Stopwatch s = new Stopwatch();
        foreach (Pixel p in source)
        {
            s.Start();
            // algorithm removed for brevity
            // involves a binary search of all Bins and a List.Add call
            s.Stop();
        }
    }

对于大图像,预计我的算法会花费一些时间,但是当我将Stopwatch放到函数调用之外时,事实证明它花费的时间大约是s所花费时间的两倍,这意味着使用foreach进行枚举占用此功能一半的时间(对于1920x1200图像,该时间约为1.6秒钟的800毫秒)。

我需要传递一个枚举列表的原因是因为有时用户只会添加图片的一小部分,而不是整个图片。耗时的调用向下传递几个迭代器,首先从图像列表中传递,然后从列表中的每个图像中传递,如下所示(简化):
class ImageList : IEnumerable<Pixel>
{
    private List<Image> imageList;
    public IEnumerator<Pixel> GetEnumerator()
    {
        foreach (Image i in imageList)
            foreach (Pixel p in i)
                yield return p;
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

class Image : IEnumerable<Pixel>
{
    private Pixel[,] pixels; // all pixels in the image
    private List<Pixel> selectedPixels;// all pixels in the user's selection

    public IEnumerator<Pixel> GetEnumerator()
    {
        if (selectedPixels == null)
            for (int i = 0; i < image.Width; i++)
                for (int j = 0; j < image.Height; j++)
                    yield return pixels[i, j];
        else
            for (int i = 0; i < selectedPixels.Count; i++)
                yield return selectedPixels[i];
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

最后我叫这个
ImageList list; // pretend it contains only 1 image, and it's large
fillBinsFromSource(list);

问题1)由于需要枚举2D像素阵列和所选区域,这取决于用户选择的内容,因此枚举实际上非常缓慢。我怎样才能加快速度?

然后,在用Pixel对象填充所有这些垃圾箱之后,对它们进行排序。我称List<Pixel>.Sort()并依赖IComparable,如下所示:
ImageList list; // pretend it contains only 1 image, and it's large
fillBinsFromSource(list);
foreach(Bin b in allBins)
    b.Sort(); // calls List<Pixel>.Sort()


class Pixel : IComparable
{
    // store both HSV and RGB
    float h, s, v;
    byte r, g, b;

    // we sort by HSV's value property
    public int CompareTo(object obj)
    {
        // this is much faster than calling CompareTo on a float
        Pixel rhs = obj as Pixel;
        if (v < rhs.v)
            return -1;
        else if (v > rhs.v)
            return 1;
        return 0;
    }

问题2)假设allBins有7个元素;排序7个单独的列表(其中总共包含230万个Pixel)大约需要2秒钟的时间。对230万个随机int的列表进行排序所需的时间不到200毫秒。我可以体会到使用原始类型有一定程度的提速,但是使用IComparable真的慢10倍以上吗?这里有提速吗?

对于长期存在的问题,我深表歉意,如果有人对我有任何建议,我将不胜感激!

最佳答案

您确实需要分析您的代码,然后看看运行缓慢。

最明显的:

  • Pixel没有实现通用的IComparable<Pixel>,因此Compare必须做更多的工作。
  • 尝试设置像素值类型。您很可能会看到性能下降,但可能并非如此。
  • 如果发现性能低于可接受的范围,请考虑传递2d范围(矩形)并直接通过索引而不是迭代器访问Pixel对象。迭代器很好,但不是免费的。
  • 关于c# - 使用List <T> .Sort和IEnumerable加快算法速度,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/13654278/

    10-12 18:48