Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pre compute suggestion db during build time #5698

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
b95a302
build: indexStdLib
4e6 Feb 9, 2023
6bb4d43
feat: serialization manager
4e6 Feb 14, 2023
9c84027
feat: update EnsureCompiledJob
4e6 Feb 15, 2023
b84603e
feat: simplify suggestions init
4e6 Feb 15, 2023
1ee00d3
misc: cleanup
4e6 Feb 20, 2023
e2ac361
Serialize provided suggestions to each standard library
JaroslavTulach Feb 20, 2023
d62ec42
Demo of iteratively adding constructing Tree
JaroslavTulach Feb 21, 2023
560a107
Merge remote-tracking branch 'origin/develop' into wip/db/5068-pre-co…
JaroslavTulach Feb 22, 2023
b68da75
Only regenerate index when necessary
hubertp Feb 22, 2023
8f7314e
Merge branch 'wip/db/5068-pre-compute-suggestion-db-during-build-time…
JaroslavTulach Feb 22, 2023
2fbdf75
Using Jackson to serialize and deserialize Suggestion.Module
JaroslavTulach Feb 23, 2023
f413b62
Can serialize Suggestion[]
JaroslavTulach Feb 23, 2023
958cc78
Can serialize record with List<Suggestion>
JaroslavTulach Feb 23, 2023
1ff4c25
Serialize and deserialize the Suggestion list as a case class
JaroslavTulach Feb 24, 2023
ce821b1
Can load back the Tree.Node[Suggestion] from a cache
JaroslavTulach Feb 24, 2023
5bff1ab
Less debug messages
JaroslavTulach Feb 24, 2023
f480e03
subscribe to Api.LibraryLoaded
JaroslavTulach Feb 24, 2023
335305a
Merge branch 'develop' into wip/db/5068-pre-compute-suggestion-db-dur…
4e6 Mar 2, 2023
b4a98a4
feat: suggestions cache
4e6 Mar 2, 2023
6638c9d
feat: suggestions deserialization
4e6 Mar 5, 2023
283b6f5
feat: generate docs
4e6 Mar 6, 2023
9081707
misc: format
4e6 Mar 6, 2023
0e769c1
fix: suggestions loading
4e6 Mar 6, 2023
6a96e38
misc: run deserialization in background
4e6 Mar 6, 2023
77c5a2b
test: fix language-server
4e6 Mar 6, 2023
009b595
test: fix runtime
4e6 Mar 6, 2023
31773a6
Merge branch 'develop' into wip/db/5068-pre-compute-suggestion-db-dur…
4e6 Mar 6, 2023
a45898a
test: fix RuntimeServerTest
4e6 Mar 6, 2023
5314e10
test: fix RuntimeErrorsTest
4e6 Mar 6, 2023
1447ed0
test: fix RuntimeInstrumentTest
4e6 Mar 6, 2023
20c2e40
test: fix RuntimeSuggestionUpdatesTest
4e6 Mar 6, 2023
89739c0
test: fix RuntimeStdlibTest
4e6 Mar 6, 2023
36862c6
test: fix RuntimeComponentsTest
4e6 Mar 6, 2023
3468399
misc: cleanup
4e6 Mar 6, 2023
952f0f6
Merge branch 'develop' into wip/db/5068-pre-compute-suggestion-db-dur…
mergify[bot] Mar 8, 2023
4742f5f
misc: review comments
4e6 Mar 8, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions engine/runtime/src/main/java/org/enso/compiler/Cache.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import org.bouncycastle.util.encoders.Hex;
import org.enso.interpreter.runtime.EnsoContext;
import org.enso.logger.masking.MaskedPath;
import org.enso.pkg.SourceFile;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
Expand All @@ -19,6 +20,8 @@
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.security.MessageDigest;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.logging.Level;

Expand Down Expand Up @@ -330,6 +333,30 @@ protected String computeDigestFromBytes(byte[] bytes) {
return Hex.toHexString(messageDigest().digest(bytes));
}

