Boo

Use ValueType Iterators

Details

  • Type: Improvement Improvement
  • Status: Resolved Resolved
  • Priority: Major Major
  • Resolution: Fixed
  • Affects Version/s: 0.8.1
  • Fix Version/s: 0.9.2
  • Component/s: Compiler
  • Labels:
    None
  • Number of attachments :
    0

Description

Compare the il code for this boo code:

import System.Collections.Generic

x = List[of int] ()
x.Add(1)
x.Add(2)

for i in x:
print i

and this C# code:

using System.Collections.Generic;

class X {
static void Main() {
List<int> x = new List<int>();
x.Add(1);
x.Add(2);

foreach (int i in x) { System.Console.WriteLine(i); }
}}

IL_0015: callvirt instance class [mscorlib]System.Collections.IEnumerator class [mscorlib]System.Collections.IEnumerable::GetEnumerator()
IL_001a: stloc.2
.try { // 0
IL_001b: br IL_0032

IL_0020: ldloc.2
IL_0021: callvirt instance object class [mscorlib]System.Collections.IEnumerator::get_Current()
IL_0026: call int32 class [Boo.Lang]Boo.Lang.Runtime.RuntimeServices::UnboxInt32(object)
IL_002b: stloc.1
IL_002c: ldloc.1
IL_002d: call void class [mscorlib]System.Console::WriteLine(int32)
IL_0032: ldloc.2
IL_0033: callvirt instance bool class [mscorlib]System.Collections.IEnumerator::MoveNext()
IL_0038: brtrue IL_0020

IL_003d: leave IL_005c

IL_0015: callvirt instance valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<!0> class [mscorlib]System.Collections.Generic.List`1<int32>::GetEnumerator()
IL_001a: stloc.2
.try { // 0 IL_001b: br IL_002e IL_0020: ldloca.s 2 IL_0022: call instance !0 valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<int32>::get_Current() IL_0027: stloc.1 IL_0028: ldloc.1 IL_0029: call void class [mscorlib]System.Console::WriteLine(int32) IL_002e: ldloca.s 2 IL_0030: call instance bool valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<int32>::MoveNext() IL_0035: brtrue IL_0020 IL_003a: leave IL_0047 } // end .try 0

The C# version uses the value type iterator which is much faster

Issue Links

Activity

Hide
Daniel Grunwald added a comment -

Remember that there are three possible GetEnumerator methods:
1) There's List<T>.IEnumerable.GetEnumerator(). This causes the enumerator object to be allocated on the heap, all enumerator method calls to be virtual, and causes boxing for every element in the list.

2) There's List<T>.IEnumerable<T>.GetEnumerator(). This causes the enumerator object to be allocated on the heap, all enumerator method calls to be virtual, but elements don't have to be boxed.

3) There's List<T>.GetEnumerator(), with return type struct List<T>.Enumerator. The enumerator is a value type on the stack, the enumerator method calls are non-virtual, and elements don't have to be boxed. There is no heap allocation at all.

Boo currently uses 1, C# uses 3.

Show
Daniel Grunwald added a comment - Remember that there are three possible GetEnumerator methods: 1) There's List<T>.IEnumerable.GetEnumerator(). This causes the enumerator object to be allocated on the heap, all enumerator method calls to be virtual, and causes boxing for every element in the list. 2) There's List<T>.IEnumerable<T>.GetEnumerator(). This causes the enumerator object to be allocated on the heap, all enumerator method calls to be virtual, but elements don't have to be boxed. 3) There's List<T>.GetEnumerator(), with return type struct List<T>.Enumerator. The enumerator is a value type on the stack, the enumerator method calls are non-virtual, and elements don't have to be boxed. There is no heap allocation at all. Boo currently uses 1, C# uses 3.
Hide
Cedric Vivier added a comment -

This relates to BOO-779, though 779 seems to be about using method 2.

Show
Cedric Vivier added a comment - This relates to BOO-779, though 779 seems to be about using method 2.
Hide
Avishay Lavie added a comment -

How does the C# compiler know that List<T> exposes its own GetEnumerator() that should be used over its explicit implementations of IEnumerable<T> and IEnumerable?
If that's just a hard-coded optimization then I suggest we implement BOO-779 first (method 2, which is applicable to everything implementing IEnumerable<T>) and only later try to bring in custom handling for specific types.

Show
Avishay Lavie added a comment - How does the C# compiler know that List<T> exposes its own GetEnumerator() that should be used over its explicit implementations of IEnumerable<T> and IEnumerable? If that's just a hard-coded optimization then I suggest we implement BOO-779 first (method 2, which is applicable to everything implementing IEnumerable<T>) and only later try to bring in custom handling for specific types.
Hide
Daniel Grunwald added a comment -

C#'s foreach loop simply calls the method named GetEnumerator, it doesn't care about the IEnumerable interface at all. (foreach can be used to iterate any object exposing a GetEnumerator method, even if it doesn't implement IEnumerable).
Only if the class doesn't have a public GetEnumerator method, the C# compiler looks if it implements IEnumerable<T> or IEnumerable.

So basically the C# compiler tries to use option 3, if that's not possible it tries option 2, then option 1, then fails at compile time with CS1579 (foreach statement cannot operate on variables of type 'object' because 'object' does not contain a public definition for 'GetEnumerator').

Boo currently tries option 1, and if that's not possible, it calls Boo.Lang.Runtime.RuntimeServices.GetEnumerable, which tries to cast the object to IEnumerable, and also handles the implicit TextReader->IEnumerable conversion.

Show
Daniel Grunwald added a comment - C#'s foreach loop simply calls the method named GetEnumerator, it doesn't care about the IEnumerable interface at all. (foreach can be used to iterate any object exposing a GetEnumerator method, even if it doesn't implement IEnumerable). Only if the class doesn't have a public GetEnumerator method, the C# compiler looks if it implements IEnumerable<T> or IEnumerable. So basically the C# compiler tries to use option 3, if that's not possible it tries option 2, then option 1, then fails at compile time with CS1579 (foreach statement cannot operate on variables of type 'object' because 'object' does not contain a public definition for 'GetEnumerator'). Boo currently tries option 1, and if that's not possible, it calls Boo.Lang.Runtime.RuntimeServices.GetEnumerable, which tries to cast the object to IEnumerable, and also handles the implicit TextReader->IEnumerable conversion.
Hide
Cedric Vivier added a comment -

Landed in rev. 3331

Show
Cedric Vivier added a comment - Landed in rev. 3331

People

Vote (0)
Watch (0)

Dates

  • Created:
    Updated:
    Resolved: