Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/261.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/database/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# C微优化查询:IEnumerable替换_C#_Optimization - Fatal编程技术网

C# C微优化查询:IEnumerable替换

C# C微优化查询:IEnumerable替换,c#,optimization,C#,Optimization,注意:我之所以优化是因为过去的经验和profiler软件的建议。我意识到另一种优化方法是减少调用GetNeights的次数,但这是目前的次要问题 我有一个非常简单的函数,如下所述。通常,我在foreach循环中调用它。我经常调用这个函数,大约每秒100000次。不久前,我用Java编写了这个程序的一个变体,对它的速度非常反感,最后我用4个if语句替换了几个使用它的for循环。循环展开看起来很难看,但它确实在应用程序速度上产生了显著的差异。因此,我提出了一些潜在的优化方案,并想征求对其优点的意见和

注意:我之所以优化是因为过去的经验和profiler软件的建议。我意识到另一种优化方法是减少调用GetNeights的次数,但这是目前的次要问题

我有一个非常简单的函数,如下所述。通常,我在foreach循环中调用它。我经常调用这个函数,大约每秒100000次。不久前,我用Java编写了这个程序的一个变体,对它的速度非常反感,最后我用4个if语句替换了几个使用它的for循环。循环展开看起来很难看,但它确实在应用程序速度上产生了显著的差异。因此,我提出了一些潜在的优化方案,并想征求对其优点的意见和建议:

使用四个if语句,完全忽略干燥原则。根据过去的经验,我相信这将提高绩效,但这让我很难过。为了澄清这一点,4个if语句将粘贴到我调用getNeights太频繁的任何地方,然后将foreach块的内部粘贴到其中。 以某种神秘的方式记下结果。 将邻居属性添加到所有正方形。在初始化时生成其内容。 作为编译的一部分,使用代码生成实用程序将对GetNeights的调用转换为if语句

public static IEnumerable<Square> GetNeighbors(Model m, Square s)
{
    int x = s.X;
    int y = s.Y;        
    if (x > 0) yield return m[x - 1, y];
    if (y > 0) yield return m[x, y - 1];
    if (x < m.Width - 1) yield return m[x + 1, y];
    if (y < m.Height - 1) yield return m[x, y + 1];
    yield break;
}

//The property of Model used to get elements.
private Square[,] grid;
//...
public Square this[int x, int y]
{
    get
    {
        return grid[x, y];
    }
}

注意:GetNeights函数花费的20%的时间用于调用m.get_项,另80%的时间用于方法本身。

我建议创建一个容量为4的平方数组,然后返回该数组。对于在性能敏感的上下文中使用迭代器,我会非常怀疑。例如:

// could return IEnumerable<Square> still instead if you preferred.
public static Square[] GetNeighbors(Model m, Square s)
{
    int x = s.X, y = s.Y, i = 0;
    var result = new Square[4];

    if (x > 0) result[i++] = m[x - 1, y];
    if (y > 0) result[i++] = m[x, y - 1];
    if (x < m.Width  - 1) result[i++] = m[x + 1, y];
    if (y < m.Height - 1) result[i++] = m[x, y + 1];

    return result;
}

如果速度快得多,我也不会感到惊讶。

我正处在一个下滑的斜坡上,所以在这里插入免责声明

我会选择第三种。懒洋洋地填写邻居的推荐信,你就有了一种记忆

public class Square {

    private Model _model;
    private int _x;
    private int _y;
    private Square[] _neightbours;

    public Square(Model model, int x, int y) {
        _model = model;
        _x = x;
        _y = y;
        _neightbours = null;
    }

    public Square[] Neighbours {
        get {
            if (_neightbours == null) {
                _neighbours = GetNeighbours();
            }
            return _neighbours;
        }
    }

    private Square[] GetNeightbours() {
        int len = 4;
        if (_x == 0) len--;
        if (_x == _model.Width - 1) len--;
        if (_y == 0) len--;
        if (-y == _model.Height -1) len--;
        Square [] result = new Square(len);
        int i = 0;
        if (_x > 0) {
            result[i++] = _model[_x - 1,_y];
        }
        if (_x < _model.Width - 1) {
            result[i++] = _model[_x + 1,_y];
        }
        if (_y > 0) {
            result[i++] = _model[_x,_y - 1];
        }
        if (_y < _model.Height - 1) {
            result[i++] = _model[_x,_y + 1];
        }
        return result;
    }

}
另一种记忆是返回一个数组而不是一个惰性的IEnumerable,而getNeights则变成了一个纯粹的函数,不需要记忆。不过,这大致相当于选项3

