.NET 2.0: Activator.CreateInstance(...) против нового: Почему скорость выполнения зависит от порядка?
Когда я создаю объект класса, который реализует интерфейс через Activator.CreateInstance
или через new
Ключевое слово, скорость выполнения явно отличается. Вид создания, который я использую первым, является более быстрым для остальной части выполнения программы. Я написал следующий "Тест производительности":
static private void SpeedTest(IPlugin plugin)
{
Console.Write("SpeedTest of \"" + plugin.Name + "\": ");
Stopwatch sw = new Stopwatch();
int workingNumber = 0;
sw.Start();
for (int i = 0; i < 2147483647; i++)
{
plugin.Add(ref workingNumber);
plugin.DSub(ref workingNumber);
plugin.Add(ref workingNumber);
}
sw.Stop();
Console.WriteLine(sw.Elapsed.ToString());
}
Я загружаю экземпляры классов, реализующих IPlugin через:
static void Main(string[] args)
{
IPlugin pluginInstance = null;
IPlugin localInstance = new LocalPlugin();
Assembly plugin = Assembly.LoadFile("Path\\Plugin.dll");
Type pluginInterface = typeof(IPlugin);
foreach (Type type in plugin.GetTypes())
if (pluginInterface.IsAssignableFrom(type))
pluginInstance = (IPlugin)Activator.CreateInstance(type);
SpeedTest(localInstance);
SpeedTest(pluginInstance);
SpeedTest(localInstance);
SpeedTest(pluginInstance);
}
IPlugin выглядит так:
public interface IPlugin
{
string Name { get; }
void Add(ref int number);
void DSub(ref int number);
}
"Локальный" плагин (и удаленный плагин из Plugin.dll, который является тем же, за исключением того, что он имеет public
-модификатор и возвращает "Remote" вместо "Local") выглядят так:
class LocalPlugin : IPlugin
{
public string Name
{
get { return "Local"; }
}
public void Add(ref int number)
{
number++;
}
public void DSub(ref int number)
{
number -= 2;
}
}
Здесь вы можете скачать оба проекта: ZIP-файл с примерами проектов.
Вам нужно скомпилировать PluginHost
-Проект до Plugin
-Проект.
Теперь программа-выход для этого тест-блока
SpeedTest(pluginInstance);
SpeedTest(localInstance);
SpeedTest(pluginInstance);
SpeedTest(localInstance);
является:
SpeedTest of "Plugin": 00:00:25.9785649
SpeedTest of "Local": 00:00:38.8875138
SpeedTest of "Plugin": 00:00:25.8757588
SpeedTest of "Local": 00:00:38.5222134
Если я переместить первую строку в последнюю позицию:
SpeedTest(localInstance);
SpeedTest(pluginInstance);
SpeedTest(localInstance);
SpeedTest(pluginInstance);
Я получаю следующий вывод:
SpeedTest of "Local": 00:00:26.1881051
SpeedTest of "Plugin": 00:00:38.9942815
SpeedTest of "Local": 00:00:25.9634257
SpeedTest of "Plugin": 00:00:38.6881451
Вывод: первый экземпляр, который я использую, быстрее второго. Это поведение не зависит от метода.
Вопросы:
- Почему это так?
- Что я могу сделать, чтобы получить одинаковую производительность для каждого вида исполнения?
С уважением,
Матиас
1 ответ
Похоронен в следующей статье является причиной:
Любопытная тонкость о том, как CLR выполняет диспетчеризацию интерфейса для типов массивов
Конкретный интересный текст следующий:
Наша логика диспетчеризации интерфейса имеет важную оптимизацию, где FOR EACH CALL SITE, она явно проверяет одну конкретную цель, и, если она терпит неудачу, выполняет медленный поиск в хеш-таблице, и, если этот поиск завершается неудачей, возвращается к дорогостоящему поиску. Таким образом, для сайтов вызовов, которые имеют тенденцию переходить в одно место назначения, это очень быстро, а для сайтов вызовов, которые имеют много целей, вы получаете хорошую, но не столь высокую производительность.
Оптимизация сайта вызова инициализируется в соответствии с конкретным типом первого появившегося объекта.
Вы должны быть в состоянии произвести справедливое сравнение, заменив IPlugin
интерфейс в вашем тесте с PluginBase
учебный класс.