在IEnumerable中跳过第一个和最后一个,推迟执行

时间:2016-03-04 14:14:19

标签: c#

我有这个巨大的json文件,从字符" [\ r \ n"并以"]"结尾。我有这段代码:

foreach (var line in File.ReadLines(@"d:\wikipedia\wikipedia.json").Skip(1))
{
  if (line[0] == ']') break;
  // Do stuff
}

我想知道,如果我将上述代码与我所拥有的代码进行比较,哪种机器代码在消耗多少时钟周期和内存方面是最优的,哪种机器代码最佳?取代"休息"使用"继续",或者这两段代码是否会编译为相同的MSIL和机器代码?如果您知道答案,请详细说明您的结论?我真的很想知道。

编辑:在你将它关闭为荒谬之前,请考虑这段代码与上面的代码相同,并认为c#编译器在代码路径是平的时候会优化并且不会在很多方面进行分叉,会是以下所有示例为CPU生成相同的工作量?

IEnumerable<char> text = new[] {'[', 'a', 'b', 'c', ']'};
foreach (var c in text.Skip(1))
{
    if (c == ']') break;
    // Do stuff
}
foreach (var c in text.Skip(1))
{
    if (c == ']') continue;
    // Do stuff
}
foreach (var c in text.Skip(1))
{
    if (c != ']')
    {
        // Do stuff                    
    }
}
foreach (var c in text.Skip(1))
{
    if (c != ']')
    {
        // Do stuff                    
    }
}
foreach (var c in text.Skip(1))
{
    if (c != ']')
    {
        // Do stuff                    
    }
    else
    {
        break;
    }
}

EDIT2:这是另一种方式:它是跳过IEnumerable中第一个和最后一个项目的最漂亮的方式,同时仍然推迟执行直到// Do stuff?

1 个答案:

答案 0 :(得分:3)

问:不同的MSIL用于中断或继续循环?

是的,那是因为它的工作原理如下:

foreach (var item in foo)
{
    // more code...

    if (...) { continue; } // jump to #1
    if (...) { break; } // jump to #2

    // more code...

    // #1 -- just before the '}'
}

// #2 -- after the exit of the loop.

问:什么会给你最大的表现?

分支是编译器的分支。如果您有gotocontinuebreak,它最终会被编译为分支(操作码br),这将进行分析。换句话说:它没有什么区别。

所带来的好处是在代码中拥有可预测的数据和代码流模式。分支会破坏代码流,所以如果你想要性能,你应该避免不规则的分支。

换句话说,更喜欢:

for (int i=0; i<10 && someCondition; ++i)

为:

for (int i=0; i<10; ++i) 
{
    // some code
    if (someCondition) { ... } 
    // some code
}

与性能一样,最好的办法是运行基准测试。没有代理人。

问:什么会给你最大的表现? (#2)

你在使用IEnumerable做了很多。如果您想要原始效果并有选项,最好使用arraystring。对于元素的顺序访问,原始性能没有更好的选择。

如果数组不是一个选项(例如,因为它与访问模式不匹配),最好使用最适合访问模式的数据结构。了解哈希表(字典),红黑树(SortedDictionary)的特征以及List的工作原理。了解如何真正起作用的东西是您需要的。如果不确定,再次测试,测试和测试。

问:什么会给你最大的表现? (#3)

如果你打算解析它,我也会尝试使用JSON库。这些人可能已经为你发明了轮子 - 如果没有,它会给你一个“击败”的基线。

问:[...]跳过第一个和最后一个项目的最漂亮的方法是什么[...]

如果基础数据结构是stringListarray,我只需执行此操作:

for (int i=1; i<str.Length-1; ++i)
{ ... }

坦率地说,其他数据结构在IMO中并没有多大意义。也就是说,人们喜欢将Linq代码放在任何地方,所以......

使用枚举器

您可以轻松创建一个返回除第一个和最后一个元素之外的所有元素的方法。在我的书中,总是通过foreach之类的东西在代码中访问枚举器,以确保正确调用IDisposable。

public static IEnumerable<T> GetAllButFirstAndLast<T>(IEnumerable<T> myEnum)
{
    T jtem = default(T);
    bool first = true;
    foreach (T item in myEnum.Skip(1)) 
    { 
        if (first) { first = false; } else { yield return jtem; }  
        jtem = item;
    }
}

请注意,这与“从代码中获得最佳性能”几乎没有关系。一看IL就会告诉你所有你需要知道的事情。