/**
* Computes digest from package sources using a default hashing algorithm.
*
* @param pkgSources the list of package sources
* @param logger the truffle logger
* @return string representation of bytes' hash
*/
protected String computeDigestOfLibrarySources(
4e6 marked this conversation as resolved.
Show resolved Hide resolved
List<SourceFile<TruffleFile>> pkgSources, TruffleLogger logger) {
pkgSources.sort(Comparator.comparing(o -> o.qualifiedName().toString()));

var digest = messageDigest();
pkgSources.forEach(
source -> {
try {
digest.update(source.file().readAllBytes());
} catch (IOException e) {
logger.log(
logLevel, "failed to compute digest for " + source.qualifiedName().toString(), e);
}
});
return Hex.toHexString(digest.digest());
}

/**
* Returns a default hashing algorithm used for Enso caches.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import com.oracle.truffle.api.TruffleFile;
import com.oracle.truffle.api.TruffleLogger;
import org.bouncycastle.util.encoders.Hex;
import org.enso.compiler.data.BindingsMap;
import org.enso.editions.LibraryName;
import org.enso.interpreter.runtime.EnsoContext;
Expand All @@ -15,15 +14,13 @@
import scala.collection.immutable.Map;
import scala.jdk.CollectionConverters;

import java.io.IOException;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.logging.Level;

public class ImportExportCache extends Cache<ImportExportCache.CachedBindings, ImportExportCache.Metadata> {
public final class ImportExportCache extends Cache<ImportExportCache.CachedBindings, ImportExportCache.Metadata> {

private final LibraryName libraryName;

Expand Down Expand Up @@ -79,22 +76,6 @@ protected Optional<String> computeDigestFromSource(EnsoContext context, TruffleL
.map(pkg -> computeDigestOfLibrarySources(pkg.listSourcesJava(), logger));
}

private String computeDigestOfLibrarySources(List<SourceFile<TruffleFile>> pkgSources, TruffleLogger logger) {
pkgSources.sort(Comparator.comparing(o -> o.qualifiedName().toString()));

var digest = messageDigest();
pkgSources.forEach(source ->
{
try {
digest.update(source.file().readAllBytes());
} catch (IOException e) {
logger.log(logLevel, "failed to compute digest for " + source.qualifiedName().toString(), e);
}
}
);
return Hex.toHexString(digest.digest());
}

@Override
@SuppressWarnings("unchecked")
protected Optional<Cache.Roots> getCacheRoots(EnsoContext context) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
import java.util.Optional;
import java.util.logging.Level;

public class ModuleCache extends Cache<ModuleCache.CachedModule, ModuleCache.Metadata> {
public final class ModuleCache extends Cache<ModuleCache.CachedModule, ModuleCache.Metadata> {

private final Module module;

Expand Down
151 changes: 151 additions & 0 deletions engine/runtime/src/main/java/org/enso/compiler/SuggestionsCache.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
package org.enso.compiler;

import buildinfo.Info;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.oracle.truffle.api.TruffleLogger;
import org.bouncycastle.util.encoders.Hex;
import org.enso.editions.LibraryName;
import org.enso.interpreter.runtime.EnsoContext;
import org.enso.polyglot.Suggestion;
import scala.jdk.CollectionConverters;

import java.io.Serializable;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.logging.Level;

public final class SuggestionsCache
extends Cache<SuggestionsCache.CachedSuggestions, SuggestionsCache.Metadata> {

private static final String SUGGESTIONS_CACHE_DATA_EXTENSION = ".suggestions";
private static final String SUGGESTIONS_CACHE_METADATA_EXTENSION =".suggestions.meta";

final LibraryName libraryName;

public SuggestionsCache(LibraryName libraryName) {
this.libraryName = libraryName;
this.logLevel = Level.FINEST;
this.stringRepr = libraryName.toString();
this.entryName = libraryName.name();
this.dataSuffix = SUGGESTIONS_CACHE_DATA_EXTENSION;
this.metadataSuffix = SUGGESTIONS_CACHE_METADATA_EXTENSION;
}

@Override
protected byte[] metadata(String sourceDigest, String blobDigest, CachedSuggestions entry) {
var mapper = new ObjectMapper();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't the mapper be a field in the cache? Unless I am mistaken the ObjectMapper instance maintains mapping from Class to "functionality". If we create new instance for every library, then its internal mapping must be initialized every time.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's not what I experienced. This causes major slowdowns, which is why I also changed it in #5791

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's not what I experienced. This causes major slowdowns, which is why I also changed it in #5791

try {
return mapper.writeValueAsString(new Metadata(sourceDigest, blobDigest)).getBytes(metadataCharset);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}

@Override
protected CachedSuggestions validateReadObject(Object obj, Metadata meta, TruffleLogger logger)
throws CacheException {
if (obj instanceof Suggestions suggestions) {
return new CachedSuggestions(libraryName, suggestions);
} else {
throw new CacheException("Expected SuggestionsCache.Suggestions, got " + obj.getClass());
}
}

@Override
protected Optional<Metadata> metadataFromBytes(byte[] bytes) {
var maybeJsonString = new String(bytes, Cache.metadataCharset);
var mapper = new ObjectMapper();
try {
return Optional.of(mapper.readValue(maybeJsonString, SuggestionsCache.Metadata.class));
} catch (JsonProcessingException e) {
return Optional.empty();
}
}

@Override
protected Optional<String> computeDigest(CachedSuggestions entry, TruffleLogger logger) {
var digest = messageDigest();
entry.getSuggestions().getSuggestions().forEach(suggestion -> {
ByteBuffer bytes = ByteBuffer.allocate(Integer.BYTES).putInt(suggestion.hashCode());
digest.update(bytes);
});
return Optional.of(Hex.toHexString(digest.digest()));
}

@Override
protected Optional<String> computeDigestFromSource(EnsoContext context, TruffleLogger logger) {
return context
.getPackageRepository()
.getPackageForLibraryJava(libraryName)
.map(pkg -> computeDigestOfLibrarySources(pkg.listSourcesJava(), logger));
}


@Override
protected Optional<Roots> getCacheRoots(EnsoContext context) {
return context.getPackageRepository().getPackageForLibraryJava(libraryName).map(pkg -> {
var bindingsCacheRoot = pkg.getSuggestionsCacheRootForPackage(Info.ensoVersion());
var localCacheRoot = bindingsCacheRoot.resolve(libraryName.namespace());
var distribution = context.getDistributionManager();
var pathSegments = CollectionConverters.ListHasAsScala(Arrays.asList(
pkg.namespace(),
pkg.name(),
pkg.config().version(),
Info.ensoVersion(),
libraryName.namespace())
).asScala();
var path = distribution.LocallyInstalledDirectories().irCacheDirectory()
.resolve(pathSegments.mkString("/"));
var globalCacheRoot = context.getTruffleFile(path.toFile());
return new Cache.Roots(localCacheRoot, globalCacheRoot);
});
}

@Override
protected Object extractObjectToSerialize(CachedSuggestions entry) {
return entry.getSuggestions();
}

// Suggestions class is not a record because of a Frgaal bug leading to invalid compilation error.
final static class Suggestions implements Serializable {

private final List<Suggestion> suggestions;

public Suggestions(List<Suggestion> suggestions) {
this.suggestions = suggestions;
}

public List<Suggestion> getSuggestions() {
return suggestions;
}
}

// CachedSuggestions class is not a record because of a Frgaal bug leading to invalid compilation error.
Copy link
Member

@JaroslavTulach JaroslavTulach Mar 6, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the error? Maybe updating to new version of frgaal would fix the problem. If I modify:

enso$ git diff
diff --git engine/runtime/src/main/java/org/enso/compiler/SuggestionsCache.java engine/runtime/src/main/java/org/enso/compiler/SuggestionsCache.java
index f5d65b8e0..220c01e9c 100644
--- engine/runtime/src/main/java/org/enso/compiler/SuggestionsCache.java
+++ engine/runtime/src/main/java/org/enso/compiler/SuggestionsCache.java
@@ -125,16 +125,7 @@ public final class SuggestionsCache
   }
 
   // CachedSuggestions class is not a record because of a Frgaal bug leading to invalid compilation error.
-  public final static class CachedSuggestions {
-
-    private final LibraryName libraryName;
-    private final Suggestions suggestions;
-
-    public CachedSuggestions(LibraryName libraryName, Suggestions suggestions) {
-      this.libraryName = libraryName;
-      this.suggestions = suggestions;
-    }
-
+  public record CachedSuggestions(LibraryName libraryName, Suggestions suggestions) {
     public LibraryName getLibraryName() {
       return libraryName;
     }

I am seeing:

sbt:enso> buildEngineDistribution
[info] compiling 45 Scala sources and 191 Java sources to /enso/engine/runtime/target/scala-2.13/classes ...
[error] /enso/engine/runtime/src/main/java/org/enso/compiler/SuggestionsCache.java:128:17: not found: type java
[error]   public record CachedSuggestions(LibraryName libraryName, Suggestions suggestions) {
[error]                 ^
[error] one error found

That's a wild error. Reported to the frgaal project.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this is what I was seeing as well and the reason why in other cases I don't use the record. Thanks for reporting it with frgaal

final static class CachedSuggestions {

private final LibraryName libraryName;
private final Suggestions suggestions;

public CachedSuggestions(LibraryName libraryName, Suggestions suggestions) {
this.libraryName = libraryName;
this.suggestions = suggestions;
}

public LibraryName getLibraryName() {
return libraryName;
}

public Suggestions getSuggestions() {
return suggestions;
}
}

record Metadata(
@JsonProperty("source_hash") String sourceHash,
@JsonProperty("blob_hash") String blobHash
) implements Cache.Metadata { }
}
32 changes: 7 additions & 25 deletions engine/runtime/src/main/scala/org/enso/compiler/Compiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,7 @@ package org.enso.compiler
import com.oracle.truffle.api.TruffleLogger
import com.oracle.truffle.api.source.Source
import org.enso.compiler.codegen.{AstToIr, IrToTruffle, RuntimeStubsGenerator}
import org.enso.compiler.context.{
FreshNameSupply,
InlineContext,
ModuleContext,
SuggestionBuilder
}
import org.enso.compiler.context.{FreshNameSupply, InlineContext, ModuleContext}
import org.enso.compiler.core.IR
import org.enso.compiler.core.IR.Expression

Expand Down Expand Up @@ -68,10 +63,6 @@ class Compiler(
)
private val serializationManager: SerializationManager =
new SerializationManager(this)
private val suggestionsSerializationManager: SuggestionsSerializationManager =
new SuggestionsSerializationManager(
context.getLogger(classOf[SuggestionsSerializationManager])
)
private val logger: TruffleLogger = context.getLogger(getClass)
private val output: PrintStream =
if (config.outputRedirect.isDefined)
Expand Down Expand Up @@ -200,25 +191,16 @@ class Compiler(
mod
}.toList

val compilerResult =
runInternal(
packageModules,
generateCode = false,
shouldCompileDependencies
)
runInternal(
packageModules,
generateCode = false,
shouldCompileDependencies
)

serializationManager.serializeLibraryBindings(
serializationManager.serializeLibrary(
pkg.libraryName,
useGlobalCacheLocations = true
)
val suggestions =
compilerResult.compiledModules
.flatMap { module =>
SuggestionBuilder(module)
.build(module.getName, module.getIr)
.toVector
}
suggestionsSerializationManager.serialize(suggestions, pkg)
}
}
}
Expand Down
Loading