FluorineFx的AMFWriter实现的也太粗心了...

时间:2022-08-30 10:23:41

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

    出来的结果让我摸不着头脑.

FluorineFx的AMFWriter实现的也太粗心了...

损耗排在前面的竟然一些意想不到的对象...于是详细跟踪进行发现这两个对象的开销都来源于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的效率对比.

FluorineFx的AMFWriter实现的也太粗心了...

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