Igor  Lozovsky
Igor Lozovsky

Reputation: 2415

Why operator 'as' is slower then 'is'?

I have next class:

public class A
{
    public int MyProperty { get; set; }
}

And the following code in Main:

object obj = new A();

Stopwatch sw = Stopwatch.StartNew();
var res = obj as A;
if (res != null)
{
    res.MyProperty = 10;
    Console.WriteLine("obj is A (as)" + sw.Elapsed);
}
sw.Stop();

Stopwatch sw2 = Stopwatch.StartNew();
if (obj.GetType() == typeof(A))
{
    A a = (A)obj;
    a.MyProperty = 10;
    Console.WriteLine("obj is A (GetType)" + sw2.Elapsed);
}
sw2.Stop();

Stopwatch sw3 = Stopwatch.StartNew();
var isA = obj is A;
if (isA)
{
    A a = (A)obj;
    a.MyProperty = 19;
    Console.WriteLine("obj is A (is)" + sw3.Elapsed);
}
sw3.Stop();

Console.ReadKey();

The results are:

obj is A (as) 00:00:00.0000589
obj is A (GetType) 00:00:00.0000024
obj is A (is) 00:00:00.0000006

The point is that operator 'is' works always faster than 'as'. Why 'as' is slower than 'is'? Even GetType() faster than 'as'. What stands for 'as' operator that cause such delays comparing to 'is' and GetType().

Upvotes: 0

Views: 129

Answers (3)

Matthew Watson
Matthew Watson

Reputation: 109577

Your measurements are incorrect if they show that "as" is slower than "is".

The reason that can't be the case is that both the "as" and the "is" keywords generate an isinst IL instruction, but the "is" instruction generates an additional check of the return value.

You do not need to perform any timings to determine this, because you can examine the generated IL code using reflector.

For example, this method:

static bool isTest(object value)
{
    return value is Random;
}

Generates this IL:

.method private hidebysig static bool isTest(object 'value') cil managed
{
    .maxstack 8
    L_0000: ldarg.0 
    L_0001: isinst [mscorlib]System.Random
    L_0006: ldnull 
    L_0007: cgt.un 
    L_0009: ret 
}

While this:

static object asTest(object value)
{
    return value as Random;
}

Generates:

.method private hidebysig static object asTest(object 'value') cil managed
{
    .maxstack 8
    L_0000: ldarg.0 
    L_0001: isinst [mscorlib]System.Random
    L_0006: ret 
}

The "is" keyword generates identical IL to the "as" keyword plus some additional instructions; therefore, "is" must be slower than "as" for this kind of usage.

Upvotes: 2

Paolo Tedesco
Paolo Tedesco

Reputation: 57202

I think it could be due to the fact that you are also setting MyProperty, so the first time you do that the property setter is JIT-compiled.

Try running this code, with and without the commented lines, and check the difference:

    object obj = new A();

    // uncomment these lines and see the difference
    // A tmp = new A();
    // tmp.MyProperty = 100;

    Stopwatch sw = Stopwatch.StartNew();
    var res = obj as A;
    if (res != null) {
        res.MyProperty = 10;
    }
    sw.Stop();
    Console.WriteLine("as : " + sw.Elapsed);

    Stopwatch sw2 = Stopwatch.StartNew();
    if (obj.GetType() == typeof(A)) {
        A a = (A)obj;
        a.MyProperty = 10;
    }
    sw2.Stop();
    Console.WriteLine("GetType : " + sw2.Elapsed);

    Stopwatch sw3 = Stopwatch.StartNew();
    var isA = obj is A;
    if (isA) {
        A a = (A)obj;
        a.MyProperty = 19;
    }
    sw3.Stop();
    Console.WriteLine("is : " + sw3.Elapsed);

Upvotes: 1

Guffa
Guffa

Reputation: 700362

I would guess that it's the first Console.Write that has to open a stream to the console, so that takes a lot more time.

Anyway, writing to the console takes so much more time than doing a cast, so you can't draw any conclusion at all about the castings from your test.

Doing the casts a billion times and don't write anything to the console for each cast, gives you a more reasonable result:

object obj = new A();
int iterations = 1000000000;

Stopwatch sw = Stopwatch.StartNew();
for (int i = 0; i < iterations; i++) {
  var res = obj as A;
  if (res != null) {
    res.MyProperty = 10;
  }
}
sw.Stop();
Console.WriteLine("obj is A (as)" + sw.Elapsed);

Stopwatch sw2 = Stopwatch.StartNew();
for (int i = 0; i < iterations; i++) {
  if (obj.GetType() == typeof(A)) {
    A a = (A)obj;
    a.MyProperty = 10;
  }
}
sw2.Stop();
Console.WriteLine("obj is A (GetType)" + sw2.Elapsed);

Stopwatch sw3 = Stopwatch.StartNew();
for (int i = 0; i < iterations; i++) {
  var isA = obj is A;
  if (isA) {
    A a = (A)obj;
    a.MyProperty = 19;
  }
}
sw3.Stop();
Console.WriteLine("obj is A (is)" + sw3.Elapsed);

Example output:

obj is A (as)00:00:00.3937249
obj is A (GetType)00:00:00.3452988
obj is A (is)00:00:01.0193541

Upvotes: 4

Related Questions