C# 跳过IEnumerable中的第一个和最后一个,推迟执行

C# 跳过IEnumerable中的第一个和最后一个,推迟执行,c#,C#,我将这个巨大的json文件整齐地格式化,以字符“[\r\n”开头,以“]”结尾。我有一段代码: foreach (var line in File.ReadLines(@"d:\wikipedia\wikipedia.json").Skip(1)) { if (line[0] == ']') break; // Do stuff } 我想知道,在性能方面,什么是最好的,如果我将上面的代码与我用“continue”替换“break”的代码进行比较,那么在消耗多少时钟周期和内存方面,哪种机

我将这个巨大的json文件整齐地格式化,以字符“[\r\n”开头,以“]”结尾。我有一段代码:

foreach (var line in File.ReadLines(@"d:\wikipedia\wikipedia.json").Skip(1))
{
  if (line[0] == ']') break;
  // Do stuff
}
我想知道,在性能方面,什么是最好的,如果我将上面的代码与我用“continue”替换“break”的代码进行比较,那么在消耗多少时钟周期和内存方面,哪种机器代码是最理想的,或者这两段代码是否编译为相同的MSIL和机器代码?如果你知道答案,请确切解释你是如何得出结论的?我真的很想知道

编辑:在将此关闭为无意义之前,请考虑此代码等同于上面的代码,并考虑当代码路径平坦且不以多种方式分叉时C编译器优化,下面的所有示例都会为CPU生成相同数量的工作吗?p>

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;
    }
}
IEnumerable text=new[]{'[','a','b','c',']};
foreach(文本中的var c.Skip(1))
{
如果(c=']')中断;
//做事
}
foreach(文本中的var c.Skip(1))
{
如果(c=']')继续;
//做事
}
foreach(文本中的var c.Skip(1))
{
如果(c!=']')
{
//做事
}
}
foreach(文本中的var c.Skip(1))
{
如果(c!=']')
{
//做事
}
}
foreach(文本中的var c.Skip(1))
{
如果(c!=']')
{
//做事
}
其他的
{
打破
}
}

EDIT2:这里有另一种说法:跳过IEnumerable中的第一项和最后一项,同时仍将执行推迟到//执行任务时,最漂亮的方法是什么?

Q:在循环中中断或继续使用不同的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.
Q:什么能给你带来最好的表现?

分支是编译器的分支。如果您有一个
goto
、一个
continue
或一个
break
,它最终将被编译为一个分支(操作码
br
),并进行分析。换句话说:这没有什么区别

真正不同的是在代码中有可预测的数据和代码流模式。分支会中断代码流,所以如果您想要提高性能,应该避免不规则的分支

换句话说,您更喜欢:

for (int i=0; i<10 && someCondition; ++i)
坦率地说,在我看来,其他的数据结构并没有什么意义。也就是说,人们喜欢把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;
    }
}
公共静态IEnumerable GetAllButFirstAndLast(IEnumerable myEnum)
{
T jtem=默认值(T);
bool first=true;
foreach(myEnum.Skip(1)中的T项)
{ 
if(first){first=false;}else{yield return jtem;}
jtem=项目;
}
}

请注意,这与“从代码中获得最佳性能”没有多大关系。只要看一眼IL,你就会知道你需要知道的一切。

最好的选择是使用JSON解析器,而不是自己动手。是什么阻止你编译这两种代码和查看MSIL代码?它们应该编译为跳转指令
continue
跳到下一个循环迭代的开始,而
break
跳到循环的出口。比较两者之间的性能似乎毫无意义,因为它们适用于不同的操作…@Marcus-跳转本身,不。它只是内存中的某个位置。但是,由于继续迭代循环,您将做更多的工作。我发现很难给出有意义的答案,因为continue和break做不同的事情。显然,中断通常更便宜,因为它结束了循环。你能发布两段你想要比较的等价代码吗?对我来说,主要的收获是:在代码中有可预测的数据和代码流模式是有区别的。分支会破坏代码流,所以如果您想要性能,您应该避免不规则的分支。但是假设您必须在循环中多次执行某些操作,并且每次迭代都需要检查一个条件,例如“is count小于max size”,那么while循环的性能会优于if子句吗?我正在迭代67GB的json,数百万条记录。我想知道我没有不必要的分支。@Marcus可预测的模式,分支越少(通常是最短的代码),通常越好。我在这里写了一些关于编译器如何处理分支的内容:。请注意,while循环也是一个分支,就像从IL的角度看if一样。thx这是我关心的问题。无需降低代码的可读性,因为编译器会对其进行优化,这似乎是大多数情况下的规则,但需要注意不必要的分支(例如循环中的条件)
for (int i=1; i<str.Length-1; ++i)
{ ... }
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;
    }
}