Я пытаюсь написать сетевую библиотеку высокого уровня (в основном для удовольствия), поскольку пользователь может легко определить свои пакеты, выведя класс. Таким образом, синтаксический анализ сообщений выполняется легко. Пакет пользователя должен содержать только базовое значение types.Внедрение родовых геттеров и сеттеров для повышения производительности, чем отражение
Для этого мне нужно получить доступ к каждому полю каждого пользовательского пакета. Эту проблему можно легко сделать с помощью рефлексии, но поскольку рефлексия медленна, я не могу ее использовать. Чтобы сделать это быстро, я сделал класс, чтобы вводить геттеры и сеттеры для каждого пользовательского поля пакета во время выполнения (нашел это где-то в StackOverflow). Геттеры - Func<UserDefinedPacket, fieldType>
, а сеттеры - Action<UserDefinedPAcket, setValue>
.
В этой статье:, потому что пользовательские пакеты определены в другой сборке. Я не могу знать тип получателей или сеттеров во время компиляции. Самый производный класс, который я могу использовать для геттеров и сеттеров, - Delegate
. Это означает, что я могу использовать DynamicInvoke, поэтому снова получаю отражение ... но поскольку я знаю, что методы являются действительными Funcs и Actions, но не могут их отличать, я могу использовать динамический тип и вызывать Invoke. Динамический тип улучшил производительность примерно в 5 раз, но настройка и получение значения по-прежнему медленнее по сравнению с обычным полевым доступом (что примерно в 100 раз быстрее), что делает эту библиотеку непригодной.
В конечном счете, я хочу, чтобы иметь возможность преобразовать определенный пользователем класс в массив байтов. Также я не хочу сериализовать класс, потому что сериализация включает в себя некоторые нежелательные данные, которые я действительно не хочу отправлять по сети, и сериализация выглядит довольно медленной.
Вот вопрос: Могу ли я на самом деле сделать методы GetVaue и SetValue быстрее? Я попробовал лить делегатов на нужные функции/действия (переместил пользовательский класс пакета в библиотеку, чтобы это было плохо), а сеттеры заняли около 50 мс вместо 300 мс. Я надеялся получить эту производительность для общих пакетов.
namespace MirrorNet
{
// Base class for packets
// This will be derived by users and should only contain basic type fields
public class UserPacket
{
}
public class MirrorNetManager
{
private static MirrorNetManager instance = new MirrorNetManager();
public static MirrorNetManager Instance
{
get { return instance; }
}
// Dictionary: packetType -> field -> getter | setter
private class PropertyDictionary : Dictionary<Type, Dictionary<FieldInfo, Delegate>>
{
}
private Dictionary<int, PacketConstructor> m_packetConstructors = new Dictionary<int, PacketConstructor>();
private PropertyDictionary m_packetFieldGetters = new PropertyDictionary();
private PropertyDictionary m_packetFieldSetters = new PropertyDictionary();
public void SetValue(UserPacket packet, FieldInfo field, object value)
{
var setDelegate = m_packetFieldSetters[packet.GetType()][field];
dynamic setAction = setDelegate; //Convert.ChangeType(setDelegate, setDelegate.GetType());
dynamic setObject = packet; //Convert.ChangeType(packet, packet.GetType());
dynamic setValue = value; //Convert.ChangeType(value, value.GetType());
setAction.Invoke(setObject, setValue);
//setDelegate.DynamicInvoke(packet, value);
}
public object GetValue(UserPacket packet, FieldInfo field)
{
var getDelegate = m_packetFieldGetters[packet.GetType()][field];
dynamic getFunction = getDelegate; //Convert.ChangeType(getDelegate, getDelegate.GetType());
dynamic getObject = packet; //Convert.ChangeType(packet, packet.GetType());
return getFunction.Invoke(getObject);
//return getDelegate.DynamicInvoke(packet);
}
public void InitializePackets(Assembly packetsAssembly)
{
var typesArray = packetsAssembly.GetTypes();
foreach (Type type in typesArray)
{
if (type.BaseType == typeof(UserPacket))
{
InsertPacketConstructor(type);
InsertSettersAndGetters(type);
}
}
}
private void InsertPacketConstructor(Type packetType)
{
foreach (var member in packetType.GetFields())
{
Console.WriteLine(member);
// TODO: Implement
}
}
private void InsertSettersAndGetters(Type type)
{
Dictionary<FieldInfo, Delegate> getters = new Dictionary<FieldInfo, Delegate>();
Dictionary<FieldInfo, Delegate> setters = new Dictionary<FieldInfo, Delegate>();
foreach (FieldInfo field in type.GetFields())
{
Delegate getDelegate = CreateGetter(type, field.FieldType, field);
Delegate setDelegate = CreateSetter(type, field.FieldType, field);
getters.Add(field, getDelegate);
setters.Add(field, setDelegate);
}
m_packetFieldGetters.Add(type, getters);
m_packetFieldSetters.Add(type, setters);
}
private Delegate CreateGetter(Type classType, Type getReturnType, FieldInfo field)
{
string methodName = field.ReflectedType.FullName + ".get_" + field.Name;
Type[] parameterTypes = new Type[1] { classType };
DynamicMethod getterMethod = new DynamicMethod(methodName, getReturnType, parameterTypes, true);
ILGenerator gen = getterMethod.GetILGenerator();
if (field.IsStatic)
{
gen.Emit(OpCodes.Ldsfld, field);
}
else
{
gen.Emit(OpCodes.Ldarg_0);
gen.Emit(OpCodes.Ldfld, field);
}
gen.Emit(OpCodes.Ret);
// Create the specific Func<,> instance
Type[] typeArgs = new Type[] { classType, getReturnType };
Type generic = typeof(Func<,>);
Type genInstance = generic.MakeGenericType(typeArgs);
Delegate getterDelegate = getterMethod.CreateDelegate(genInstance);
return getterDelegate;
}
private Delegate CreateSetter(Type classType, Type setValueType, FieldInfo field)
{
string methodName = field.ReflectedType.FullName + ".set_" + field.Name;
Type[] parameters = new Type[2]
{
classType,
setValueType
};
DynamicMethod setterMethod = new DynamicMethod(methodName, null, parameters);
ILGenerator gen = setterMethod.GetILGenerator();
if (field.IsStatic)
{
gen.Emit(OpCodes.Ldarg_1);
gen.Emit(OpCodes.Stsfld, field);
}
else
{
gen.Emit(OpCodes.Ldarg_0);
gen.Emit(OpCodes.Ldarg_1);
gen.Emit(OpCodes.Stfld, field);
}
gen.Emit(OpCodes.Ret);
// Create the specific Action<,> instance
Type[] typeArgs = new Type[] { classType, setValueType };
Type generic = typeof(Action<,>);
Type genInstance = generic.MakeGenericType(typeArgs);
Delegate ret = setterMethod.CreateDelegate(genInstance);
return ret;
}
}
}
// THIS IS IN A DIFERENT ASSEMBLY
namespace MirrorNetTesting
{
// This is just an example packet
public class StudentPacket : UserPacket
{
public int age;
public int height;
public double grades;
public string firstName;
public string lastName;
}
class Program
{
static void Main(string[] args)
{
Assembly asm = Assembly.GetAssembly(typeof(StudentPacket));
MirrorNetManager.Instance.InitializePackets(asm);
PerformanceTesting();
Console.ReadLine();
}
public static void PerformanceTesting()
{
int studentsCount = 1000 * 100;
StudentPacket[] studentsArray = new StudentPacket[studentsCount];
//////////////////////////////////////////////////////////////////////////
Random rnd = new Random();
for (int i = 0; i < studentsArray.Length; i++)
{
StudentPacket student = new StudentPacket();
student.age = rnd.Next();
student.height = rnd.Next();
student.grades = rnd.NextDouble();
student.firstName = "First " + rnd.Next().ToString();
student.lastName = "Last " + rnd.Next().ToString();
studentsArray[i] = student;
}
var fieldsArray = typeof(StudentPacket).GetFields().ToArray();
//////////////////////////////////////////////////////////////////////////
// Begin normal getter test
Console.WriteLine("Testing normal getters");
Stopwatch normalGetterSw = new Stopwatch();
normalGetterSw.Start();
foreach (var student in studentsArray)
{
//object getValue;
var getAge = student.age;
var getHeight = student.height;
var getGrades = student.grades;
var getFirstName = student.firstName;
var getLastName = student.lastName;
}
normalGetterSw.Stop();
//////////////////////////////////////////////////////////////////////////
// Begin reflection getter test
Console.WriteLine("Testing reflection getters");
Stopwatch reflectionGetterSw = new Stopwatch();
reflectionGetterSw.Start();
foreach (var student in studentsArray)
{
object getValue;
for (int i = 0; i < fieldsArray.Length; i++)
{
FieldInfo field = fieldsArray[i];
getValue = MirrorNetManager.Instance.GetValue(student, field);
}
}
reflectionGetterSw.Stop();
//////////////////////////////////////////////////////////////////////////
// Begin normal setter test
Console.WriteLine("Testing normal setters");
Stopwatch normalSetterSw = new Stopwatch();
int age = 10;
int height = 12;
double grades = 1432.523d;
string firstName = "first name";
string lastName = "last name";
normalSetterSw.Start();
foreach (var student in studentsArray)
{
student.age = age;
student.height = height;
student.grades = grades;
student.firstName = firstName;
student.lastName = lastName;
}
normalSetterSw.Stop();
//////////////////////////////////////////////////////////////////////////
// Begin reflection setter test
Console.WriteLine("Testing reflection setters ");
Stopwatch reflectionSetterSw = new Stopwatch();
object[] setValues = new object[]
{
age,
height,
grades,
firstName,
lastName
};
reflectionSetterSw.Start();
foreach (var student in studentsArray)
{
for (int i = 0; i < fieldsArray.Length; i++)
{
FieldInfo field = fieldsArray[i];
MirrorNetManager.Instance.SetValue(student, field, setValues[i]);
}
}
reflectionSetterSw.Stop();
//////////////////////////////////////////////////////////////////////////
Console.WriteLine("Normal getter: \t {0}", normalGetterSw.ElapsedMilliseconds);
Console.WriteLine("Normal setter: \t {0}", normalSetterSw.ElapsedMilliseconds);
Console.WriteLine("Reflection getter: \t {0}", reflectionGetterSw.ElapsedMilliseconds);
Console.WriteLine("Reflection setter: \t {0}", reflectionSetterSw.ElapsedMilliseconds);
//////////////////////////////////////////////////////////////////////////
}
}
}
Output (соответствующие вещи только):
Normal getter: 3
Normal setter: 4
Reflection getter: 261
Reflection setter: 183
Отражение геттер и сеттер на самом деле динамические вызовы, и они, как правило, в конечном итоге принимает 300 мс.
Также, поскольку код довольно длинный, я отправил его также here.
Спасибо. Я был сконцентрирован на том, чтобы сделать этот код лучше, я фактически забыл о его цели. – dburner