在任何情况下,但你知道这一点,概况和重新评估的每一步的方式。例如,我不确定惰性IEnumerable与直接返回结果数组之间的权衡。您可以避免一些间接操作,但需要进行分配。

Brian

我在代码中遇到过类似的事情

我发现C对我帮助最大的两件事是:

首先,不要害怕分配。C内存分配非常非常快,因此动态分配数组通常比生成枚举数更快。然而,这是否有帮助很大程度上取决于您如何使用结果。我看到的唯一陷阱是,如果返回一个固定大小的数组4,则必须在使用结果的例程中检查边缘情况

根据模型中正方形矩阵的大小,最好先进行一次检查,看看是否在边缘,如果不在边缘,则预计算完整数组并返回它。如果您处于边缘,您可以分别处理这些特殊情况,并根据需要创建一个1或2元素数组。这可能会有一个更大的说法,但根据我的经验,这通常更快。如果模型很大,我将避免预计算所有的邻居。广场上的开销可能超过好处

根据我的经验,预分配和返回与使用yield相比,JIT更可能内联您的函数,这在速度上会有很大的不同。如果您可以利用IEnumerable结果,并且并不总是使用每个返回的元素,这会更好,但如果不是这样,预计算可能会更快

另一个要考虑的事情——我不知道在你的情况下在方块中保存什么信息,但是如果HTE对象相对较小,并且被用在一个大的矩阵中,并且反复很多次,就把它构造成一个结构。我有一个类似的例程,在一个循环中调用数十万次或数百万次,将类更改为结构,在我的例子中,将例程的速度提高了40%以上。不过,这是假设您使用的是.NET3.5sp1,因为JIT在最新版本中对结构进行了更多的优化


当然,切换到struct与class还有其他潜在的缺陷,但这可能会对性能产生巨大影响。

为什么不让Square类负责返回它的邻居呢?然后,您就有了一个很好的地方来进行惰性初始化,而无需额外的记忆开销

public class Square {

    private Model _model;
    private int _x;
    private int _y;
    private Square[] _neightbours;

    public Square(Model model, int x, int y) {
        _model = model;
        _x = x;
        _y = y;
        _neightbours = null;
    }

    public Square[] Neighbours {
        get {
            if (_neightbours == null) {
                _neighbours = GetNeighbours();
            }
            return _neighbours;
        }
    }

    private Square[] GetNeightbours() {
        int len = 4;
        if (_x == 0) len--;
        if (_x == _model.Width - 1) len--;
        if (_y == 0) len--;
        if (-y == _model.Height -1) len--;
        Square [] result = new Square(len);
        int i = 0;
        if (_x > 0) {
            result[i++] = _model[_x - 1,_y];
        }
        if (_x < _model.Width - 1) {
            result[i++] = _model[_x + 1,_y];
        }
        if (_y > 0) {
            result[i++] = _model[_x,_y - 1];
        }
        if (_y < _model.Height - 1) {
            result[i++] = _model[_x,_y + 1];
        }
        return result;
    }

}

根据GetNeights的使用情况,控制反转可能会有所帮助:

public static void DoOnNeighbors(Model m, Square s, Action<s> action) {
  int x = s.X;
  int y = s.Y;        
  if (x > 0) action(m[x - 1, y]);
  if (y > 0) action(m[x, y - 1]);
  if (x < m.Width - 1) action(m[x + 1, y]);
  if (y < m.Height - 1) action(m[x, y + 1]);
}

但我不确定这是否有更好的性能。

我正在提出完全相同的建议。IEnumerable的使用在封面下引入了一些隐藏的成本,并且对于仅返回4个元素,您不需要灵活性
IEnumerable提供了简洁和易用性。我发现即使我有很多元素,IEnumerable也太贵了。这并不是唯一一个为此付出代价的函数。您可能有兴趣阅读这篇博客文章,其中描述了迭代器的特定代码生成和返回。在这种情况下,当需要优化时,放弃原则是可以的。不要为此感到难过,只要记录下来,这样未来的开发人员就不会重构您的优化。DoOnNeighbors听起来可能会好4倍,因为它使用一个委托而不是4个枚举。如果模型中没有太多的正方形,这很好,但是如果有很多正方形,则会添加很多指针。10万次/秒的循环显示了很多方块,所以我也会注意记忆。