Modify FileResolver / bytestore to keep track of invalidated cache elements.

Change-Id: I60cb1f5f04eb9a1ee34c50157d5ed2da92d408ec
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/167861
Commit-Queue: Keerti Parthasarathy <keertip@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
This commit is contained in:
Keerti Parthasarathy 2020-10-16 00:17:31 +00:00 committed by commit-bot@chromium.org
parent 5a28f14a25
commit fdad84fa5b
3 changed files with 70 additions and 21 deletions

View file

@ -5,6 +5,13 @@
import 'package:analyzer/src/dart/analysis/cache.dart';
import 'package:collection/collection.dart';
class CacheData {
final int id;
final List<int> bytes;
CacheData(this.id, this.bytes);
}
/// Store of bytes associated with string keys and a hash.
///
/// Each key must be not longer than 100 characters and consist of only `[a-z]`,
@ -18,39 +25,52 @@ abstract class CiderByteStore {
/// [signature].
///
/// Return `null` if the association does not exist.
List<int> get(String key, List<int> signature);
CacheData get(String key, List<int> signature);
/// Associate the given [bytes] with the [key] and [digest].
void put(String key, List<int> signature, List<int> bytes);
/// Associate the given [bytes] with the [key] and [signature]. Return the
/// [CacheData].
CacheData putGet(String key, List<int> signature, List<int> bytes);
/// Used to decrement reference count for the given ids, if implemented.
void release(Iterable<int> ids);
}
class CiderCachedByteStore implements CiderByteStore {
final Cache<String, CiderCacheEntry> _cache;
int idCounter = 0;
CiderCachedByteStore(int maxCacheSize)
: _cache =
Cache<String, CiderCacheEntry>(maxCacheSize, (v) => v.bytes.length);
: _cache = Cache<String, CiderCacheEntry>(
maxCacheSize, (v) => v.data.bytes.length);
@override
List<int> get(String key, List<int> signature) {
CacheData get(String key, List<int> signature) {
var entry = _cache.get(key, () => null);
if (entry != null &&
const ListEquality<int>().equals(entry.signature, signature)) {
return entry.bytes;
return entry.data;
}
return null;
}
@override
void put(String key, List<int> signature, List<int> bytes) {
_cache.put(key, CiderCacheEntry(signature, bytes));
CacheData putGet(String key, List<int> signature, List<int> bytes) {
idCounter++;
var entry = CiderCacheEntry(signature, CacheData(idCounter, bytes));
_cache.put(key, entry);
return entry.data;
}
@override
void release(Iterable<int> ids) {
// do nothing
}
}
class CiderCacheEntry {
final CacheData data;
final List<int> signature;
final List<int> bytes;
CiderCacheEntry(this.signature, this.bytes);
CiderCacheEntry(this.signature, this.data);
}

View file

@ -90,6 +90,9 @@ class FileState {
UnlinkedUnit2 unlinked2;
LibraryCycle _libraryCycle;
/// id of the cache entry.
int id;
FileState._(
this._fsState,
this.path,
@ -219,7 +222,8 @@ class FileState {
// Prepare bytes of the unlinked bundle - existing or new.
List<int> bytes;
{
bytes = _fsState._byteStore.get(unlinkedKey, _digest);
var cacheData = _fsState._byteStore.get(unlinkedKey, _digest);
bytes = cacheData?.bytes;
if (bytes == null || bytes.isEmpty) {
var content = performance.run('content', (_) {
@ -236,7 +240,8 @@ class FileState {
var unlinkedBuilder = serializeAstCiderUnlinked(_digest, unit);
bytes = unlinkedBuilder.toBuffer();
performance.getDataInt('length').add(bytes.length);
_fsState._byteStore.put(unlinkedKey, _digest, bytes);
cacheData = _fsState._byteStore.putGet(unlinkedKey, _digest, bytes);
bytes = cacheData.bytes;
});
performance.run('prefetch', (_) {
@ -244,6 +249,7 @@ class FileState {
_prefetchDirectReferences(unlinked2);
});
}
id = cacheData.id;
}
// Read the unlinked bundle.
@ -645,6 +651,9 @@ class LibraryCycle {
/// The hash of all the paths of the files in this cycle.
String cyclePathsHash;
/// id of the cache entry.
int id;
LibraryCycle();
String get signatureStr {

View file

@ -83,6 +83,11 @@ class FileResolver {
_LibraryContext libraryContext;
/// List of ids for cache elements that are invalidated. Track elements that
/// are invalidated during [changeFile]. Used in [releaseAndClearRemovedIds] to
/// release the cache items and is then cleared.
final Set<int> removedCacheIds = {};
FileResolver(
PerformanceLog logger,
ResourceProvider resourceProvider,
@ -132,7 +137,8 @@ class FileResolver {
/// Update the resolver to reflect the fact that the file with the given
/// [path] was changed. We need to make sure that when this file, of any file
/// that directly or indirectly referenced it, is resolved, we used the new
/// state of the file.
/// state of the file. Updates [removedCacheIds] with the ids of the invalidated
/// items, used in [releaseAndClearRemovedIds] to release the cache items.
void changeFile(String path) {
if (fsState == null) {
return;
@ -149,12 +155,13 @@ class FileResolver {
path: removedFile.path,
uri: removedFile.uri,
);
removedCacheIds.add(removedFile.id);
}
// Remove libraries represented by removed files.
// If we need these libraries later, we will relink and reattach them.
if (libraryContext != null) {
libraryContext.remove(removedFiles);
libraryContext.remove(removedFiles, removedCacheIds);
}
}
@ -184,7 +191,7 @@ class FileResolver {
var errorsSignature = errorsSignatureBuilder.toByteList();
var errorsKey = file.path + '.errors';
var bytes = byteStore.get(errorsKey, errorsSignature);
var bytes = byteStore.get(errorsKey, errorsSignature)?.bytes;
List<AnalysisError> errors;
if (bytes != null) {
var data = CiderUnitErrors.fromBuffer(bytes);
@ -204,7 +211,7 @@ class FileResolver {
signature: errorsSignature,
errors: errors.map(ErrorEncoding.encode).toList(),
).toBuffer();
byteStore.put(errorsKey, errorsSignature, bytes);
bytes = byteStore.putGet(errorsKey, errorsSignature, bytes).bytes;
}
return ErrorsResultImpl(
@ -314,6 +321,12 @@ class FileResolver {
_resetContextObjects();
}
/// Update the cache with list of invalidated ids and clears [removedCacheIds].
void releaseAndClearRemovedIds() {
byteStore.release(removedCacheIds);
removedCacheIds.clear();
}
/// The [completionLine] and [completionColumn] are zero based.
ResolvedUnitResult resolve({
int completionLine,
@ -680,7 +693,8 @@ class _LibraryContext {
cycle.directDependencies.forEach(loadBundle);
var key = cycle.cyclePathsHash;
var bytes = byteStore.get(key, cycle.signature);
var data = byteStore.get(key, cycle.signature);
var bytes = data?.bytes;
if (bytes == null) {
librariesLinkedTimer.start();
@ -732,7 +746,8 @@ class _LibraryContext {
bytes = serializeBundle(cycle.signature, linkResult).toBuffer();
byteStore.put(key, cycle.signature, bytes);
data = byteStore.putGet(key, cycle.signature, bytes);
bytes = data.bytes;
performance.getDataInt('bytesPut').add(bytes.length);
librariesLinkedTimer.stop();
@ -740,6 +755,7 @@ class _LibraryContext {
performance.getDataInt('bytesGet').add(bytes.length);
performance.getDataInt('libraryLoadCount').add(cycle.libraries.length);
}
cycle.id = data.id;
var cBundle = CiderLinkedLibraryCycle.fromBuffer(bytes);
inputBundles.add(cBundle.bundle);
@ -775,14 +791,18 @@ class _LibraryContext {
/// Remove libraries represented by the [removed] files.
/// If we need these libraries later, we will relink and reattach them.
void remove(List<FileState> removed) {
void remove(List<FileState> removed, Set<int> removedIds) {
elementFactory.removeLibraries(
removed.map((e) => e.uriStr).toSet(),
);
var removedSet = removed.toSet();
loadedBundles.removeWhere((cycle) {
return cycle.libraries.any(removedSet.contains);
if (cycle.libraries.any(removedSet.contains)) {
removedIds.add(cycle.id);
return true;
}
return false;
});
}