Explain this strange behavior with IEnumerable / yield

Guess ... how long will this program take to create the very first output when I == 0? It should be instant, right? And through a lazy yield estimate after that, he should produce the products in quick succession, right?

 static void Main(string[] args) { Stopwatch stopwatch = Stopwatch.StartNew(); int i = 0; foreach (var item in massiveYieldStatement()) { if (i++ % 10000 == 0) Console.WriteLine(stopwatch.ElapsedMilliseconds / 1000); } Console.ReadKey(); } static IEnumerable<string> massiveYieldStatement() { yield return "a"; yield return "a"; .. repeat 200,000 times !! yield return "a"; } 

But this is not so! He sits there without exiting for 4 to 21 minutes, and then quickly ends - up to 60 ms in one case! During these minutes, 100% of one main CPU is used, and memory usage is increased. In the actual scenario where I came across this, a Stackoverflow exception is Stackoverflow before the first iteration happens! I tried it in debug mode in Visual Studio and in release mode from the command line. I tried this on Windows 7 x64 and Windows Server 2008 R2 x64.

Can anyone explain what is going on here? Does it play for you?

NOTE. This is not real code: real code has much less profitability operators, but much more complicated. This is simply the easiest option.

+4
source share
2 answers

The assembly created by this code is several megabytes. yield return is a special beast because it looks deceptively simple, but the C # compiler actually generates a class ("state machine") to implement the massive level method. I am sure that you are expecting a JIT compiler to compile the MoveNext () method of this class (you can verify that with ildasm: if you try to open the MoveNext () method, it takes a lot of time).

+4
source

the problem is not the output, but the function returning 200K profitability (about 100K lines have already slowed down my VS). It needs to be evaluated and a new state class generated each time you make your first MoveNext() on the IEnumerator returned from IEnumerable<string>.GetEnumerator .

 static IEnumerable<string> massiveYieldStatement() { for(int i = 0; i < 200000; ++i) yield return "a"; } 

It is fast as expected, as the assessment is fast.

+2
source

Source: https://habr.com/ru/post/1382842/


All Articles