Я наткнулся на код операции castclass
, который определен как Standard ECMA - 335, III.4.3
.Я написал несколько примеров использования callvirt
кода операции с кастингом и без.Оказывается, что castclass
код операции имеет большое влияние на производительность.
Для тестирования я использовал следующую «грубую» (с точки зрения неточности времени выполнения методов) программу (скомпилированную в Release mode
с помощью msvc 2015
):
public class Program
{
public interface IShow { string show(); }
public class ObjectWithShow : IShow
{
public string show() => "Hello, that's the show method.";
}
// Such functions remains unchanged
public static string showWithCast(object o) => ((IShow)o).show();
public static string show(IShow o) => o.show();
// Such function will be patched later
public static string showWithoutCast(object o) => ((IShow)o).show();
public static void Main()
{
int N = 10000000;
var show_object = new ObjectWithShow();
{
var watch = System.Diagnostics.Stopwatch.StartNew();
for (int i = 0; i < N; ++i)
{
showWithCast(show_object);
}
watch.Stop();
Console.WriteLine($"With cast {watch.ElapsedMilliseconds}");
}
{
var watch = System.Diagnostics.Stopwatch.StartNew();
for (int i = 0; i < N; ++i)
{
showWithoutCast(show_object);
}
watch.Stop();
Console.WriteLine($"Without cast {watch.ElapsedMilliseconds}");
}
{
var watch = System.Diagnostics.Stopwatch.StartNew();
for (int i = 0; i < N; ++i)
{
show(show_object);
}
watch.Stop();
Console.WriteLine($"Without cast {watch.ElapsedMilliseconds}");
}
}
}
ЗдесьIL
коды show/showWitCast
функций:
.method public hidebysig static string show (class IShow o) cil managed
{
.maxstack 8
IL_0000: ldarg.0
IL_0001: callvirt instance string IShow::show()
IL_0006: ret
} // end of method Program::show
.method public hidebysig static string showWithCast (object o) cil managed
{
.maxstack 8
IL_0000: ldarg.0
IL_0001: castclass IShow
IL_0006: callvirt instance string IShow::show()
IL_000b: ret
} // end of method Program::showWithCast
Вот код для showWithoutCast
(ПРИМЕЧАНИЕ: я пропатчил его, удалив castclass IShow
в редакторе IL
. Исходная версия была такой же, как showWithCast
)
.method public hidebysig static string showWithoutCast (object o) cil managed
{
.maxstack 8
IL_0000: ldarg.0
IL_0001: callvirt instance string IShow::show()
IL_0006: ret
} // end of method Program::showWithoutCast
Результаты выполнения (i7-3370 CPU @ 3,40 ГГц, 8 ГБ ОЗУ) показывают следующий результат:
С приведением 46
Безприведение 24
без преобразования 23
Оказывается, что callvirt
для объекта без castclass
показывает почти такой же результат, как мы использовали вместо этого экземпляр интерфейса.Итак, какова цель castclass
?Я предполагаю, что c# compiler
испускает такой код, чтобы гарантировать, что код операции callvirt
не будет использоваться для некорректных типов (потому что он не может выполнять такие проверки во время компиляции).Итак, следующий вопрос - является ли это последовательным CIL
кодом, где я намеренно исключаю использование castclass
в местах, где я ГАРАНТИРУЮ, что метод будет использоваться только для типов, которые реализуют IShow
?
PS Конечно, вы можете спросить, может быть, тогда следует использовать метод show
?Но есть случаи, когда такую функцию нельзя использовать.Если быть кратким, я динамически генерирую код и хочу реализовать универсальный контейнер (он наследует IShow
), но его универсальный параметр может дополнительно реализовывать интерфейс IShow
.Если универсальный параметр не реализует интерфейс (например, это int
), тогда я гарантирую, что метод show
контейнера не будет использоваться.