optimize DocumentStore garbage collection (#969)
This commit is contained in:
parent
66b7d7ad58
commit
5a78cf1e50
@ -78,6 +78,9 @@ pub const Handle = struct {
|
|||||||
}
|
}
|
||||||
self.import_uris.deinit(allocator);
|
self.import_uris.deinit(allocator);
|
||||||
|
|
||||||
|
for (self.cimports.items(.source)) |source| {
|
||||||
|
allocator.free(source);
|
||||||
|
}
|
||||||
self.cimports.deinit(allocator);
|
self.cimports.deinit(allocator);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -208,16 +211,26 @@ pub fn refreshDocument(self: *DocumentStore, uri: Uri, new_text: [:0]const u8) !
|
|||||||
for (handle.import_uris.items) |import_uri| {
|
for (handle.import_uris.items) |import_uri| {
|
||||||
self.allocator.free(import_uri);
|
self.allocator.free(import_uri);
|
||||||
}
|
}
|
||||||
|
const old_import_count = handle.import_uris.items.len;
|
||||||
|
const new_import_count = new_import_uris.items.len;
|
||||||
handle.import_uris.deinit(self.allocator);
|
handle.import_uris.deinit(self.allocator);
|
||||||
handle.import_uris = new_import_uris;
|
handle.import_uris = new_import_uris;
|
||||||
|
|
||||||
var new_cimports = try self.collectCIncludes(handle.*);
|
var new_cimports = try self.collectCIncludes(handle.*);
|
||||||
|
const old_cimport_count = handle.cimports.len;
|
||||||
|
const new_cimport_count = new_cimports.len;
|
||||||
|
for (handle.cimports.items(.source)) |source| {
|
||||||
|
self.allocator.free(source);
|
||||||
|
}
|
||||||
handle.cimports.deinit(self.allocator);
|
handle.cimports.deinit(self.allocator);
|
||||||
handle.cimports = new_cimports;
|
handle.cimports = new_cimports;
|
||||||
|
|
||||||
// a include could have been removed but it would increase latency
|
if (old_import_count != new_import_count or
|
||||||
// try self.garbageCollectionImports();
|
old_cimport_count != new_cimport_count)
|
||||||
// try self.garbageCollectionCImports();
|
{
|
||||||
|
self.garbageCollectionImports() catch {};
|
||||||
|
self.garbageCollectionCImports() catch {};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn applySave(self: *DocumentStore, handle: *const Handle) !void {
|
pub fn applySave(self: *DocumentStore, handle: *const Handle) !void {
|
||||||
@ -238,7 +251,7 @@ pub fn applySave(self: *DocumentStore, handle: *const Handle) !void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The `DocumentStore` represents a graph structure where every
|
/// The `DocumentStore` represents a graph structure where every
|
||||||
/// handle/document is a node and every `@import` & `@cImport` represent
|
/// handle/document is a node and every `@import` and `@cImport` represent
|
||||||
/// a directed edge.
|
/// a directed edge.
|
||||||
/// We can remove every document which cannot be reached from
|
/// We can remove every document which cannot be reached from
|
||||||
/// another document that is `open` (see `Handle.open`)
|
/// another document that is `open` (see `Handle.open`)
|
||||||
@ -249,97 +262,103 @@ fn garbageCollectionImports(self: *DocumentStore) error{OutOfMemory}!void {
|
|||||||
var arena = std.heap.ArenaAllocator.init(self.allocator);
|
var arena = std.heap.ArenaAllocator.init(self.allocator);
|
||||||
defer arena.deinit();
|
defer arena.deinit();
|
||||||
|
|
||||||
var reachable_handles = std.StringHashMapUnmanaged(void){};
|
var reachable = try std.DynamicBitSetUnmanaged.initEmpty(arena.allocator(), self.handles.count());
|
||||||
defer reachable_handles.deinit(arena.allocator());
|
|
||||||
|
|
||||||
var queue = std.ArrayListUnmanaged(Uri){};
|
var queue = std.ArrayListUnmanaged(Uri){};
|
||||||
|
|
||||||
for (self.handles.values()) |handle| {
|
for (self.handles.values()) |handle, handle_index| {
|
||||||
if (!handle.open) continue;
|
if (!handle.open) continue;
|
||||||
|
|
||||||
try reachable_handles.put(arena.allocator(), handle.uri, {});
|
reachable.set(handle_index);
|
||||||
|
|
||||||
try self.collectDependencies(arena.allocator(), handle.*, &queue);
|
try self.collectDependencies(arena.allocator(), handle.*, &queue);
|
||||||
}
|
}
|
||||||
|
|
||||||
while (queue.popOrNull()) |uri| {
|
while (queue.popOrNull()) |uri| {
|
||||||
const gop = try reachable_handles.getOrPut(arena.allocator(), uri);
|
const handle_index = self.handles.getIndex(uri) orelse continue;
|
||||||
if (gop.found_existing) continue;
|
if (reachable.isSet(handle_index)) continue;
|
||||||
|
reachable.set(handle_index);
|
||||||
|
|
||||||
const handle = self.handles.get(uri) orelse continue;
|
const handle = self.handles.values()[handle_index];
|
||||||
|
|
||||||
try self.collectDependencies(arena.allocator(), handle.*, &queue);
|
try self.collectDependencies(arena.allocator(), handle.*, &queue);
|
||||||
}
|
}
|
||||||
|
|
||||||
var i: usize = 0;
|
var it = reachable.iterator(.{
|
||||||
while (i < self.handles.count()) {
|
.kind = .unset,
|
||||||
const handle = self.handles.values()[i];
|
.direction = .reverse,
|
||||||
if (reachable_handles.contains(handle.uri)) {
|
});
|
||||||
i += 1;
|
|
||||||
continue;
|
while (it.next()) |handle_index| {
|
||||||
}
|
const handle = self.handles.values()[handle_index];
|
||||||
log.debug("Closing document {s}", .{handle.uri});
|
log.debug("Closing document {s}", .{handle.uri});
|
||||||
var kv = self.handles.fetchSwapRemove(handle.uri).?;
|
self.handles.swapRemoveAt(handle_index);
|
||||||
kv.value.deinit(self.allocator);
|
handle.deinit(self.allocator);
|
||||||
self.allocator.destroy(kv.value);
|
self.allocator.destroy(handle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// see `garbageCollectionImports`
|
||||||
fn garbageCollectionCImports(self: *DocumentStore) error{OutOfMemory}!void {
|
fn garbageCollectionCImports(self: *DocumentStore) error{OutOfMemory}!void {
|
||||||
const tracy_zone = tracy.trace(@src());
|
const tracy_zone = tracy.trace(@src());
|
||||||
defer tracy_zone.end();
|
defer tracy_zone.end();
|
||||||
|
|
||||||
if (self.cimports.count() == 0) return;
|
if (self.cimports.count() == 0) return;
|
||||||
|
|
||||||
var reachable_hashes = std.AutoArrayHashMapUnmanaged(Hash, void){};
|
var reachable = try std.DynamicBitSetUnmanaged.initEmpty(self.allocator, self.cimports.count());
|
||||||
defer reachable_hashes.deinit(self.allocator);
|
defer reachable.deinit(self.allocator);
|
||||||
|
|
||||||
for (self.handles.values()) |handle| {
|
for (self.handles.values()) |handle| {
|
||||||
for (handle.cimports.items(.hash)) |hash| {
|
for (handle.cimports.items(.hash)) |hash| {
|
||||||
try reachable_hashes.put(self.allocator, hash, {});
|
const index = self.cimports.getIndex(hash).?;
|
||||||
|
reachable.set(index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var i: usize = 0;
|
var it = reachable.iterator(.{
|
||||||
while (i < self.cimports.count()) {
|
.kind = .unset,
|
||||||
const hash = self.cimports.keys()[i];
|
.direction = .reverse,
|
||||||
if (reachable_hashes.contains(hash)) {
|
});
|
||||||
i += 1;
|
|
||||||
continue;
|
while (it.next()) |cimport_index| {
|
||||||
}
|
var result = self.cimports.values()[cimport_index];
|
||||||
var kv = self.cimports.fetchSwapRemove(hash).?;
|
const message = switch (result) {
|
||||||
const message = switch (kv.value) {
|
|
||||||
.failure => "",
|
.failure => "",
|
||||||
.success => |uri| uri,
|
.success => |uri| uri,
|
||||||
};
|
};
|
||||||
log.debug("Destroying cimport {s}", .{message});
|
log.debug("Destroying cimport {s}", .{message});
|
||||||
kv.value.deinit(self.allocator);
|
self.cimports.swapRemoveAt(cimport_index);
|
||||||
|
result.deinit(self.allocator);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// see `garbageCollectionImports`
|
||||||
fn garbageCollectionBuildFiles(self: *DocumentStore) error{OutOfMemory}!void {
|
fn garbageCollectionBuildFiles(self: *DocumentStore) error{OutOfMemory}!void {
|
||||||
const tracy_zone = tracy.trace(@src());
|
const tracy_zone = tracy.trace(@src());
|
||||||
defer tracy_zone.end();
|
defer tracy_zone.end();
|
||||||
|
|
||||||
var reachable_build_files = std.StringHashMapUnmanaged(void){};
|
if (self.build_files.count() == 0) return;
|
||||||
defer reachable_build_files.deinit(self.allocator);
|
|
||||||
|
var reachable = try std.DynamicBitSetUnmanaged.initEmpty(self.allocator, self.build_files.count());
|
||||||
|
defer reachable.deinit(self.allocator);
|
||||||
|
|
||||||
for (self.handles.values()) |handle| {
|
for (self.handles.values()) |handle| {
|
||||||
const build_file_uri = handle.associated_build_file orelse continue;
|
const build_file_uri = handle.associated_build_file orelse continue;
|
||||||
|
const build_file_index = self.build_files.getIndex(build_file_uri).?;
|
||||||
|
|
||||||
try reachable_build_files.put(self.allocator, build_file_uri, {});
|
reachable.set(build_file_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
var i: usize = 0;
|
var it = reachable.iterator(.{
|
||||||
while (i < self.build_files.count()) {
|
.kind = .unset,
|
||||||
const hash = self.build_files.keys()[i];
|
.direction = .reverse,
|
||||||
if (reachable_build_files.contains(hash)) {
|
});
|
||||||
i += 1;
|
|
||||||
continue;
|
while (it.next()) |build_file_index| {
|
||||||
}
|
var build_file = self.build_files.values()[build_file_index];
|
||||||
var kv = self.build_files.fetchSwapRemove(hash).?;
|
log.debug("Destroying build file {s}", .{build_file.uri});
|
||||||
log.debug("Destroying build file {s}", .{kv.value.uri});
|
self.build_files.swapRemoveAt(build_file_index);
|
||||||
kv.value.deinit(self.allocator);
|
build_file.deinit(self.allocator);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -811,12 +830,11 @@ pub fn collectDependencies(
|
|||||||
const tracy_zone = tracy.trace(@src());
|
const tracy_zone = tracy.trace(@src());
|
||||||
defer tracy_zone.end();
|
defer tracy_zone.end();
|
||||||
|
|
||||||
try dependencies.ensureUnusedCapacity(allocator, handle.import_uris.items.len);
|
try dependencies.ensureUnusedCapacity(allocator, handle.import_uris.items.len + handle.cimports.len);
|
||||||
for (handle.import_uris.items) |uri| {
|
for (handle.import_uris.items) |uri| {
|
||||||
dependencies.appendAssumeCapacity(try allocator.dupe(u8, uri));
|
dependencies.appendAssumeCapacity(try allocator.dupe(u8, uri));
|
||||||
}
|
}
|
||||||
|
|
||||||
try dependencies.ensureUnusedCapacity(allocator, handle.cimports.len);
|
|
||||||
for (handle.cimports.items(.hash)) |hash| {
|
for (handle.cimports.items(.hash)) |hash| {
|
||||||
const result = store.cimports.get(hash) orelse continue;
|
const result = store.cimports.get(hash) orelse continue;
|
||||||
switch (result) {
|
switch (result) {
|
||||||
@ -846,7 +864,7 @@ pub fn resolveCImport(self: *DocumentStore, handle: Handle, node: Ast.Node.Index
|
|||||||
|
|
||||||
if (!std.process.can_spawn) return null;
|
if (!std.process.can_spawn) return null;
|
||||||
|
|
||||||
const index = std.mem.indexOfScalar(Ast.Node.Index, handle.cimports.items(.node), node).?;
|
const index = std.mem.indexOfScalar(Ast.Node.Index, handle.cimports.items(.node), node) orelse return null;
|
||||||
|
|
||||||
const hash: Hash = handle.cimports.items(.hash)[index];
|
const hash: Hash = handle.cimports.items(.hash)[index];
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user