Cache property lookups for the object parsing

This commit is contained in:
2016-04-27 11:16:54 -04:00
parent 9730600164
commit 790930dd66
10 changed files with 185 additions and 58 deletions
+3
View File
@@ -104,10 +104,13 @@
<Compile Include="Parsers\CsvParserBase.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Utility\FileUtility.cs" />
<Compile Include="Utility\Memoizer.cs" />
<Compile Include="Utility\ParseInfoAttribute.cs" />
<Compile Include="Parsers\LeafInputCsvParser.cs" />
<Compile Include="Utility\ParsedObjectFactory.cs" />
<Compile Include="Utility\ParseException.cs" />
<Compile Include="Utility\ParseInfoPropertyMatcher.cs" />
<Compile Include="Utility\ParseInfoPropertyMatcherWithCache.cs" />
<Compile Include="Utility\ReflectionExtensions.cs" />
<Compile Include="Utility\StringExtensions.cs" />
</ItemGroup>
+2 -1
View File
@@ -30,6 +30,7 @@ namespace LeafWeb.Core.Parsers
private IEnumerable<LeafGasComparison> ParseLeafGasComparisonSet(string[] fittingTitles)
{
var matcher = new ParseInfoPropertyMatcher<LeafGasComparisonPhotosyntheticInfo>();
var endOfFile = false;
while (!endOfFile)
{
@@ -43,7 +44,7 @@ namespace LeafWeb.Core.Parsers
throw new ParseException($"Encountered empty line while readding fitting info on line {CsvReader.Row}");
if (values == null) // end of file
yield break;
if (ParsedObjectFactory<LeafGasComparisonPhotosyntheticInfo>.IsPropertiesTitlesMatch(values))
if (matcher.IsPropertiesTitlesMatch(values))
{
photosyntheticTitles = values;
break;
+22
View File
@@ -0,0 +1,22 @@
using System;
using System.Collections.Concurrent;
namespace LeafWeb.Core.Utility
{
public static class Memoizer
{
public static Func<A, R> ThreadsafeMemoize<A, R>(Func<A, R> f)
{
var cache = new ConcurrentDictionary<A, R>();
return argument => cache.GetOrAdd(argument, f);
}
public static Func<A1, A2, R> ThreadsafeMemoize<A1, A2, R>(Func<A1, A2, R> f)
{
var cache = new ConcurrentDictionary<Tuple<A1, A2>, R>();
return (a1, a2) => cache.GetOrAdd(Tuple.Create(a1, a2), t => f(t.Item1, t.Item2));
}
}
}
+66
View File
@@ -0,0 +1,66 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Fasterflect;
namespace LeafWeb.Core.Utility
{
public class ParseInfoPropertyMatcher<T>
where T : new()
{
public IList<PropertyInfo> ParseInfoProperties { get; }
public ParseInfoPropertyMatcher()
{
ParseInfoProperties = GetParseInfoProperties();
}
private PropertyInfo[] GetParseInfoProperties()
{
var propertyInfos = typeof(T).Properties();
return propertyInfos.Where(p => AttributeExtensions.HasAttribute<ParseInfoAttribute>(p)).ToArray();
}
public virtual PropertyInfo MatchProperty(string title, int position)
{
return
ParseInfoProperties.FirstOrDefault(p => p.Attribute<ParseInfoAttribute>().IsTitleMatch(title)) ??
ParseInfoProperties.FirstOrDefault(p => p.Attribute<ParseInfoAttribute>().IsPositionMatch(position));
}
public virtual PropertyInfo MatchPropertyExact(string title, int position)
{
return
ParseInfoProperties
.FirstOrDefault(p =>
{
var attribute = p.Attribute<ParseInfoAttribute>();
return attribute.IsTitleMatch(title) && attribute.IsPositionMatch(position);
});
}
public virtual bool IsPropertiesTitlesMatch(string[] titles)
{
var propertyMatch = 0;
var propertyNoMatch = 0;
for (var i = 0; i < titles.Length; i++)
{
var title = titles[i];
var position = i + 1;
if (String.IsNullOrEmpty(title))
continue;
var property = MatchPropertyExact(title, position);
if (property != null)
propertyMatch++;
else
propertyNoMatch++;
}
return propertyMatch / (double)propertyNoMatch > .9;
}
}
}
@@ -0,0 +1,28 @@
using System;
using System.Reflection;
namespace LeafWeb.Core.Utility
{
public class ParseInfoPropertyMatcherWithCache<T> : ParseInfoPropertyMatcher<T>
where T: new()
{
private readonly Func<string, int, PropertyInfo> _matchPropertyMemo;
private readonly Func<string, int, PropertyInfo> _matchPropertyExactMemo;
public ParseInfoPropertyMatcherWithCache()
{
_matchPropertyMemo = Memoizer.ThreadsafeMemoize<string,int,PropertyInfo>(base.MatchProperty);
_matchPropertyExactMemo = Memoizer.ThreadsafeMemoize<string,int,PropertyInfo>(base.MatchPropertyExact);
}
public override PropertyInfo MatchProperty(string title, int position)
{
return _matchPropertyMemo(title, position);
}
public override PropertyInfo MatchPropertyExact(string title, int position)
{
return _matchPropertyExactMemo(title, position);
}
}
}
+7 -56
View File
@@ -6,7 +6,8 @@ using Fasterflect;
namespace LeafWeb.Core.Utility
{
public static class ParsedObjectFactory<T> where T : new()
public static class ParsedObjectFactory<T>
where T : new()
{
static ParsedObjectFactory()
{
@@ -14,19 +15,13 @@ namespace LeafWeb.Core.Utility
BoolTypeConverter.Register();
}
private static PropertyInfo[] GetProperties()
{
var propertyInfos = typeof(T).Properties();
return propertyInfos.Where(p => p.HasAttribute<ParseInfoAttribute>()).ToArray();
}
/// <summary>
/// Create an object type T filling properties from the given title values
/// </summary>
/// <param name="titleValues">Colon separated title: values</param>
public static T Create(string[] titleValues)
{
var properties = GetProperties();
var properties = new ParseInfoPropertyMatcherWithCache<T>().ParseInfoProperties;
var obj = new T();
// take each of the
for (var index = 0; index < titleValues.Length; index++)
@@ -68,7 +63,7 @@ namespace LeafWeb.Core.Utility
public static T[] Create(string[] titles, string[][] valueArrays)
{
var properties = GetProperties();
var matcher = new ParseInfoPropertyMatcherWithCache<T>();
var objs = new T[valueArrays.Length];
for (var vIndex = 0; vIndex < valueArrays.Length; vIndex++)
@@ -84,7 +79,7 @@ namespace LeafWeb.Core.Utility
if (IsMissingValue(value))
continue;
var property = MatchProperty(properties, title, position);
var property = matcher.MatchProperty(title, position);
if (property != null)
{
@@ -101,7 +96,7 @@ namespace LeafWeb.Core.Utility
public static T Create(Tuple<string, string>[] titleValues)
{
var properties = GetProperties();
var matcher = new ParseInfoPropertyMatcherWithCache<T>();
var obj = new T();
for (var index = 0; index < titleValues.Length; index++)
@@ -114,7 +109,7 @@ namespace LeafWeb.Core.Utility
if (IsMissingValue(value))
continue;
var property = MatchProperty(properties, title, position);
var property = matcher.MatchProperty(title, position);
if (property != null)
{
@@ -132,50 +127,6 @@ namespace LeafWeb.Core.Utility
return string.IsNullOrEmpty(value) || value == "NA" || value == "-9999";
}
// TODO: this call maybe could be cached
private static PropertyInfo MatchProperty(PropertyInfo[] properties, string title, int position)
{
return
properties.FirstOrDefault(p => p.Attribute<ParseInfoAttribute>().IsTitleMatch(title)) ??
properties.FirstOrDefault(p => p.Attribute<ParseInfoAttribute>().IsPositionMatch(position));
}
// TODO: this call maybe could be cached
private static PropertyInfo MatchPropertyExact(PropertyInfo[] properties, string title, int position)
{
return
properties
.FirstOrDefault(p =>
{
var attribute = p.Attribute<ParseInfoAttribute>();
return attribute.IsTitleMatch(title) && attribute.IsPositionMatch(position);
});
}
public static bool IsPropertiesTitlesMatch(string[] titles)
{
var properties = GetProperties();
var propertyMatch = 0;
var propertyNoMatch = 0;
for (var i = 0; i < titles.Length; i++)
{
var title = titles[i];
var position = i + 1;
if (string.IsNullOrEmpty(title))
continue;
var property = MatchPropertyExact(properties, title, position);
if (property != null)
propertyMatch++;
else
propertyNoMatch++;
}
return propertyMatch / (double) propertyNoMatch > .9;
}
private static bool TryConvertValue(PropertyInfo property, object value, out object convertedValue)
{
try