提交 ddd11e47 编写于 作者: H heejaechang

added cache option and made caches to listen to them.

now cache size can be changed and dynamically configured if we want. (changeset 1308740)
上级 71b441c8
...@@ -96,6 +96,14 @@ public bool Remove(T value) ...@@ -96,6 +96,14 @@ public bool Remove(T value)
return dictionary.TryRemove(value, out b); return dictionary.TryRemove(value, out b);
} }
/// <summary>
/// Clear the set
/// </summary>
public void Clear()
{
dictionary.Clear();
}
public struct KeyEnumerator public struct KeyEnumerator
{ {
private readonly IEnumerator<KeyValuePair<T, byte>> kvpEnumerator; private readonly IEnumerator<KeyValuePair<T, byte>> kvpEnumerator;
......
...@@ -502,9 +502,9 @@ private static ValueSource<Compilation> GetCompilation(Project project, Compilat ...@@ -502,9 +502,9 @@ private static ValueSource<Compilation> GetCompilation(Project project, Compilat
return new ConstantValueSource<Compilation>(compilation); return new ConstantValueSource<Compilation>(compilation);
} }
if (project.Solution.BranchId == workspace.PrimaryBranchId && compilationCache.Primary != null) if (project.Solution.BranchId == workspace.PrimaryBranchId)
{ {
return new CachedObjectSource<Compilation>(compilation, compilationCache.Primary); return new CachedObjectSource<Compilation>(compilation, compilationCache);
} }
return new ConstantValueSource<Compilation>(compilation); return new ConstantValueSource<Compilation>(compilation);
......
// Copyright (c) Microsoft Open Technologies, Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Threading;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Options;
namespace Microsoft.CodeAnalysis.Host
{
internal abstract class AbstractCacheServiceFactory : IWorkspaceServiceFactory
{
private IWorkspaceService cache = null;
protected abstract int InitialMinimumCount { get; }
protected abstract long InitialCacheSize { get; }
protected abstract IWorkspaceService CreateCache(IOptionService service);
public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices)
{
if (cache == null)
{
var service = workspaceServices.GetService<IOptionService>();
var newCache = CreateCache(service);
Interlocked.CompareExchange(ref cache, newCache, null);
}
return cache;
}
protected void GetInitialCacheValues(IOptionService service, Option<int> minimumCountKey, Option<long> sizeKey, out int minimumCount, out long size)
{
if (service == null)
{
minimumCount = InitialMinimumCount;
size = InitialCacheSize;
}
minimumCount = service.GetOption(minimumCountKey);
size = service.GetOption(sizeKey);
}
}
}
\ No newline at end of file
// Copyright (c) Microsoft Open Technologies, Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.CodeAnalysis.Options;
namespace Microsoft.CodeAnalysis.Host
{
internal static class CacheOptions
{
public const string FeatureName = "Cache Options";
[ExportOption]
public static readonly Option<long> TextCacheSize = new Option<long>(FeatureName, "Default Text Cache Size", defaultValue: TextCacheServiceFactory.CacheSize);
[ExportOption]
public static readonly Option<int> TextCacheCount = new Option<int>(FeatureName, "Default Minimum Text Count In The Cache", defaultValue: TextCacheServiceFactory.TextCount);
[ExportOption]
public static readonly Option<long> SyntaxTreeCacheSize = new Option<long>(FeatureName, "Default SyntaxTree Cache Size", defaultValue: SyntaxTreeCacheServiceFactory.CacheSize);
[ExportOption]
public static readonly Option<int> SyntaxTreeCacheCount = new Option<int>(FeatureName, "Default Minimum SyntaxTree Count In The Cache", defaultValue: SyntaxTreeCacheServiceFactory.TreeCount);
[ExportOption]
public static readonly Option<long> CompilationCacheSize = new Option<long>(FeatureName, "Default Compilation Cache Size", defaultValue: CompilationCacheServiceFactory.CacheSize);
[ExportOption]
public static readonly Option<int> CompilationCacheCount = new Option<int>(FeatureName, "Default Minimum Compilation Count In The Cache", defaultValue: CompilationCacheServiceFactory.CompilationCount);
}
}
...@@ -2,84 +2,83 @@ ...@@ -2,84 +2,83 @@
using System; using System;
using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Options;
namespace Microsoft.CodeAnalysis.Host namespace Microsoft.CodeAnalysis.Host
{ {
[ExportWorkspaceServiceFactory(typeof(ICompilationCacheService), ServiceLayer.Default)] [ExportWorkspaceServiceFactory(typeof(ICompilationCacheService), ServiceLayer.Default)]
internal partial class CompilationCacheServiceFactory : IWorkspaceServiceFactory internal partial class CompilationCacheServiceFactory : AbstractCacheServiceFactory
{ {
public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices) public const long CacheSize = 1000;
public const int CompilationCount = 2;
protected override IWorkspaceService CreateCache(IOptionService service)
{ {
return new CompilationCacheService(); int count;
long size;
GetInitialCacheValues(service, CacheOptions.CompilationCacheCount, CacheOptions.CompilationCacheSize, out count, out size);
return new CompilationCacheService(service, count, size);
} }
private class CompilationCacheService : ICompilationCacheService protected override long InitialCacheSize
{ {
public CompilationCacheService() get
{ {
// this will basically slowly push up current cache threshold but return CacheSize;
// fastly bring it down to the normal threshold. }
this.Primary = new CompilationCache(); }
// this one will push up and down threshold fast but at the end protected override int InitialMinimumCount
// will not leave anything in the cache. {
this.Secondary = new CompilationCache( get
increment: 100, {
minimumCompilationsCount: 0, return CompilationCount;
defaultTotalCostForAllCompilations: 0,
eagarlyEvict: true);
} }
}
public ICompilationCache Primary { get; private set; } private class CompilationCacheService : CostBasedCache<Compilation>, ICompilationCacheService
public ICompilationCache Secondary { get; private set; } {
// this will make cache size to be half in about 3 seconds with 500ms buffer time.
private const double CoolingRate = 0.00006;
public void Clear() private const int Increment = 1;
private const int UpperBoundRatio = 10;
public CompilationCacheService(IOptionService service, int minCount, long minCost)
: base(service, minCount, minCost, minCost * UpperBoundRatio,
CoolingRate, Increment, TimeSpan.FromMilliseconds(500), // Keep all added compilations at least 500ms
ComputeCompilationCost, false, GetUniqueId)
{ {
this.Primary.Clear();
this.Secondary.Clear();
} }
private class CompilationCache : CostBasedCache<Compilation>, ICompilationCache protected override void OnOptionChanged(OptionChangedEventArgs e)
{ {
// this will make cache size to be half in about 3 seconds with 500ms buffer time. if (e.Option == CacheOptions.CompilationCacheCount)
private const double CoolingRate = 0.00006;
private const int Increment = 1;
private const int MinimumCompilationsCount = 2;
private const long DefaultTotalCostForAllCompilations = 1000;
private const long MaxTotalCostForAllCompilations = 10000;
internal CompilationCache(
int increment = Increment,
int minimumCompilationsCount = MinimumCompilationsCount,
long defaultTotalCostForAllCompilations = DefaultTotalCostForAllCompilations,
bool eagarlyEvict = false)
: base(
minimumCompilationsCount,
defaultTotalCostForAllCompilations,
MaxTotalCostForAllCompilations,
CoolingRate,
Increment,
TimeSpan.FromMilliseconds(500), // Keep all added compilations at least 500ms
ComputeCompilationCost,
eagarlyEvict,
GetUniqueId)
{ {
this.minCount = (int)e.Value;
} }
else if (e.Option == CacheOptions.CompilationCacheSize)
private static string GetUniqueId(Compilation compilation)
{ {
return compilation.AssemblyName; var cost = (long)e.Value;
}
private static long ComputeCompilationCost(Compilation compilation) this.minCost = cost;
{ this.maxCost = cost * UpperBoundRatio;
// having same value as cost and Increment will make cache to be too eagar on increasing its high water mark.
// now, 1:100 will make cache to increase its cache slot by 1 for every 100 cache accesses. it will basically linearly
// increase its high water mark up to 100 cache slots.
return 100;
} }
} }
private static string GetUniqueId(Compilation compilation)
{
return compilation.AssemblyName;
}
private static long ComputeCompilationCost(Compilation compilation)
{
// having same value as cost and Increment will make cache to be too eagar on increasing its high water mark.
// now, 1:100 will make cache to increase its cache slot by 1 for every 100 cache accesses. it will basically linearly
// increase its high water mark up to 100 cache slots.
return 100;
}
} }
} }
} }
\ No newline at end of file
// Copyright (c) Microsoft Open Technologies, Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
namespace Microsoft.CodeAnalysis.Host
{
internal partial class CostBasedCache<T> : IObjectCache<T> where T : class
{
// entry that holds onto various info
// not a struct since it will be stored in concurrent dictionary as a value
// and that would wrap it into a class anyways
private class CacheEntry
{
public readonly long ItemId;
public readonly int CreatedTime;
public readonly IWeakAction<T> EvictAction;
public DateTime LastTimeAccessed;
public int AccessCount;
public long Cost;
public CacheEntry(long itemId, IWeakAction<T> evictor)
{
this.ItemId = itemId;
this.CreatedTime = Environment.TickCount;
this.EvictAction = evictor;
this.LastTimeAccessed = DateTime.UtcNow;
this.AccessCount = 1;
this.Cost = UnitializedCost;
}
}
private struct SnapshotItem
{
public T Item { get; private set; }
public double Rank { get; private set; }
public CacheEntry Entry { get; private set; }
public SnapshotItem(T item, double rank, CacheEntry entry)
: this()
{
this.Item = item;
this.Rank = rank;
this.Entry = entry;
}
}
}
}
\ No newline at end of file
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.Internal.Log;
using Microsoft.CodeAnalysis.Options;
using Roslyn.Utilities; using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Host namespace Microsoft.CodeAnalysis.Host
...@@ -26,7 +27,7 @@ namespace Microsoft.CodeAnalysis.Host ...@@ -26,7 +27,7 @@ namespace Microsoft.CodeAnalysis.Host
/// ///
/// the size of the upper bound will be decreased to its normal level once those activities have ceased. /// the size of the upper bound will be decreased to its normal level once those activities have ceased.
/// </summary> /// </summary>
internal class CostBasedCache<T> : IObjectCache<T> where T : class internal partial class CostBasedCache<T> : IObjectCache<T> where T : class
{ {
// constant to indicate that cost is not evaluated yet. // constant to indicate that cost is not evaluated yet.
private const long UnitializedCost = -1; private const long UnitializedCost = -1;
...@@ -37,29 +38,6 @@ internal class CostBasedCache<T> : IObjectCache<T> where T : class ...@@ -37,29 +38,6 @@ internal class CostBasedCache<T> : IObjectCache<T> where T : class
// cache hit threshold where it would slow down cache decay rate // cache hit threshold where it would slow down cache decay rate
private const float CacheHitThreshold = 0.8F; private const float CacheHitThreshold = 0.8F;
// delegate cache to avoid allocations
private readonly Func<long, T, bool, string> logAddOrAccessed;
private readonly Func<long, int, string> logEvictedOrRemoved;
private readonly Func<T, CacheEntry, CacheEntry> updateCacheEntry;
private readonly Func<KeyValuePair<T, CacheEntry>, double> rankGetter;
private readonly Comparison<SnapshotItem> comparer;
// delegate that will return an identity of a cached item regardless of its version
private readonly Func<T, string> uniqueIdGetter;
// current cache id
private readonly int cacheId;
// evict items without waiting for next item access
private readonly bool eagarlyEvict;
// minimum number of entries to keep in the cache
private readonly int minCount;
// lower and upper bound of the cache in terms of cost
private readonly long minCost;
private readonly long maxCost;
// control variable for exponential decaying algorithm that is used to // control variable for exponential decaying algorithm that is used to
// determine current cost upper bound // determine current cost upper bound
private readonly int fixedIncrementAmount; private readonly int fixedIncrementAmount;
...@@ -79,6 +57,9 @@ internal class CostBasedCache<T> : IObjectCache<T> where T : class ...@@ -79,6 +57,9 @@ internal class CostBasedCache<T> : IObjectCache<T> where T : class
// pre-allocated list to be re-used during eviction // pre-allocated list to be re-used during eviction
private readonly List<SnapshotItem> evictionCandidateList = new List<SnapshotItem>(); private readonly List<SnapshotItem> evictionCandidateList = new List<SnapshotItem>();
// option service
private readonly IOptionService optionService;
// hold onto last time the task ran // hold onto last time the task ran
private int lastTimeTaskRan; private int lastTimeTaskRan;
...@@ -88,45 +69,29 @@ internal class CostBasedCache<T> : IObjectCache<T> where T : class ...@@ -88,45 +69,29 @@ internal class CostBasedCache<T> : IObjectCache<T> where T : class
// thread that is successful at setting the slot gets to fill it with a running task. // thread that is successful at setting the slot gets to fill it with a running task.
private Task taskSlot; private Task taskSlot;
// cache hit rate for diagnostic purpose // evict items without waiting for next item access
private float cacheHitRate; protected bool eagarlyEvict;
// entry that holds onto various info
// not a struct since it will be stored in concurrent dictionary as a value
// and that would wrap it into a class anyways
private class CacheEntry
{
public readonly long ItemId;
public readonly int CreatedTime;
public readonly IWeakAction<T> EvictAction;
public DateTime LastTimeAccessed;
public int AccessCount;
public long Cost;
public CacheEntry(long itemId, IWeakAction<T> evictor) // minimum number of entries to keep in the cache
{ protected int minCount;
this.ItemId = itemId;
this.CreatedTime = Environment.TickCount;
this.EvictAction = evictor;
this.LastTimeAccessed = DateTime.UtcNow; // lower and upper bound of the cache in terms of cost
this.AccessCount = 1; protected long minCost;
this.Cost = UnitializedCost; protected long maxCost;
}
}
protected CostBasedCache( protected CostBasedCache(
IOptionService optionService,
int minCount, long minCost, long maxCost, int minCount, long minCost, long maxCost,
double coolingRate, int fixedIncAmount, TimeSpan dataCollectionTimeSpan, double coolingRate, int fixedIncAmount, TimeSpan dataCollectionTimeSpan,
Func<T, long> costCalculator, Func<T, long> costCalculator,
Func<T, string> uniqueIdGetter) Func<T, string> uniqueIdGetter)
: this(minCount, minCost, maxCost, coolingRate, fixedIncAmount, : this(optionService, minCount, minCost, maxCost, coolingRate, fixedIncAmount,
dataCollectionTimeSpan, costCalculator, false, uniqueIdGetter) dataCollectionTimeSpan, costCalculator, false, uniqueIdGetter)
{ {
} }
protected CostBasedCache( protected CostBasedCache(
IOptionService optionService,
int minCount, long minCost, long maxCost, int minCount, long minCost, long maxCost,
double coolingRate, int fixedIncAmount, TimeSpan dataCollectionTimeSpan, double coolingRate, int fixedIncAmount, TimeSpan dataCollectionTimeSpan,
Func<T, long> costCalculator, Func<T, long> costCalculator,
...@@ -167,6 +132,10 @@ public CacheEntry(long itemId, IWeakAction<T> evictor) ...@@ -167,6 +132,10 @@ public CacheEntry(long itemId, IWeakAction<T> evictor)
this.currentCostUpperBound = minCost; this.currentCostUpperBound = minCost;
this.rankGetter = RankingAlgorithm; this.rankGetter = RankingAlgorithm;
// respond to option change
this.optionService = optionService;
this.optionService.OptionChanged += OnOptionChanged;
} }
private static double RankingAlgorithm(KeyValuePair<T, CacheEntry> item) private static double RankingAlgorithm(KeyValuePair<T, CacheEntry> item)
...@@ -359,10 +328,10 @@ private void Evict() ...@@ -359,10 +328,10 @@ private void Evict()
ResetEvictionStates(); ResetEvictionStates();
EagarlyEvict(); EvictEagarly();
} }
private void EagarlyEvict() private void EvictEagarly()
{ {
if (!this.eagarlyEvict || this.taskSlot != null || items.Count <= minCount) if (!this.eagarlyEvict || this.taskSlot != null || items.Count <= minCount)
{ {
...@@ -389,41 +358,6 @@ private void EagarlyEvict() ...@@ -389,41 +358,6 @@ private void EagarlyEvict()
} }
} }
private void UpdateCacheHitRate(List<SnapshotItem> cacheItems)
{
var cacheHit = 0;
for (int i = 0; i < cacheItems.Count; i++)
{
var item = cacheItems[i];
if (item.Entry.CreatedTime < this.lastTimeTaskRan)
{
cacheHit++;
}
}
// item that has survived last eviction vs new item added after the last eviction
this.cacheHitRate = (float)cacheHit / Math.Max(1, cacheItems.Count);
}
private void LogCacheRankInformation(List<SnapshotItem> snapshot)
{
#if false
// log everything inside of the cache for diagnostic purpose
var now = Environment.TickCount;
Func<SnapshotItem, string> logMessage = i =>
{
return string.Join(",", now, this.cacheId, i.Rank, i.Entry.AccessCount, i.Entry.CreatedTime, i.Entry.LastTimeAccessed, i.Entry.ItemId, this.uniqueIdGetter(i.Item));
};
foreach (var item in snapshot)
{
Logger.Log(FeatureId.Cache, FunctionId.Cache_ItemRank, logMessage, item);
}
#endif
}
private int GetIndexAndCostToDelete(double costUpperBound, List<SnapshotItem> snapshot, out long cost) private int GetIndexAndCostToDelete(double costUpperBound, List<SnapshotItem> snapshot, out long cost)
{ {
cost = 0L; cost = 0L;
...@@ -518,70 +452,14 @@ public void Clear() ...@@ -518,70 +452,14 @@ public void Clear()
} }
} }
private string LogAddOrAccessed(long itemId, T item, bool cacheHit) private void OnOptionChanged(object sender, OptionChangedEventArgs e)
{
return string.Join(",", cacheId, itemId, item.GetType(), this.uniqueIdGetter(item), cacheHit);
}
private string LogEvictedOrRemoved(long itemId, int accessCount)
{
return string.Join(",", cacheId, itemId, accessCount);
}
private struct SnapshotItem
{
public T Item { get; private set; }
public double Rank { get; private set; }
public CacheEntry Entry { get; private set; }
public SnapshotItem(T item, double rank, CacheEntry entry)
: this()
{
this.Item = item;
this.Rank = rank;
this.Entry = entry;
}
}
// internal information to show in diagnostic
internal long CurrentCostUpperBound
{
get
{
return this.currentCostUpperBound;
}
}
internal int CurrentItemCount
{ {
get OnOptionChanged(e);
{
return this.items.Count;
}
} }
internal long MinCost protected virtual void OnOptionChanged(OptionChangedEventArgs e)
{ {
get // do nothing
{
return this.minCost;
}
}
internal long MaxCost
{
get
{
return this.maxCost;
}
}
internal float CacheHitRate
{
get
{
return this.cacheHitRate;
}
} }
} }
} }
\ No newline at end of file
// Copyright (c) Microsoft Open Technologies, Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
namespace Microsoft.CodeAnalysis.Host
{
internal partial class CostBasedCache<T> : IObjectCache<T> where T : class
{
// delegate cache to avoid allocations
private readonly Func<long, T, bool, string> logAddOrAccessed;
private readonly Func<long, int, string> logEvictedOrRemoved;
private readonly Func<T, CacheEntry, CacheEntry> updateCacheEntry;
private readonly Func<KeyValuePair<T, CacheEntry>, double> rankGetter;
private readonly Comparison<SnapshotItem> comparer;
// delegate that will return an identity of a cached item regardless of its version
private readonly Func<T, string> uniqueIdGetter;
// current cache id
private readonly int cacheId;
// cache hit rate for diagnostic purpose
private float cacheHitRate;
private void UpdateCacheHitRate(List<SnapshotItem> cacheItems)
{
var cacheHit = 0;
for (int i = 0; i < cacheItems.Count; i++)
{
var item = cacheItems[i];
if (item.Entry.CreatedTime < this.lastTimeTaskRan)
{
cacheHit++;
}
}
// item that has survived last eviction vs new item added after the last eviction
this.cacheHitRate = (float)cacheHit / Math.Max(1, cacheItems.Count);
}
private void LogCacheRankInformation(List<SnapshotItem> snapshot)
{
#if false
// log everything inside of the cache for diagnostic purpose
var now = Environment.TickCount;
Func<SnapshotItem, string> logMessage = i =>
{
return string.Join(",", now, this.cacheId, i.Rank, i.Entry.AccessCount, i.Entry.CreatedTime, i.Entry.LastTimeAccessed, i.Entry.ItemId, this.uniqueIdGetter(i.Item));
};
foreach (var item in snapshot)
{
Logger.Log(FeatureId.Cache, FunctionId.Cache_ItemRank, logMessage, item);
}
#endif
}
private string LogAddOrAccessed(long itemId, T item, bool cacheHit)
{
return string.Join(",", cacheId, itemId, item.GetType(), this.uniqueIdGetter(item), cacheHit);
}
private string LogEvictedOrRemoved(long itemId, int accessCount)
{
return string.Join(",", cacheId, itemId, accessCount);
}
// internal information to show in diagnostic
internal long CurrentCostUpperBound
{
get
{
return this.currentCostUpperBound;
}
}
internal int CurrentItemCount
{
get
{
return this.items.Count;
}
}
internal long MinimumCost
{
get
{
return this.minCost;
}
}
internal long MaximumCost
{
get
{
return this.maxCost;
}
}
internal float CacheHitRate
{
get
{
return this.cacheHitRate;
}
}
}
}
\ No newline at end of file
// Copyright (c) Microsoft Open Technologies, Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.CodeAnalysis.Host
{
internal interface ICompilationCache : IObjectCache<Compilation>
{
}
}
// Copyright (c) Microsoft Open Technologies, Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Copyright (c) Microsoft Open Technologies, Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.CodeAnalysis.Text;
namespace Microsoft.CodeAnalysis.Host namespace Microsoft.CodeAnalysis.Host
{ {
internal interface ICompilationCacheService : IWorkspaceService internal interface ICompilationCacheService : IObjectCache<Compilation>, IWorkspaceService
{ {
/// <summary>
/// compilation cache for main branch
/// </summary>
ICompilationCache Primary { get; }
/// <summary>
/// compilation cache for all other branches which could include in progress main branch compilation
/// </summary>
ICompilationCache Secondary { get; }
/// <summary>
/// clear compilation caches belong to this workspace
/// </summary>
void Clear();
} }
} }
...@@ -2,19 +2,40 @@ ...@@ -2,19 +2,40 @@
using System; using System;
using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Options;
namespace Microsoft.CodeAnalysis.Host namespace Microsoft.CodeAnalysis.Host
{ {
[ExportWorkspaceServiceFactory(typeof(ISyntaxTreeCacheService), ServiceLayer.Default)] [ExportWorkspaceServiceFactory(typeof(ISyntaxTreeCacheService), ServiceLayer.Default)]
internal partial class SyntaxTreeCacheServiceFactory : IWorkspaceServiceFactory internal partial class SyntaxTreeCacheServiceFactory : AbstractCacheServiceFactory
{ {
// 4M chars * 2bytes/char = 8MB of raw text, with some syntax nodes deduplicated. // 4M chars * 2bytes/char = 8MB of raw text, with some syntax nodes deduplicated.
private const long DefaultSize = 4 * 1024 * 1024; public const long CacheSize = 4 * 1024 * 1024;
private const int DefaultTextCount = 8; public const int TreeCount = 8;
public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices) protected override IWorkspaceService CreateCache(IOptionService service)
{ {
return new SyntaxTreeCacheService(DefaultTextCount, DefaultSize, tv => tv.FullSpan.Length); int count;
long size;
GetInitialCacheValues(service, CacheOptions.SyntaxTreeCacheCount, CacheOptions.SyntaxTreeCacheSize, out count, out size);
return new SyntaxTreeCacheService(service, count, size, tv => tv.FullSpan.Length);
}
protected override long InitialCacheSize
{
get
{
return CacheSize;
}
}
protected override int InitialMinimumCount
{
get
{
return TreeCount;
}
} }
private class SyntaxTreeCacheService : CostBasedCache<SyntaxNode>, ISyntaxTreeCacheService private class SyntaxTreeCacheService : CostBasedCache<SyntaxNode>, ISyntaxTreeCacheService
...@@ -34,12 +55,27 @@ private class SyntaxTreeCacheService : CostBasedCache<SyntaxNode>, ISyntaxTreeCa ...@@ -34,12 +55,27 @@ private class SyntaxTreeCacheService : CostBasedCache<SyntaxNode>, ISyntaxTreeCa
private static readonly Func<SyntaxNode, string> uniqueIdGetter = private static readonly Func<SyntaxNode, string> uniqueIdGetter =
t => t.SyntaxTree == null ? string.Empty : t.SyntaxTree.FilePath; t => t.SyntaxTree == null ? string.Empty : t.SyntaxTree.FilePath;
public SyntaxTreeCacheService(int minCount, long minCost, Func<SyntaxNode, long> itemCost) public SyntaxTreeCacheService(IOptionService service, int minCount, long minCost, Func<SyntaxNode, long> itemCost)
: base(minCount, minCost, minCost * UpperBoundRatio, : base(service, minCount, minCost, minCost * UpperBoundRatio,
CoolingRate, Increment, TimeSpan.FromMilliseconds(500), CoolingRate, Increment, TimeSpan.FromMilliseconds(500),
itemCost, uniqueIdGetter) itemCost, uniqueIdGetter)
{ {
} }
protected override void OnOptionChanged(OptionChangedEventArgs e)
{
if (e.Option == CacheOptions.SyntaxTreeCacheCount)
{
this.minCount = (int)e.Value;
}
else if (e.Option == CacheOptions.SyntaxTreeCacheSize)
{
var cost = (long)e.Value;
this.minCost = cost;
this.maxCost = cost * UpperBoundRatio;
}
}
} }
} }
} }
\ No newline at end of file
...@@ -2,19 +2,40 @@ ...@@ -2,19 +2,40 @@
using System; using System;
using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Options;
namespace Microsoft.CodeAnalysis.Host namespace Microsoft.CodeAnalysis.Host
{ {
[ExportWorkspaceServiceFactory(typeof(ITextCacheService), ServiceLayer.Default)] [ExportWorkspaceServiceFactory(typeof(ITextCacheService), ServiceLayer.Default)]
internal partial class TextCacheServiceFactory : IWorkspaceServiceFactory internal partial class TextCacheServiceFactory : AbstractCacheServiceFactory
{ {
// 4M chars * 2bytes/char = 8 MB // 1M chars * 2bytes/char = 2 MB
private const long DefaultSize = 1 << 20; public const long CacheSize = 1 << 20;
private const int DefaultTextCount = 8; public const int TextCount = 8;
public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices) protected override IWorkspaceService CreateCache(IOptionService service)
{ {
return new TextCacheService(DefaultTextCount, DefaultSize, itemCost: tv => tv.Text.Length); int count;
long size;
GetInitialCacheValues(service, CacheOptions.TextCacheCount, CacheOptions.TextCacheSize, out count, out size);
return new TextCacheService(service, count, size, itemCost: tv => tv.Text.Length);
}
protected override long InitialCacheSize
{
get
{
return CacheSize;
}
}
protected override int InitialMinimumCount
{
get
{
return TextCount;
}
} }
private class TextCacheService : CostBasedCache<TextAndVersion>, ITextCacheService private class TextCacheService : CostBasedCache<TextAndVersion>, ITextCacheService
...@@ -33,10 +54,25 @@ private class TextCacheService : CostBasedCache<TextAndVersion>, ITextCacheServi ...@@ -33,10 +54,25 @@ private class TextCacheService : CostBasedCache<TextAndVersion>, ITextCacheServi
private static readonly Func<TextAndVersion, string> uniqueIdGetter = tv => tv.FilePath; private static readonly Func<TextAndVersion, string> uniqueIdGetter = tv => tv.FilePath;
public TextCacheService(int minCount, long minCost, Func<TextAndVersion, long> itemCost) public TextCacheService(IOptionService service, int minCount, long minCost, Func<TextAndVersion, long> itemCost)
: base(minCount, minCost, minCost * UpperBoundRatio, CoolingRate, Increment, TimeSpan.FromMilliseconds(200), itemCost, uniqueIdGetter) : base(service, minCount, minCost, minCost * UpperBoundRatio, CoolingRate, Increment, TimeSpan.FromMilliseconds(200), itemCost, uniqueIdGetter)
{ {
} }
protected override void OnOptionChanged(OptionChangedEventArgs e)
{
if (e.Option == CacheOptions.TextCacheCount)
{
this.minCount = (int)e.Value;
}
else if (e.Option == CacheOptions.TextCacheSize)
{
var cost = (long)e.Value;
this.minCost = cost;
this.maxCost = cost * UpperBoundRatio;
}
}
} }
} }
} }
...@@ -728,28 +728,9 @@ internal MetadataReference GetPartialMetadataReference(Solution solution, Projec ...@@ -728,28 +728,9 @@ internal MetadataReference GetPartialMetadataReference(Solution solution, Projec
/// </summary> /// </summary>
private ValueSource<Compilation> Retain(Solution solution, Compilation compilation) private ValueSource<Compilation> Retain(Solution solution, Compilation compilation)
{ {
var caches = solution.Services.CompilationCacheService; var cache = solution.Services.CompilationCacheService;
if (caches != null) if (cache != null)
{ {
// If the solution supports compilation caches, get the appropriate compilation cache.
// This will ensure the primary compilation cache is not polluted by items from a branched solution.
//
// The secondary cache could get an inProgress compilation that belongs to the primary branch, but as
// soon as the background compiler brings those compilations to their final state, it will be moved to the
// primary cache.
//
// Another case is if a compilation is brought to a final state by a branched solution. The final compilation
// could be in a secondary cache.
//
// We can add a bit more information in the compilation tracker and fork compilation tracker a bit more to let
// it know which branch the compilation tracker belongs to.
//
// But all those cases where a compilation goes to the secondary cache should be either temporary or about ones
// the editor currently doesn't use (no opened file from the compilation).
//
// So, until we have evidence to the contrary, let's leave the logic simple.
var cache = (solution.BranchId == solution.Workspace.PrimaryBranchId) ? caches.Primary : caches.Secondary;
return new CachedObjectSource<Compilation>(compilation, cache); return new CachedObjectSource<Compilation>(compilation, cache);
} }
else else
......
...@@ -350,6 +350,10 @@ ...@@ -350,6 +350,10 @@
<Compile Include="CodeGeneration\SyntaxAnnotationExtensions.cs" /> <Compile Include="CodeGeneration\SyntaxAnnotationExtensions.cs" />
<Compile Include="CodeGeneration\TypeGenerator.cs" /> <Compile Include="CodeGeneration\TypeGenerator.cs" />
<Compile Include="Utilities\WeakEventHandler.cs" /> <Compile Include="Utilities\WeakEventHandler.cs" />
<Compile Include="Workspace\Host\Caching\AbstractCacheServiceFactory.cs" />
<Compile Include="Workspace\Host\Caching\CacheOptions.cs" />
<Compile Include="Workspace\Host\Caching\CostBasedCache.Nested.cs" />
<Compile Include="Workspace\Host\Caching\CostBasedCache_Logging.cs" />
<Compile Include="Workspace\Host\Mef\DiagnosticProviderMetadata.cs" /> <Compile Include="Workspace\Host\Mef\DiagnosticProviderMetadata.cs" />
<Compile Include="Diagnostics\ExportDiagnosticAnalyzerAttribute.cs" /> <Compile Include="Diagnostics\ExportDiagnosticAnalyzerAttribute.cs" />
<Compile Include="Differencing\Edit.cs" /> <Compile Include="Differencing\Edit.cs" />
...@@ -801,7 +805,6 @@ ...@@ -801,7 +805,6 @@
<Compile Include="Workspace\Host\Caching\CacheId.cs" /> <Compile Include="Workspace\Host\Caching\CacheId.cs" />
<Compile Include="Workspace\Host\Caching\CompilationCacheServiceFactory.cs" /> <Compile Include="Workspace\Host\Caching\CompilationCacheServiceFactory.cs" />
<Compile Include="Workspace\Host\Caching\CostBasedCache.cs" /> <Compile Include="Workspace\Host\Caching\CostBasedCache.cs" />
<Compile Include="Workspace\Host\Caching\ICompilationCache.cs" />
<Compile Include="Workspace\Host\Caching\ICompilationCacheService.cs" /> <Compile Include="Workspace\Host\Caching\ICompilationCacheService.cs" />
<Compile Include="Workspace\Host\Caching\IObjectCache.cs" /> <Compile Include="Workspace\Host\Caching\IObjectCache.cs" />
<Compile Include="Workspace\Host\Caching\ISyntaxTreeCacheService.cs" /> <Compile Include="Workspace\Host\Caching\ISyntaxTreeCacheService.cs" />
......
// Copyright (c) Microsoft Open Technologies, Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Copyright (c) Microsoft Open Technologies, Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Text;
namespace Microsoft.CodeAnalysis.UnitTests namespace Microsoft.CodeAnalysis.UnitTests
{ {
...@@ -17,33 +15,14 @@ public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices) ...@@ -17,33 +15,14 @@ public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices)
internal class CacheService : ICompilationCacheService internal class CacheService : ICompilationCacheService
{ {
private readonly Cache cache = new Cache(); public void AddOrAccess(Compilation instance, IWeakAction<Compilation> evictor)
public ICompilationCache Primary
{
get { return this.cache; }
}
public ICompilationCache Secondary
{ {
get { return this.cache; } evictor.Invoke(instance);
} }
public void Clear() public void Clear()
{ {
} }
private class Cache : ICompilationCache
{
public void AddOrAccess(Compilation instance, IWeakAction<Compilation> evictor)
{
evictor.Invoke(instance);
}
public void Clear()
{
}
}
} }
} }
} }
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册