enso-org / enso

Enso Analytics is a self-service data prep and analysis platform designed for data teams.
https://ensoanalytics.com
Apache License 2.0
7.36k stars 323 forks source link

IdExecutionInstrument slowed down by calling `iterateFrames` #5526

Open wdanilo opened 1 year ago

wdanilo commented 1 year ago

This task is automatically imported from the old Task Issue Board and it was originally created by jaroslavtulach. Original issue is here.


Description

While working on #182412796 I noticed IdExecutionInstrument is calling iteratesFrames often - however that's not fast operation and must slow us down by magnitude. The method

    private boolean isTopFrame(CallTarget entryCallTarget) {
      Object result =
          Truffle.getRuntime()
              .iterateFrames(

cannot run at full speed as iterateFrames needs to deopt to interpreter. The method seems to be called on every change in the IDE.

Alternatives

IdExecutionInstrument shall install a wrapper node around each StandardTags.Root. Marcin suggests: we can probably wrap the body nodes in every instrumented root node with some "depth tracking" node - i.e. when a root becomes instrumented we start keeping track of enters/leaves. This way we can do it without a @TruffleBoundary, the additional cost is one memory read/write per instrumented function call (compiled libs remain unaffected) and the cost of "hey is this recursive" is just a memory read.

Consider

Shouldn't we run some benchmarks with the IdExcecutionInstrument enabled? If we want the execution in the IDE be fast, we should measure it a bit and make sure it is not order of magnitudes slower than engine's standalone performance.

The counter shall be per thread. Take a look at ContextThreadLocal.

Comments:

How many nodes are being instrumented by the IdExecutionInstrument? There is a method bind in the instrument class that currently does:

    var builder = SourceSectionFilter.newBuilder()
          .tagIs(StandardTags.ExpressionTag.class, StandardTags.CallTag.class)
          .tagIs(IdentifiedTag.class)
          .tagIsNot(AvoidIdInstrumentationTag.class)
          .sourceIs(module::isModuleSource);

    if (entryCallTarget instanceof RootCallTarget r && r.getRootNode() instanceof ClosureRootNode c && c.getSourceSection() != null) {
      final int firstFunctionLine = c.getSourceSection().getStartLine();
      final int afterFunctionLine = c.getSourceSection().getEndLine() + 1;
      builder.lineIn(SourceSectionFilter.IndexRange.between(firstFunctionLine, afterFunctionLine));
    }
    SourceSectionFilter filter = builder.build();

E.g. it selects all nodes from the .enso file between firstFunctionLine and afterFunctionLine - e.g. very likely only small set of nodes is instrumented. (jaroslavtulach - Jul 25, 2022)


kazcw commented 1 week ago

Whatever this is, it doesn't involve any parser work that I can see.