|
|
|
@ -17,7 +17,7 @@ namespace ICSharpCode.NRefactory.TypeSystem
@@ -17,7 +17,7 @@ namespace ICSharpCode.NRefactory.TypeSystem
|
|
|
|
|
{ |
|
|
|
|
//* Not a real unit test
|
|
|
|
|
[TestFixture] |
|
|
|
|
public class TestInterningProvider : IInterningProvider |
|
|
|
|
public class TestInterningProvider |
|
|
|
|
{ |
|
|
|
|
sealed class ReferenceComparer : IEqualityComparer<object> |
|
|
|
|
{ |
|
|
|
@ -67,98 +67,105 @@ namespace ICSharpCode.NRefactory.TypeSystem
@@ -67,98 +67,105 @@ namespace ICSharpCode.NRefactory.TypeSystem
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
HashSet<object> uniqueObjectsPreIntern = new HashSet<object>(new ReferenceComparer()); |
|
|
|
|
HashSet<object> uniqueObjectsPostIntern = new HashSet<object>(new ReferenceComparer()); |
|
|
|
|
Dictionary<object, object> byValueDict = new Dictionary<object, object>(); |
|
|
|
|
Dictionary<ISupportsInterning, ISupportsInterning> supportsInternDict = new Dictionary<ISupportsInterning, ISupportsInterning>(new InterningComparer()); |
|
|
|
|
Dictionary<IEnumerable<object>, IEnumerable<object>> listDict = new Dictionary<IEnumerable<object>, IEnumerable<object>>(new ListComparer()); |
|
|
|
|
|
|
|
|
|
public T Intern<T>(T obj) where T : class |
|
|
|
|
sealed class InterningProvider : IInterningProvider |
|
|
|
|
{ |
|
|
|
|
if (obj == null) |
|
|
|
|
return null; |
|
|
|
|
uniqueObjectsPreIntern.Add(obj); |
|
|
|
|
ISupportsInterning s = obj as ISupportsInterning; |
|
|
|
|
if (s != null) { |
|
|
|
|
ISupportsInterning output; |
|
|
|
|
if (supportsInternDict.TryGetValue(s, out output)) { |
|
|
|
|
obj = (T)output; |
|
|
|
|
} else { |
|
|
|
|
s.PrepareForInterning(this); |
|
|
|
|
if (supportsInternDict.TryGetValue(s, out output)) |
|
|
|
|
internal HashSet<object> uniqueObjectsPreIntern = new HashSet<object>(new ReferenceComparer()); |
|
|
|
|
internal HashSet<object> uniqueObjectsPostIntern = new HashSet<object>(new ReferenceComparer()); |
|
|
|
|
internal Dictionary<object, object> byValueDict = new Dictionary<object, object>(); |
|
|
|
|
internal Dictionary<ISupportsInterning, ISupportsInterning> supportsInternDict = new Dictionary<ISupportsInterning, ISupportsInterning>(new InterningComparer()); |
|
|
|
|
internal Dictionary<IEnumerable<object>, IEnumerable<object>> listDict = new Dictionary<IEnumerable<object>, IEnumerable<object>>(new ListComparer()); |
|
|
|
|
|
|
|
|
|
public T Intern<T>(T obj) where T : class |
|
|
|
|
{ |
|
|
|
|
if (obj == null) |
|
|
|
|
return null; |
|
|
|
|
uniqueObjectsPreIntern.Add(obj); |
|
|
|
|
ISupportsInterning s = obj as ISupportsInterning; |
|
|
|
|
if (s != null) { |
|
|
|
|
ISupportsInterning output; |
|
|
|
|
if (supportsInternDict.TryGetValue(s, out output)) { |
|
|
|
|
obj = (T)output; |
|
|
|
|
} else { |
|
|
|
|
s.PrepareForInterning(this); |
|
|
|
|
if (supportsInternDict.TryGetValue(s, out output)) |
|
|
|
|
obj = (T)output; |
|
|
|
|
else |
|
|
|
|
supportsInternDict.Add(s, s); |
|
|
|
|
} |
|
|
|
|
} else if (obj is IType || Type.GetTypeCode(obj.GetType()) >= TypeCode.Boolean) { |
|
|
|
|
object output; |
|
|
|
|
if (byValueDict.TryGetValue(obj, out output)) |
|
|
|
|
obj = (T)output; |
|
|
|
|
else |
|
|
|
|
supportsInternDict.Add(s, s); |
|
|
|
|
byValueDict.Add(obj, obj); |
|
|
|
|
} |
|
|
|
|
} else if (obj is string || obj is IType || obj is bool || obj is int) { |
|
|
|
|
object output; |
|
|
|
|
if (byValueDict.TryGetValue(obj, out output)) |
|
|
|
|
obj = (T)output; |
|
|
|
|
uniqueObjectsPostIntern.Add(obj); |
|
|
|
|
return obj; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public IList<T> InternList<T>(IList<T> list) where T : class |
|
|
|
|
{ |
|
|
|
|
if (list == null) |
|
|
|
|
return null; |
|
|
|
|
uniqueObjectsPreIntern.Add(list); |
|
|
|
|
for (int i = 0; i < list.Count; i++) { |
|
|
|
|
T oldItem = list[i]; |
|
|
|
|
T newItem = Intern(oldItem); |
|
|
|
|
if (oldItem != newItem) { |
|
|
|
|
if (list.IsReadOnly) |
|
|
|
|
list = new T[list.Count]; |
|
|
|
|
list[i] = newItem; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (!list.IsReadOnly) |
|
|
|
|
list = new ReadOnlyCollection<T>(list); |
|
|
|
|
IEnumerable<object> output; |
|
|
|
|
if (listDict.TryGetValue(list, out output)) |
|
|
|
|
list = (IList<T>)output; |
|
|
|
|
else |
|
|
|
|
byValueDict.Add(obj, obj); |
|
|
|
|
listDict.Add(list, list); |
|
|
|
|
uniqueObjectsPostIntern.Add(list); |
|
|
|
|
return list; |
|
|
|
|
} |
|
|
|
|
uniqueObjectsPostIntern.Add(obj); |
|
|
|
|
return obj; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public IList<T> InternList<T>(IList<T> list) where T : class |
|
|
|
|
{ |
|
|
|
|
if (list == null) |
|
|
|
|
return null; |
|
|
|
|
uniqueObjectsPreIntern.Add(list); |
|
|
|
|
for (int i = 0; i < list.Count; i++) { |
|
|
|
|
T oldItem = list[i]; |
|
|
|
|
T newItem = Intern(oldItem); |
|
|
|
|
if (oldItem != newItem) { |
|
|
|
|
if (list.IsReadOnly) |
|
|
|
|
list = new T[list.Count]; |
|
|
|
|
list[i] = newItem; |
|
|
|
|
|
|
|
|
|
public void InternProject(IProjectContent pc) |
|
|
|
|
{ |
|
|
|
|
foreach (var c in TreeTraversal.PreOrder(pc.GetClasses(), c => c.InnerClasses)) { |
|
|
|
|
Intern(c.Namespace); |
|
|
|
|
Intern(c.Name); |
|
|
|
|
foreach (IMember m in c.Members) { |
|
|
|
|
Intern(m); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (!list.IsReadOnly) |
|
|
|
|
list = new ReadOnlyCollection<T>(list); |
|
|
|
|
IEnumerable<object> output; |
|
|
|
|
if (listDict.TryGetValue(list, out output)) |
|
|
|
|
list = (IList<T>)output; |
|
|
|
|
else |
|
|
|
|
listDict.Add(list, list); |
|
|
|
|
uniqueObjectsPostIntern.Add(list); |
|
|
|
|
return list; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
[Test] |
|
|
|
|
public void PrintStatistics() |
|
|
|
|
{ |
|
|
|
|
foreach (var c in TreeTraversal.PreOrder(CecilLoaderTests.Mscorlib.GetClasses(), c => c.InnerClasses)) { |
|
|
|
|
Intern(c.Namespace); |
|
|
|
|
Intern(c.Name); |
|
|
|
|
foreach (IMember m in c.Members) { |
|
|
|
|
Intern(m); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
long startMemory = GC.GetTotalMemory(true); |
|
|
|
|
IProjectContent pc = new CecilLoader().LoadAssemblyFile(typeof(object).Assembly.Location); |
|
|
|
|
long memoryWithFullPC = GC.GetTotalMemory(true) - startMemory; |
|
|
|
|
InterningProvider p = new InterningProvider(); |
|
|
|
|
p.InternProject(pc); |
|
|
|
|
PrintStatistics(p); |
|
|
|
|
p = null; |
|
|
|
|
long memoryWithInternedPC = GC.GetTotalMemory(true) - startMemory; |
|
|
|
|
GC.KeepAlive(pc); |
|
|
|
|
Console.WriteLine(memoryWithInternedPC / 1024 + " KB / " + memoryWithFullPC / 1024 + " KB"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void PrintStatistics(InterningProvider p) |
|
|
|
|
{ |
|
|
|
|
var stats = |
|
|
|
|
from obj in uniqueObjectsPreIntern |
|
|
|
|
from obj in p.uniqueObjectsPreIntern |
|
|
|
|
group 1 by obj.GetType() into g |
|
|
|
|
join g2 in (from obj in uniqueObjectsPostIntern group 1 by obj.GetType()) on g.Key equals g2.Key |
|
|
|
|
join g2 in (from obj in p.uniqueObjectsPostIntern group 1 by obj.GetType()) on g.Key equals g2.Key |
|
|
|
|
orderby g.Key.FullName |
|
|
|
|
select new { Type = g.Key, PreCount = g.Count(), PostCount = g2.Count() }; |
|
|
|
|
foreach (var element in stats) { |
|
|
|
|
Console.WriteLine(element.Type + ": " + element.PostCount + "/" + element.PreCount); |
|
|
|
|
} |
|
|
|
|
Console.WriteLine(stats.Sum(r => r.PostCount * SizeOf(r.Type)) / 1024 + " KB / " |
|
|
|
|
+ stats.Sum(r => r.PreCount * SizeOf(r.Type)) / 1024 + " KB"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int SizeOf(Type t) |
|
|
|
|
{ |
|
|
|
|
if (t == typeof(string)) |
|
|
|
|
return 16; |
|
|
|
|
long start = GC.GetTotalMemory(true); |
|
|
|
|
object o = FormatterServices.GetUninitializedObject(t); |
|
|
|
|
long stop = GC.GetTotalMemory(true); |
|
|
|
|
GC.KeepAlive(o); |
|
|
|
|
return (int)Math.Max(8, stop - start); |
|
|
|
|
} |
|
|
|
|
}//*/
|
|
|
|
|
} |
|
|
|
|