Free cookie consent management tool by TermsFeed Policy Generator

Ignore:
Timestamp:
06/09/10 16:29:02 (15 years ago)
Author:
epitzer
Message:

Accelerate persistence: (#646)

  • dynamically compile field and property accessors
  • enable caching of compiled field and property accessors
  • delay costly custom attribute retrieval by checking signatures first
  • implement custom List<double> serializer
  • log deserialization time span
Location:
trunk/sources/HeuristicLab.Persistence/3.3
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • trunk/sources/HeuristicLab.Persistence/3.3/Core/DataMemberAccessor.cs

    r3743 r3913  
    2323using System.Reflection;
    2424using HeuristicLab.Persistence.Core;
     25using System.Reflection.Emit;
     26using System.Collections.Generic;
    2527
    2628namespace HeuristicLab.Persistence.Core {
    27 
     29 
    2830  /// <summary>
    2931  /// Encapsulation and abstraction for access a data member of an object
     
    3133  /// default value and an alternate name can be specified.
    3234  /// </summary>
    33   public class DataMemberAccessor {
     35  public sealed class DataMemberAccessor {
     36
     37    #region fields
    3438
    3539    /// <summary>
    3640    /// The function to get the value of the data member.
    3741    /// </summary>
    38     public readonly Func<object> Get;
     42    public readonly Func<object, object> Get;
    3943
    4044    /// <summary>
    4145    /// The function to set the value of the data member.
    4246    /// </summary>
    43     public readonly Action<object> Set;
     47    public readonly Action<object, object> Set;
    4448
    4549    /// <summary>
     
    5559    public readonly object DefaultValue;
    5660
     61    #endregion
     62
     63    #region constructors
    5764
    5865    /// <summary>
     
    6370    /// <param name="name">The name.</param>
    6471    /// <param name="defaultvalue">The defaultvalue.</param>
    65     /// <param name="obj">The object.</param>
    66     public DataMemberAccessor(MemberInfo memberInfo, string name, object defaultvalue, object obj) {
     72    public DataMemberAccessor(MemberInfo memberInfo, string name, object defaultvalue) {
     73      Get = GenerateGetter(memberInfo);
     74      Set = GenerateSetter(memberInfo);
    6775      Name = name;
    6876      DefaultValue = defaultvalue;
    69       if (memberInfo.MemberType == MemberTypes.Field) {
    70         FieldInfo fieldInfo = (FieldInfo)memberInfo;
    71         Get = () => fieldInfo.GetValue(obj);
    72         Set = value => fieldInfo.SetValue(obj, value);
    73       } else if (memberInfo.MemberType == MemberTypes.Property) {
    74         PropertyInfo propertyInfo = (PropertyInfo)memberInfo;
    75         if (!propertyInfo.CanRead || !propertyInfo.CanWrite) {
    76           throw new PersistenceException(
    77             "Storable properties must implement both a Get and a Set Accessor. ");
    78         }
    79         Get = () => propertyInfo.GetValue(obj, null);
    80         Set = value => propertyInfo.SetValue(obj, value, null);
    81       } else {
    82         throw new PersistenceException(
    83           "The Storable attribute can only be applied to fields and properties.");
    84       }
    85     }
    86 
    87     /// <summary>
    88     /// Wrap existing getter and setter functions.
    89     /// </summary>
    90     /// <param name="name">The name.</param>
    91     /// <param name="defaultValue">The default value.</param>
    92     /// <param name="getter">The getter.</param>
    93     /// <param name="setter">The setter.</param>
    94     public DataMemberAccessor(string name, object defaultValue,
    95         Func<object> getter, Action<object> setter) {
    96       Name = name;
    97       DefaultValue = defaultValue;
    98       Get = getter;
    99       Set = setter;
    10077    }
    10178
     
    10380    /// Create an empty accessor that just encapsulates an object
    10481    /// without access.
    105     /// </summary>
    106     /// <param name="o">The object</param>
    107     public DataMemberAccessor(object o) {
     82    /// </summary>   
     83    public DataMemberAccessor() {
    10884      Name = null;
    10985      DefaultValue = null;
    110       Get = () => o;
    111       Set = null;
     86      Get = Id;
    11287    }
    11388
     
    11590    /// Create an empty accessor that just encapsulates an object
    11691    /// without access.
    117     /// </summary>
    118     /// <param name="o">The object</param>
     92    /// </summary>   
    11993    /// <param name="name">The object's name.</param>
    120     public DataMemberAccessor(object o, string name) {
     94    public DataMemberAccessor(string name) {
    12195      Name = name;
    12296      DefaultValue = null;
    123       Get = () => o;
    124       Set = null;
    125     }
     97      Get = Id;
     98    }
     99
     100    /// <summary>
     101    /// Initializes a new instance of the <see cref="DataMemberAccessor"/> class using the
     102    /// getter and setter from an exisiting instance but with a new name and default value.
     103    /// </summary>
     104    /// <param name="dma">The existing DataMemberAccessor.</param>
     105    /// <param name="name">The new name.</param>
     106    /// <param name="defaultValue">The new default value.</param>
     107    public DataMemberAccessor(DataMemberAccessor dma, string name, object defaultValue) {
     108      Get = dma.Get;
     109      Set = dma.Set;
     110      this.Name = name;
     111      this.DefaultValue = defaultValue;
     112    }
     113
     114    #endregion
     115
     116    #region auxiliary methods
    126117
    127118    /// <summary>
     
    137128        Get.Method, Set.Method);
    138129    }
     130
     131    /// <summary>
     132    /// The identity function
     133    /// </summary>
     134    /// <param name="o">An object.</param>
     135    /// <returns>its argument o unmodified.</returns>
     136    public static object Id(object o) {
     137      return o;
     138    }
     139
     140    #endregion
     141
     142    #region static methods (code generators)
     143
     144    /// <summary>
     145    /// Generate a getter for the given field or property
     146    /// </summary>
     147    /// <param name="memberInfo">The member info.</param>
     148    /// <returns></returns>
     149    public static Func<object, object> GenerateGetter(MemberInfo memberInfo) {
     150      if (memberInfo.MemberType == MemberTypes.Field) {
     151        FieldInfo fieldInfo = (FieldInfo)memberInfo;
     152        return GenerateFieldGetter(fieldInfo);
     153      } else if (memberInfo.MemberType == MemberTypes.Property) {
     154        PropertyInfo propertyInfo = (PropertyInfo)memberInfo;
     155        if (!propertyInfo.CanRead || !propertyInfo.CanWrite) {
     156          throw new PersistenceException(
     157            "Storable properties must implement both a Get and a Set Accessor. ");
     158        }
     159        return GeneratePropertyGetter(propertyInfo);
     160      } else {
     161        throw new PersistenceException(
     162          "The Storable attribute can only be applied to fields and properties.");
     163      }
     164    }
     165
     166    /// <summary>
     167    /// Generates a setter for the given field or property.
     168    /// </summary>
     169    /// <param name="memberInfo">The member info.</param>
     170    /// <returns></returns>
     171    public static Action<object, object> GenerateSetter(MemberInfo memberInfo) {
     172      if (memberInfo.MemberType == MemberTypes.Field) {
     173        FieldInfo fieldInfo = (FieldInfo)memberInfo;
     174        return GenerateFieldSetter(fieldInfo);
     175      } else if (memberInfo.MemberType == MemberTypes.Property) {
     176        PropertyInfo propertyInfo = (PropertyInfo)memberInfo;
     177        if (!propertyInfo.CanRead || !propertyInfo.CanWrite) {
     178          throw new PersistenceException(
     179            "Storable properties must implement both a Get and a Set Accessor. ");
     180        }
     181        return GeneratePropertySetter(propertyInfo);
     182      } else {
     183        throw new PersistenceException(
     184          "The Storable attribute can only be applied to fields and properties.");
     185      }
     186    }
     187
     188    /// <summary>
     189    /// Generates a dynamically compiled getter to access fields (even private ones).
     190    /// </summary>
     191    /// <param name="fieldInfo">The field info.</param>
     192    /// <returns>A Func&lt;object, object&gt;</returns>
     193    public static Func<object, object> GenerateFieldGetter(FieldInfo fieldInfo) {
     194      DynamicMethod dm = new DynamicMethod("", typeof(object), new Type[] { typeof(object) }, fieldInfo.DeclaringType, true);
     195      ILGenerator ilgen = dm.GetILGenerator();
     196      ilgen.Emit(OpCodes.Ldarg_0);
     197      ilgen.Emit(OpCodes.Castclass, fieldInfo.DeclaringType);
     198      ilgen.Emit(OpCodes.Ldfld, fieldInfo);
     199      ilgen.Emit(OpCodes.Box, fieldInfo.FieldType);
     200      ilgen.Emit(OpCodes.Ret);
     201      return (Func<object, object>)dm.CreateDelegate(typeof(Func<object, object>));
     202    }
     203
     204    /// <summary>
     205    /// Generates a dynamically compiled sett to access fields (even private ones).
     206    /// </summary>
     207    /// <param name="fieldInfo">The field info.</param>
     208    /// <returns>An Action&lt;object, object%gt;</returns>
     209    public static Action<object, object> GenerateFieldSetter(FieldInfo fieldInfo) {
     210      DynamicMethod dm = new DynamicMethod("", null, new Type[] { typeof(object), typeof(object) }, fieldInfo.DeclaringType, true);
     211      ILGenerator ilgen = dm.GetILGenerator();
     212      ilgen.Emit(OpCodes.Ldarg_0);
     213      ilgen.Emit(OpCodes.Castclass, fieldInfo.DeclaringType);
     214      ilgen.Emit(OpCodes.Ldarg_1);
     215      ilgen.Emit(OpCodes.Unbox_Any, fieldInfo.FieldType);
     216      ilgen.Emit(OpCodes.Stfld, fieldInfo);
     217      ilgen.Emit(OpCodes.Ret);
     218      return (Action<object, object>)dm.CreateDelegate(typeof(Action<object, object>));
     219    }
     220
     221    /// <summary>
     222    /// Generates a dynamically compiled getter to access properties (even private ones).
     223    /// </summary>
     224    /// <param name="propertyInfo">The property info.</param>
     225    /// <returns>A Func&lt;object, object&gt;</returns>
     226    public static Func<object, object> GeneratePropertyGetter(PropertyInfo propertyInfo) {
     227      DynamicMethod dm = new DynamicMethod("", typeof(object), new Type[] { typeof(object) }, propertyInfo.DeclaringType, true);
     228      ILGenerator ilgen = dm.GetILGenerator();
     229      ilgen.Emit(OpCodes.Ldarg_0);
     230      ilgen.Emit(OpCodes.Castclass, propertyInfo.DeclaringType);
     231      ilgen.Emit(OpCodes.Callvirt, propertyInfo.GetGetMethod(true));
     232      ilgen.Emit(OpCodes.Box, propertyInfo.PropertyType);
     233      ilgen.Emit(OpCodes.Ret);
     234      return (Func<object, object>)dm.CreateDelegate(typeof(Func<object, object>));
     235    }
     236
     237    /// <summary>
     238    /// Generates a dynamically compiled setter to access properties (even private ones).
     239    /// </summary>
     240    /// <param name="propertyInfo">The property info.</param>
     241    /// <returns>An Action&lt;object, object%gt;</returns>
     242    public static Action<object, object> GeneratePropertySetter(PropertyInfo propertyInfo) {
     243      DynamicMethod dm = new DynamicMethod("", null, new Type[] { typeof(object), typeof(object) }, propertyInfo.DeclaringType, true);
     244      ILGenerator ilgen = dm.GetILGenerator();
     245      ilgen.Emit(OpCodes.Ldarg_0);
     246      ilgen.Emit(OpCodes.Castclass, propertyInfo.DeclaringType);
     247      ilgen.Emit(OpCodes.Ldarg_1);
     248      ilgen.Emit(OpCodes.Unbox_Any, propertyInfo.PropertyType);
     249      ilgen.Emit(OpCodes.Callvirt, propertyInfo.GetSetMethod(true));
     250      ilgen.Emit(OpCodes.Ret);
     251      return (Action<object, object>)dm.CreateDelegate(typeof(Action<object, object>));
     252    }
     253
     254    #endregion
    139255  }
    140256
  • trunk/sources/HeuristicLab.Persistence/3.3/Core/Serializer.cs

    r3743 r3913  
    187187    /// </returns>
    188188    public IEnumerator<ISerializationToken> GetEnumerator() {
    189       var enumerator = Serialize(new DataMemberAccessor(rootName, null, () => obj, null));
     189      var enumerator = Serialize(new DataMemberAccessor(rootName), obj);
    190190      if (isTestRun) {
    191191        return AddExceptionCompiler(enumerator);
     
    206206    private Stack<string> objectGraphTrace = new Stack<string>();
    207207
    208     private IEnumerator<ISerializationToken> Serialize(DataMemberAccessor accessor) {
     208    private IEnumerator<ISerializationToken> Serialize(DataMemberAccessor accessor, object obj) {
    209209     
    210       object value = accessor.Get();
     210      object value = accessor.Get(obj);
    211211      if (value == null)
    212212        return NullReferenceEnumerator(accessor.Name);
     
    311311      if (metaInfo != null) {
    312312        foreach (var tag in metaInfo) {
    313           IEnumerator<ISerializationToken> metaIt = Serialize(new DataMemberAccessor(tag.Value, tag.Name));
     313          IEnumerator<ISerializationToken> metaIt = Serialize(new DataMemberAccessor(tag.Name), tag.Value);
    314314          while (metaIt.MoveNext()) {
    315315            if (first) {
     
    326326      if (tags != null) {
    327327        foreach (var tag in tags) {
    328           IEnumerator<ISerializationToken> it = Serialize(new DataMemberAccessor(tag.Value, tag.Name));
     328          IEnumerator<ISerializationToken> it = Serialize(new DataMemberAccessor(tag.Name), tag.Value);
    329329          while (it.MoveNext())
    330330            yield return it.Current;
  • trunk/sources/HeuristicLab.Persistence/3.3/Default/CompositeSerializers/Storable/StorableReflection.cs

    r3742 r3913  
    7373
    7474    private static object[] emptyArgs = new object[0];
     75    private static Type[] objectArg = new[] { typeof(object) };
    7576
    7677    public static IEnumerable<Hook> CollectHooks(HookType hookType, Type type) {
     
    7980          yield return mi;
    8081      foreach (MemberInfo memberInfo in type.GetMembers(DECLARED_INSTANCE_MEMBERS)) {
     82        if (memberInfo.MemberType != MemberTypes.Method)
     83          continue;
     84        MethodInfo methodInfo = memberInfo as MethodInfo;
     85        if (methodInfo.ReturnType != typeof(void))
     86          continue;
     87        if (methodInfo.GetParameters().Length > 0)
     88          continue;
    8189        foreach (StorableHookAttribute hook in memberInfo.GetCustomAttributes(typeof(StorableHookAttribute), false)) {
    8290          if (hook != null && hook.HookType == hookType) {
    83             MethodInfo methodInfo = memberInfo as MethodInfo;
    84             if (memberInfo.MemberType != MemberTypes.Method || memberInfo == null)
    85               throw new ArgumentException("Storable hooks must be methods");           
    86             DynamicMethod dm = new DynamicMethod("", null, new[] { typeof(object) }, type);
    87             ILGenerator ilgen = dm.GetILGenerator();
    88             ilgen.Emit(OpCodes.Ldarg_0);
    89             ilgen.Emit(OpCodes.Callvirt, methodInfo);
    90             ilgen.Emit(OpCodes.Ret);
    91             yield return (Hook)dm.CreateDelegate(typeof(Hook));           
     91            yield return new Hook((o) => methodInfo.Invoke(o, emptyArgs));
    9292          }
    9393        }
     
    9999    private static void AddMarkedMembers(Type type, List<StorableMemberInfo> storableMembers) {
    100100      foreach (MemberInfo memberInfo in type.GetMembers(DECLARED_INSTANCE_MEMBERS)) {
    101         foreach (StorableAttribute attribute in memberInfo.GetCustomAttributes(typeof(StorableAttribute), false)) {
    102           storableMembers.Add(new StorableMemberInfo(attribute, memberInfo));
     101        if (memberInfo.MemberType == MemberTypes.Field ||
     102          memberInfo.MemberType == MemberTypes.Property) {
     103          foreach (StorableAttribute attribute in memberInfo.GetCustomAttributes(typeof(StorableAttribute), false)) {
     104            storableMembers.Add(new StorableMemberInfo(attribute, memberInfo));
     105          }
    103106        }
    104107      }
  • trunk/sources/HeuristicLab.Persistence/3.3/Default/CompositeSerializers/Storable/StorableSerializer.cs

    r3742 r3913  
    109109    /// <returns>An enumerable of <see cref="Tag"/>s.</returns>
    110110    public IEnumerable<Tag> Decompose(object obj) {
    111       foreach (var accessor in GetStorableAccessors(obj)) {
    112         yield return new Tag(accessor.Name, accessor.Get());
     111      foreach (var accessor in GetStorableAccessors(obj.GetType())) {
     112        yield return new Tag(accessor.Name, accessor.Get(obj));
    113113      }
    114114    }
     
    142142        memberDict.Add(iter.Current.Name, iter.Current);
    143143      }
    144       foreach (var accessor in GetStorableAccessors(instance)) {
     144      foreach (var accessor in GetStorableAccessors(instance.GetType())) {
    145145        if (memberDict.ContainsKey(accessor.Name)) {
    146           accessor.Set(memberDict[accessor.Name].Value);
     146          accessor.Set(instance, memberDict[accessor.Name].Value);
    147147        } else if (accessor.DefaultValue != null) {
    148           accessor.Set(accessor.DefaultValue);
     148          accessor.Set(instance, accessor.DefaultValue);
    149149        }
    150150      }
     
    160160
    161161    private static readonly object[] emptyArgs = new object[] { };
     162    private static readonly object[] trueArgs = new object[] { true };
    162163
    163164    private sealed class HookDesignator {
     
    171172    }
    172173
    173     private sealed class MemberCache : Dictionary<Type, IEnumerable<StorableMemberInfo>> { }
     174    private sealed class AccessorListCache : Dictionary<Type, IEnumerable<DataMemberAccessor>> { }
     175    private sealed class AccessorCache : Dictionary<MemberInfo, DataMemberAccessor> { }
    174176
    175177    #endregion
     
    177179    #region caches
    178180
    179     private MemberCache storableMemberCache = new MemberCache();
     181    private AccessorListCache accessorListCache = new AccessorListCache();
     182    private AccessorCache accessorCache = new AccessorCache();
    180183
    181184    private delegate object Constructor();
     
    191194    #region attribute access
    192195
    193     private IEnumerable<StorableMemberInfo> GetStorableMembers(Type type) {
    194       lock (storableMemberCache) {
    195         if (storableMemberCache.ContainsKey(type))
    196           return storableMemberCache[type];
    197         var storablesMembers = StorableReflection.GenerateStorableMembers(type);
    198         storableMemberCache[type] = storablesMembers;
    199         return storablesMembers;
     196    private IEnumerable<DataMemberAccessor> GetStorableAccessors(Type type) {
     197      lock (accessorListCache) {
     198        if (accessorListCache.ContainsKey(type))
     199          return accessorListCache[type];
     200        var storableMembers = StorableReflection
     201          .GenerateStorableMembers(type)
     202          .Select(mi => GetMemberAccessor(mi));
     203        accessorListCache[type] = storableMembers;
     204        return storableMembers;
     205      }     
     206    }
     207
     208    private DataMemberAccessor GetMemberAccessor(StorableMemberInfo mi) {
     209      lock (accessorCache) {
     210        if (accessorCache.ContainsKey(mi.MemberInfo))
     211          return new DataMemberAccessor(accessorCache[mi.MemberInfo], mi.DisentangledName, mi.DefaultValue);
     212        DataMemberAccessor dma = new DataMemberAccessor(mi.MemberInfo, mi.DisentangledName, mi.DefaultValue);
     213        accessorCache[mi.MemberInfo] = dma;
     214        return dma;
    200215      }
    201216    }
     
    209224        return c;
    210225      }
    211     }
     226    }   
    212227
    213228    private Constructor GetDefaultConstructor(Type type) {
    214229      ConstructorInfo ci = type.GetConstructor(ALL_CONSTRUCTORS, null, Type.EmptyTypes, null);
    215230      if (ci == null)
    216         return null;
     231        return null;     
    217232      DynamicMethod dm = new DynamicMethod("", typeof(object), null, type);
    218233      ILGenerator ilgen = dm.GetILGenerator();
     
    227242          if (ci.GetParameters().Length != 1 ||
    228243              ci.GetParameters()[0].ParameterType != typeof(bool))
    229             throw new PersistenceException("StorableConstructor must have exactly one argument of type bool");
     244            throw new PersistenceException("StorableConstructor must have exactly one argument of type bool");         
    230245          DynamicMethod dm = new DynamicMethod("", typeof(object), null, type);
    231246          ILGenerator ilgen = dm.GetILGenerator();
     
    237252      }
    238253      return null;
    239     }
    240 
    241     private IEnumerable<DataMemberAccessor> GetStorableAccessors(object obj) {
    242       return GetStorableMembers(obj.GetType())
    243         .Select(mi => new DataMemberAccessor(mi.MemberInfo, mi.DisentangledName, mi.DefaultValue, obj));
    244     }
     254    }   
    245255
    246256    private void InvokeHook(HookType hookType, object obj) {
  • trunk/sources/HeuristicLab.Persistence/3.3/Default/Xml/Compact/DoubleList2XmlSerializer.cs

    r3742 r3913  
    2323using System.Collections.Generic;
    2424using System;
     25using System.Linq;
    2526using HeuristicLab.Persistence.Core;
    2627using HeuristicLab.Persistence.Default.Xml.Primitive;
     28using System.Text;
    2729
    2830namespace HeuristicLab.Persistence.Default.Xml.Compact {
    2931
    30   internal sealed class DoubleList2XmlSerializer : NumberEnumeration2XmlSerializerBase<List<double>> {
     32  internal sealed class DoubleList2XmlSerializer : CompactXmlSerializerBase<List<double>> {
    3133
    32     protected override void Add(IEnumerable enumeration, object o) {
    33       ((List<double>)enumeration).Add((double)o);
     34    private static readonly char[] separators = new char[] { ';' };
     35
     36    public override XmlString Format(List<double> list) {
     37      StringBuilder sb = new StringBuilder();
     38      foreach (var d in list) {
     39        sb.Append(Double2XmlSerializer.FormatG17(d)).Append(';');
     40      }
     41      return new XmlString(sb.ToString());
    3442    }
    3543
    36     protected override IEnumerable Instantiate() {
    37       return new List<double>();
     44    public override List<double> Parse(XmlString data) {
     45      try {
     46        var values = data.Data.Split(separators, StringSplitOptions.RemoveEmptyEntries);
     47        List<double> list = new List<double>(values.Length);
     48        foreach (var value in values) {
     49          list.Add(Double2XmlSerializer.ParseG17(value));
     50        }
     51        return list;
     52      } catch (InvalidCastException e) {
     53        throw new PersistenceException("Invalid element data during reconstruction of List<double>.", e);
     54      } catch (OverflowException e) {
     55        throw new PersistenceException("Overflow during element parsing while trying to reconstruct List<double>.", e);
     56      }
    3857    }
    39 
    40     protected override string FormatValue(object o) {
    41       return Double2XmlSerializer.FormatG17((double)o);
    42     }
    43 
    44     protected override object ParseValue(string o) {
    45       return Double2XmlSerializer.ParseG17(o);
    46     }
    47 
    4858  }
    4959}
  • trunk/sources/HeuristicLab.Persistence/3.3/Default/Xml/XmlParser.cs

    r3742 r3913  
    188188    /// <returns>A fresh object instance</returns>
    189189    public static object Deserialize(string filename) {
    190       using (ZipFile file = new ZipFile(filename)) {
    191         return Deserialize(file);
     190      TimeSpan start = System.Diagnostics.Process.GetCurrentProcess().TotalProcessorTime;
     191      try {
     192        using (ZipFile file = new ZipFile(filename)) {
     193          return Deserialize(file);
     194        }
     195      } finally {
     196        TimeSpan end = System.Diagnostics.Process.GetCurrentProcess().TotalProcessorTime;
     197        Tracing.Logger.Info(string.Format(
     198          "deserialization of {0} took {1} seconds",
     199          filename, (end - start).TotalSeconds));
    192200      }
    193201    }
Note: See TracChangeset for help on using the changeset viewer.