编译器会优化创建此列表两次吗?

问题描述 投票:0回答:4
public SharpQuery Add(params HtmlNode[] elements)
{
    var nodes = new List<HtmlNode>(_context.Count + elements.Length);
    nodes.AddRange(_context);
    nodes.AddRange(elements);
    return new SharpQuery(nodes, this);
}

public SharpQuery(IEnumerable<HtmlNode> nodes, SharpQuery previous = null)
{
    if (nodes == null) throw new ArgumentNullException("nodes");
    _previous = previous;
    _context = new List<HtmlNode>(nodes);
}

我有一大堆函数,可以创建一个新的

List<T>
,向其中添加一堆节点,然后将其传递给该构造函数,该构造函数获取列表,并用它创建另一个新列表。

编译器是否足够聪明,能够发现它实际上不需要创建两次列表?

c# optimization
4个回答
5
投票

这并不是“足够聪明”的情况——编译器按照它的指示去做;您告诉它创建多个列表:它将创建多个列表。

但是,既然你及时释放了它们,它们应该会被相当干净地收集起来,希望是第 0 代。因此,除非你在一个紧密的循环中这样做,否则我不会对此感到太兴奋。

如果您想避免使用列表,您可能考虑 LINQ

Concat
,它允许您附加序列而无需任何额外的列表/集合/等。


2
投票

如果你告诉它创建一个新对象,它就会创建一个新对象。我认为没有一种优化可以用强制转换和分配来替换构造函数调用 - 编译器必须对构造函数的作用有太多了解才能以这种方式优化它。

从技术上讲,您可以自己完成 -

_context = (List<HtmlNode>)nodes;
- 这就是您希望编译器执行的操作。或者,更好的是,
_context = nodes as List<HtmlNode> ?? new List<HtmlNode>(nodes)

但在任何一种情况下,该列表都可能在您的班级之外进行修改,因此您必须确保它不会导致意外行为。

到目前为止,还有些过早优化的味道。您的代码看起来不错,在看到实际的性能问题之前我不会更改任何内容。


2
投票

不,编译器不能进行这样的优化。

由于构造函数采用 IEnumerable,因此您可以创建表达式而不是列表:

public SharpQuery Add(params HtmlNode[] elements) {
  return new SharpQuery(_context.Concat(elements), this);
}

Concat
方法将创建一个表达式,首先返回
_context
的项目,然后返回
elements
的项目。当在构造函数中创建列表时,它将使用直接从
_context
elements
读取的表达式,因此集合仅创建一次。


1
投票

(JIT 的)编译器无法对此进行优化。它为您创建两个列表。然而问题是,这是否会给您带来性能问题。有了绩效,你就可以衡量、衡量、再衡量。在 98% 的情况下,我认为这段代码不会出现任何问题。

© www.soinside.com 2019 - 2024. All rights reserved.