FluorineFx的AMFWriter实现的也太粗心了...
前两天打算给组件做个AMF3适配器用于组件和Flash进行通讯交互,经了解后发现FluorineFx在.net下对AMF3支持比较完善的一个项目,于就下载下来做一下集成.出于好奇于是看了一下相关代码,由于只需要用到序列化问题所以只关注了一下AMFWriter;从实现代码来看AMFWriter基本没有考虑高并发下的GC压力,数据写入过程基本都是通过new byte[]复制的方式.为了进行步了解于是对FluorineFx序列化对象做了个内存分析.
出来的结果让我摸不着头脑.

损耗排在前面的竟然一些意想不到的对象...于是详细跟踪进行发现这两个对象的开销都来源于AMFWriter.GetMember方法;细看这个方法我当场晕倒:
internal object GetMember(object instance, ClassMember member)
{
if (instance is ASObject)
{
ASObject aso = instance as ASObject;
if (aso.ContainsKey(member.Name))
return aso[member.Name];
}
Type type = instance.GetType();
if (member.MemberType == MemberTypes.Property)
{
PropertyInfo property= type.GetProperty(member.Name, member.BindingFlags);
return property.GetValue(instance, null);
}
if (member.MemberType == MemberTypes.Field)
{
FieldInfo field = type.GetField(member.Name, member.BindingFlags);
return field.GetValue(instance);
}
string msg = __Res.GetString(__Res.Reflection_MemberNotFound, string.Format("{0}.{1}", type.FullName, member.Name));
throw new FluorineException(msg);
}
获取成员值的方法竟然每次都会GetProperty或GetField,所以在测试内存结果为什么PropertyInfo[]占第一位置的原因.其实PropertyInfo和FieldInfo都是线程安全根本没有必要做.于是做了简单的调整
internal object GetMember(object instance, ClassMember member)
{
if (instance is ASObject)
{
ASObject aso = instance as ASObject;
if (aso.ContainsKey(member.Name))
return aso[member.Name];
}
Type type = instance.GetType();
if (member.MemberType == MemberTypes.Property)
{
if(member.Property ==null)
member.Property= type.GetProperty(member.Name, member.BindingFlags);
return member.Property.GetValue(instance, null);
}
if (member.MemberType == MemberTypes.Field)
{
if(member.Field ==null)
member.Field = type.GetField(member.Name, member.BindingFlags);
return member.Field.GetValue(instance);
}
string msg = __Res.GetString(__Res.Reflection_MemberNotFound, string.Format("{0}.{1}", type.FullName, member.Name));
throw new FluorineException(msg);
}
同样在查看代码中发现WriteAMF3UTF也很有问题,于是也调整了一下
static void _WriteAMF3UTF(string value, AMFWriter writer)
{
lock (_LockWriterString)
{
if (value == string.Empty)
{
writer.WriteAMF3IntegerData(1);
}
else
{
if (!writer._stringReferences.ContainsKey(value))
{
writer._stringReferences.Add(value, writer._stringReferences.Count);
int byteCount = Encoding.UTF8.GetBytes(value, 0, value.Length, encodingdata, 0);
int handle = byteCount;
handle = handle << 1;
handle = handle | 1;
writer.WriteAMF3IntegerData(handle);
if (byteCount > 0)
writer.Write(encodingdata,0,byteCount);
}
else
{
int handle = (int)writer._stringReferences[value];
handle = handle << 1;
writer.WriteAMF3IntegerData(handle);
}
}
}
}
/// <summary>
/// Writes a UTF-8 string to the current position in the AMF stream.
/// </summary>
/// <param name="value">The UTF-8 string.</param>
/// <remarks>Standard or long string header is not written.</remarks>
public void WriteAMF3UTF(string value)
{
_WriteAMF3UTF(value, this);
//if( value == string.Empty )
//{
// WriteAMF3IntegerData(1);
//}
//else
//{
// if (!_stringReferences.ContainsKey(value))
// {
// _stringReferences.Add(value, _stringReferences.Count);
// UTF8Encoding utf8Encoding = new UTF8Encoding();
// int byteCount = utf8Encoding.GetByteCount(value);
// int handle = byteCount;
// handle = handle << 1;
// handle = handle | 1;
// WriteAMF3IntegerData(handle);
// byte[] buffer = utf8Encoding.GetBytes(value);
// if (buffer.Length > 0)
// Write(buffer);
// }
// else
// {
// int handle = (int)_stringReferences[value];
// handle = handle << 1;
// WriteAMF3IntegerData(handle);
// }
//}
}
从整个AMFWriter的实现代码来说,发现作者内存开销方面没有任何意识,那也难怪MS在推出.NET的时候就告诉我们不需要管内存,我们会把它搞定...
调整前后序列化10000个order的效率对比.

总的来说AMFWriter的每个写入数据方法都有可优化的余地,看FluorineFx持官网似乎有商业支持,但代码修改日期已经是很久以前,竟然没意识到这一点感觉有点惊讶.
访问Beetlex的Github

浙公网安备 33010602011771号