Я сделал простой тест, где я заменил проблематичную .GetValue
с функцией исполняющей упрощенного ассигнования («если имя свойства является блабло, значение Object.blabla»). Тест состоит только из простой версии ваших функций/переменных/свойств и цикла, позволяющего полностью контролировать количество итераций. Результаты, безусловно, удивительно: новый подход в 10 раз быстрее! Имейте в виду, что в моих оригинальных тестах (50000 итераций) время было 2276 (старый) против 234 (новый). Эта разница остается постоянной для разных сценариев; например, для 8000 итераций, он обеспечивает 358 мс против 36 мс. Я провел эти тесты на довольно мощном компьютере и на winforms C#; @Xaisoft может взять код ниже, выполнить тест в соответствии с его конкретными условиями и сообщить результаты.
Код:
private void Form1_Load(object sender, EventArgs e)
{
List<List> var = new List<List>();
List var1 = new List();
var1.var = 1;
var1.var2 = 1;
var1.var3 = 1;
var1.var4 = 1;
var1.var5 = 1;
List var2 = new List();
var2.var = 1;
var2.var2 = 1;
var2.var3 = 1;
var2.var4 = 1;
var2.var5 = 1;
List var3 = new List();
var3.var = 1;
var3.var2 = 1;
var3.var3 = 1;
var3.var4 = 1;
var3.var5 = 1;
List var4 = new List();
var4.var = 1;
var4.var2 = 1;
var4.var3 = 1;
var4.var4 = 1;
var4.var5 = 1;
var.Add(var1);
var.Add(var2);
var.Add(var3);
var.Add(var4);
InitializeData(var, typeof(List).GetProperties());
}
private static void InitializeData(List<List> objects, PropertyInfo[] props)
{
DateTime start = DateTime.Now;
int count = 0;
do
{
count = count + 1;
foreach (var item in objects)
{
foreach (var p in props)
{
object returnData = p.GetValue(item, null); //returnProps(p.Name, item);
}
}
} while (count < 50000);
TimeSpan timer = new TimeSpan();
timer = DateTime.Now.Subtract(start);
}
private class List
{
public int var { set; get; }
public int var2 { set; get; }
public int var3 { set; get; }
public int var4 { set; get; }
public int var5 { set; get; }
public int var6 { set; get; }
public int var7 { set; get; }
public int var8 { set; get; }
public int var9 { set; get; }
public int var10 { set; get; }
public int var11 { set; get; }
public int var12 { set; get; }
public int var13 { set; get; }
public int var14 { set; get; }
}
private static object returnProps(string propName, List curObject)
{
if (propName == "var")
{
return curObject.var;
}
else if (propName == "var2")
{
return curObject.var2;
}
else if (propName == "var3")
{
return curObject.var3;
}
else if (propName == "var4")
{
return curObject.var4;
}
else if (propName == "var5")
{
return curObject.var5;
}
else if (propName == "var6")
{
return curObject.var6;
}
else if (propName == "var7")
{
return curObject.var7;
}
else if (propName == "var8")
{
return curObject.var8;
}
else if (propName == "var9")
{
return curObject.var9;
}
else if (propName == "var10")
{
return curObject.var10;
}
else if (propName == "var11")
{
return curObject.var11;
}
else if (propName == "var12")
{
return curObject.var12;
}
else if (propName == "var13")
{
return curObject.var13;
}
else if (propName == "var14")
{
return curObject.var14;
}
return new object();
}
FINAL Примечание: Я хотел бы, чтобы люди поняли так впечатляющие результаты в более общем, чем только применительно к .GetValue
. В наши дни компьютеры могут справляться с множеством вещей, и вам не нужно максимизировать производительность каждого отдельного бита, это правда. С другой стороны, если у вас проблемы с производительностью, и вам нужно «экономить ресурсы» более соответствующим образом, вы должны сосредоточить свои улучшения на идее «чем проще, тем быстрее». Я сделал себе улучшение производительности в кодах с использованием соответствующего количества Lists
и Dictionaries
, и результаты можно получить, даже после каждого отдельного изменения (List
в обычный Array
). Вам не нужно быть слишком пассивным на этом фронте, но в случае необходимости помните, что требования к потреблению памяти/связанного времени с List
по отношению к Array
выше (и оба элемента в основном одинаковы). То же самое для многомерных массивов, долго размера массивов и т.д.
------ ПОДРОБНЕЕ АНАЛИЗ ИСПОЛНЕНИЯ
Даже если я пусть моя точка очень ясно с самого начала (только идея, которая должен быть адаптирован к каждой ситуации), я понимаю, что мое требование (в 10 раз быстрее) действительно требует правильного определения. Я делаю тесты в разных условиях, и вот результаты:
ПРИМЕЧАНИЕ: вышеупомянутые результаты были выведены 32-битным исполняемым файлом; все перечисленные ниже - из 64-битного. Я наблюдал улучшение производительности .GetValue
при переходе от 32-битного к 64-битовому. Обновление 64-разрядная версия результатов выше (мс):
GetValue Direct Assignation
50000 iterations -> 1197 157
80000 iterations -> 1922 253
100000 iterations -> 2354 310
Таким образом, коэффициент изменяется от 10 раз до 7,5 раз.
Я начал увеличивать количество свойств (каждый раз на 64-битных) и GetValue
стал лучше и лучше.Результаты:
28 Properties
GetValue Direct Assignation
50000 iterations -> 2386 552
80000 iterations -> 3857 872
Aver. ratio = 4.37
50 Properties
GetValue Direct Assignation
50000 iterations -> 4292 1707
80000 iterations -> 6772 2711
Aver. ratio = 2.475
Я не уверен, если улучшение GetValue
продолжится и достигнет точки, где будет лучше, чем упрощенный подход, но кого это волнует? На этом этапе ясно, что все большее число свойств играет против упрощенного подхода, поэтому пришло время попробовать другую (опять же довольно упрощенную) альтернативу: глобальный массив, сохраняющий все свойства.
private static int[,] List0;
Будучи заселен параллельно с заданным свойством (то есть, когда object.propX = any value
соответствующих позиций в массиве также заполняется) и отнесено объекты/позиций свойств (первый объект, третья собственности, и т.д.). Логически это ограничивает количество объектов (рост первого измерения выше 1000 не рекомендуется), но вы можете полагаться на разные массивы (один хранится от первого объекта до 1000-го, другого от 1001-го до 2000-го , и т.д.); вы можете установить функцию, которая принимает в качестве аргумента имя объекта и возвращает соответствующий массив.
Изменения в главном цикле:
int countObject = -1;
foreach (var item in objects)
{
countObject = countObject + 1;
int countProp = -1;
foreach (var p in props)
{
countProp = countProp + 1;
object returnData = List0[countObject, countProp];
}
}
, запустив этот новый подход в случае, описанном выше, я получаю:
50 Properties
GetValue 2D Array
80000 iterations -> 6772 155
Aver. ratio = 45.146
еще один:
70 Properties
GetValue 2D Array
80000 iterations -> 10444 213
Aver. ratio = 49.06
И я остановился мои тесты здесь. Думаю, этого достаточно, чтобы доказать свою точку зрения.
Различные подходы обеспечивают различные характеристики в разных условиях и, таким образом, лучший способ узнать идеальную конфигурацию для конкретной ситуации - это проверить ее. Опираясь на предельную истину, редко является лучшим решением проблемы (хотя я могу ошибаться ... все еще жду ответа от Дмитрия, чтобы проверить его решение в разных условиях). Таким образом, в соответствии с проверенными условиями, кажется, что исходный упрощенный подход приемлем для случаев, когда количество свойств относительно невелико (т. Е. Ниже 20); выше этого, требуемое усилие hardcoding не кажется достойным, и полагаться на другую альтернативу (например, 2D-массив, который я предложил) лучше. В любом случае GetValue
обеспечивает четкую производительность, которая может быть улучшена различными способами.
Я надеюсь, что мне не нужно будет обновлять этот ответ еще раз :)
Так что вы хотите, чтобы 'PropertyInfo.GetValue()' ускорялся? –
Как долго длится эквивалентная версия без вызовов отражения? Это основные накладные расходы «GetValue», но мне интересно, сколько накладных расходов он добавляет по сравнению с прямой ссылкой на свойства. (1000 нс нетривиальны, но не огромны) – Guvante
Известны ли типы объектов во время компиляции? Можете ли вы написать безопасные значения getters/formatters для каждого известного типа, и все, что вам нужно сделать, - это поиск типа/отбрасывание на каждый 'элемент', который будет создавать KeyValuePairs для вас и вообще избежать отражения? EDIT: Или еще лучше, чтобы ваши объекты реализовали интерфейс с помощью одного метода, задачей которого является возвращение набора KeyValuePairs; избегайте полного поиска/отливки. –