- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我正在尝试编写一个高级网络库(主要是为了好玩),用户可以通过派生类轻松定义他们的数据包。这种解析消息的方式非常容易。用户数据包应该只包含基本值 types .
为此,我需要访问每个用户定义数据包的每个字段。这个问题可以很容易地通过反射来解决,但是由于反射非常慢,我不能使用它。为了快速做到这一点,我创建了一个类来在运行时为每个用户定义的数据包字段注入(inject) getter 和 setter(在 StackOverflow 的某处找到)。 setter/getter 是 Func<UserDefinedPacket, fieldType>
和 setter 是 Action<UserDefinedPAcket, setValue>
.
问题是:因为用户数据包是在另一个程序集中定义的,所以我不可能在编译时知道 getter 或 setter 的类型。我可以用于 getter 和 setter 的最派生类是 Delegate
.这意味着我只能使用 DynamicInvoke,所以我再次进行反射...但是因为我知道这些方法实际上是 Funcs 和 Actions 但实际上不能强制转换它们,所以我可以使用动态类型并调用 Invoke。动态类型将性能提高了大约 5 倍,但与普通字段访问(快大约 100 倍)相比,设置和获取值仍然很慢,这使得该库无法使用。
最终我希望能够将用户定义的类转换为字节数组。我也不想序列化这个类,因为序列化包含一些我不想通过网络发送的垃圾数据,而且序列化看起来很慢。
问题来了:我真的可以让 GetVaue 和 SetValue 方法更快吗?我尝试将委托(delegate)转换为所需的函数/操作(在库中移动了自定义数据包类,所以这不好)并且 setter 花费了大约 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);
//////////////////////////////////////////////////////////////////////////
}
}
输出(仅相关内容):
Normal getter: 3
Normal setter: 4
Reflection getter: 261
Reflection setter: 183
Reflection getter 和 setter 实际上是动态调用,通常耗时 300ms。
此外,由于代码很长,我将其发布了 here还有。
最佳答案
您是否考虑过稍微不同的方法?由于 getter 和 setter 在这种情况下的主要目的是序列化和反序列化,也许您应该专注于动态生成序列化方法,这将消除使用动态方法逐一访问这些字段的开销。
例如,假设您想使用 BinaryFormatter
进行序列化(尽管您可能会选择更好的东西),目标是动态生成如下方法:
static private byte[] SerializeStudentPacket(StudentPacket packet)
{
var bf = new BinaryFormatter();
var ms = new MemoryStream();
bf.Serialize(ms, packet.age);
bf.Serialize(ms, packet.firstName);
bf.Serialize(ms, packet.grades);
bf.Serialize(ms, packet.height);
bf.Serialize(ms, packet.lastName);
return ms.ToArray();
}
通过使用 Linq 表达式可以使它比 ILGenerator 更简单:
ParameterExpression @object = Expression.Parameter(typeof(StudentPacket), "@object");
MethodInfo serializeMethodInfo = typeof(BinaryFormatter).GetMethod("Serialize", new Type[] { typeof(Stream), typeof(object) });
MethodInfo toArrayMethodInfo = typeof(MemoryStream).GetMethod("ToArray");
var bf = Expression.Variable(typeof(BinaryFormatter), "bf");
var ms = Expression.Variable(typeof(System.IO.MemoryStream), "ms");
List<Expression> expressions = new List<Expression>();
expressions.Add(
Expression.Assign(bf, Expression.New(typeof(BinaryFormatter))));
expressions.Add(
Expression.Assign(ms, Expression.New(typeof(MemoryStream))));
foreach (FieldInfo field in typeof(StudentPacket).GetFields())
{
expressions.Add(
Expression.Call(bf, serializeMethodInfo, ms,
Expression.Convert(Expression.Field(@object, field.Name),
typeof(object))));
}
expressions.Add(Expression.Call(ms, toArrayMethodInfo));
var lambda = Expression.Lambda(
Expression.Block(
new[] { bf, ms },
expressions
),
@object);
然后您当然可以存储 lambda.Compile()
的结果以序列化 StudentPacket
。同样的方法也可以用于反序列化。
关于c# - 注入(inject)通用的 getter 和 setter 以获得比反射更好的性能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19882676/
据我所知,根本不为元素呈现 HTML,或添加 display:none,似乎具有完全相同的行为:两者都使元素消失并且不与 HTML 交互。 我正在尝试禁用和隐藏一个复选框。所以HTML的总量很小;我无
我刚刚读了Android Architecture Tutorial: Developing an App with a Background Service (using IPC) .基本上是 让服
我有两个查询具有相同的结果,现在我想知道哪个查询更优化? 在选择中: select t1.*, sum(t2.value) as total_votes from table1 t1 left joi
有人告诉我,对于 I/O 绑定(bind)的应用程序,非阻塞 I/O 会更好。对于 CPU 密集型应用程序,阻塞 I/O 会好得多。我找不到这种说法的原因。试过谷歌,但很少有文章只是触及这个话题而没有
我有一个算法可以在数字列表中寻找好的对。一个好的配对被认为是索引 i 小于 j 且 arr[i] 1: # Finding the mid of the array
我有一个算法可以在数字列表中寻找好的对。一个好的配对被认为是索引 i 小于 j 且 arr[i] 1: # Finding the mid of the array
我从 API 收到一个 json,我需要解析并修改一个属性值。问题是,我收到的 json 数据的嵌套结构不一致,我无法控制它。 这将禁止我指定在特定深度(如 parsedJson.children[0
我有 451 个城市的坐标。现在我想计算每个城市之间的距离,然后根据该距离对一些结果进行排序。现在我有两个选择: 我可以运行一个循环来计算每个可能的城市组合的距离并将它们存储到一个表中,这将产生大约
对于返回相同结果的不同查询,我有两个查询计划我想知道是否有人可以告诉我哪个“更好”,以及为什么。 SELECT * FROM bids order by (select ranking from us
关闭。这个问题需要更多focused .它目前不接受答案。 想改进这个问题吗? 更新问题,使其只关注一个问题 editing this post . 关闭 7 年前。 Improve this qu
我有一个二维数组。我需要尽可能快地对其执行一些操作(函数每秒将被调用十几次,所以让它变得高效会很好)。 现在,假设我想获取元素 A[i][j],简单地使用 A[i][j] 在速度上有什么不同吗和 *(
在声明或使用字符串的代码中,我通常会看到开发人员这样声明它: string randomString = @"C:\Random\RandomFolder\ThisFile.xml"; 代替: str
这个问题在这里已经有了答案: 关闭 10 年前。 Possible Duplicate: Why don't CSS resets use '*' to cover all elements? 我正
如果我有一个包含许多重复项的 python 列表,并且我想遍历每个项目,而不是重复项,最好使用一个集合(如 set(mylist),或者找到另一种方法来创建没有重复的列表?我想只是循环遍历列表并检查重
在阅读常量接口(interface)反模式时,我发现没有实例的最终常量类比常量接口(interface)更好。 请解释一下怎么做? public interface ConstIfc { publ
我正在查看我继承的一些旧代码,我真的不喜欢某些地方的风格。我真的不喜欢它的外观的一件事是: bool func() { bool ret = true; ret &= test1();
关闭。这个问题是opinion-based 。目前不接受答案。 想要改进这个问题吗?更新问题,以便 editing this post 可以用事实和引文来回答它。 . 已关闭 4 年前。 Improv
我经常发现自己试图使用 boost/QT 信号解耦对象。实现这一点的简单方法是针对我要通信的每个具体类型,创建一个新的信号和插槽签名并连接所有相关对象。这导致了访问者模式,理想情况下我想发出一个访问者
我正在 https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html 上阅读有关 lambda 的内容 在方法
public List getInts() { List xs = new ArrayList(); xs.add(1); // return Collections.unmo
我是一名优秀的程序员,十分优秀!