Skip to content

Commit

Permalink
Integrate DFG information into CFG (#1254)
Browse files Browse the repository at this point in the history
* refactor: quote github env variable

* doc(extract-cfg): document use of normalized ast

* feat(wip): insert flow edges

* feat(points-to): support function call resolution
  • Loading branch information
EagleoutIce authored Jan 22, 2025
1 parent 9aaf76b commit a85d13b
Show file tree
Hide file tree
Showing 18 changed files with 149 additions and 59 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/scripts/global-configuration.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ fi

function make_var {
export $1=${2:-}
echo "$1=${2:-}" >> $GITHUB_ENV
echo "$1=${2:-}" >> "$GITHUB_ENV"
}

make_var ACTION_NODE_VERSION "22.13.x"
Expand Down
38 changes: 29 additions & 9 deletions src/cli/repl/commands/repl-cfg.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,41 @@
import type { ReplCommand } from './repl-main';
import type { ReplCommand, ReplOutput } from './repl-main';
import { extractCFG } from '../../../util/cfg/cfg';
import { createNormalizePipeline } from '../../../core/steps/pipeline/default-pipelines';
import { createDataflowPipeline } from '../../../core/steps/pipeline/default-pipelines';
import { fileProtocol, requestFromInput } from '../../../r-bridge/retriever';
import { cfgToMermaid, cfgToMermaidUrl } from '../../../util/mermaid/cfg';
import type { KnownParser } from '../../../r-bridge/parser';
import { ColorEffect, Colors, FontStyles } from '../../../util/ansi';
import clipboard from 'clipboardy';

async function controlflow(parser: KnownParser, remainingLine: string) {
return await createNormalizePipeline(parser, {
return await createDataflowPipeline(parser, {
request: requestFromInput(remainingLine.trim())
}).allRemainingSteps();
}

function handleString(code: string): string {
return code.startsWith('"') ? JSON.parse(code) as string : code;
}

function formatInfo(out: ReplOutput, type: string): string {
return out.formatter.format(`Copied ${type} to clipboard.`, { color: Colors.White, effect: ColorEffect.Foreground, style: FontStyles.Italic });
}

export const controlflowCommand: ReplCommand = {
description: `Get mermaid code for the control-flow graph of R code, start with '${fileProtocol}' to indicate a file`,
usageExample: ':controlflow',
aliases: [ 'cfg', 'cf' ],
script: false,
fn: async(output, shell, remainingLine) => {
const result = await controlflow(shell, remainingLine);
const result = await controlflow(shell, handleString(remainingLine));

const cfg = extractCFG(result.normalize);
output.stdout(cfgToMermaid(cfg, result.normalize));
const cfg = extractCFG(result.normalize, result.dataflow.graph);
const mermaid = cfgToMermaid(cfg, result.normalize);
output.stdout(mermaid);
try {
clipboard.writeSync(mermaid);
output.stdout(formatInfo(output, 'mermaid code'));
} catch(e) { /* do nothing this is a service thing */ }
}
};

Expand All @@ -30,9 +45,14 @@ export const controlflowStarCommand: ReplCommand = {
aliases: [ 'cfg*', 'cf*' ],
script: false,
fn: async(output, shell, remainingLine) => {
const result = await controlflow(shell, remainingLine);
const result = await controlflow(shell, handleString(remainingLine));

const cfg = extractCFG(result.normalize);
output.stdout(cfgToMermaidUrl(cfg, result.normalize));
const cfg = extractCFG(result.normalize, result.dataflow.graph);
const mermaid = cfgToMermaidUrl(cfg, result.normalize);
output.stdout(mermaid);
try {
clipboard.writeSync(mermaid);
output.stdout(formatInfo(output, 'mermaid url'));
} catch(e) { /* do nothing this is a service thing */ }
}
};
8 changes: 4 additions & 4 deletions src/cli/repl/commands/repl-dataflow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ function handleString(code: string): string {
return code.startsWith('"') ? JSON.parse(code) as string : code;
}

function formatInfo(out: ReplOutput, type: string): string {
return out.formatter.format(`Copied ${type} to clipboard.`, { color: Colors.White, effect: ColorEffect.Foreground, style: FontStyles.Italic });
function formatInfo(out: ReplOutput, type: string, timing: number): string {
return out.formatter.format(`Copied ${type} to clipboard (dataflow: ${timing}ms).`, { color: Colors.White, effect: ColorEffect.Foreground, style: FontStyles.Italic });
}

export const dataflowCommand: ReplCommand = {
Expand All @@ -31,7 +31,7 @@ export const dataflowCommand: ReplCommand = {
output.stdout(mermaid);
try {
clipboard.writeSync(mermaid);
output.stdout(formatInfo(output, 'mermaid code'));
output.stdout(formatInfo(output, 'mermaid code', result.dataflow['.meta'].timing));
} catch(e) { /* do nothing this is a service thing */ }
}
};
Expand All @@ -47,7 +47,7 @@ export const dataflowStarCommand: ReplCommand = {
output.stdout(mermaid);
try {
clipboard.writeSync(mermaid);
output.stdout(formatInfo(output, 'mermaid url'));
output.stdout(formatInfo(output, 'mermaid url', result.dataflow['.meta'].timing));
} catch(e) { /* do nothing this is a service thing */ }
}
};
2 changes: 1 addition & 1 deletion src/cli/repl/server/connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ export class FlowRServerConnection {
private async sendFileAnalysisResponse(results: Partial<PipelineOutput<typeof DEFAULT_SLICING_PIPELINE>>, message: FileAnalysisRequestMessage): Promise<void> {
let cfg: ControlFlowInformation | undefined = undefined;
if(message.cfg) {
cfg = extractCFG(results.normalize as NormalizedAst);
cfg = extractCFG(results.normalize as NormalizedAst, results.dataflow?.graph);
}

const config = (): QuadSerializationConfiguration => ({ context: message.filename ?? 'unknown', getId: defaultQuadIdGenerator() });
Expand Down
2 changes: 1 addition & 1 deletion src/cli/script-core/statistics-helper-core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export async function getStatsForSingleFile(options: StatsHelperCliOptions) {
if(stats.outputs.size === 1) {
if(options['dump-json']) {
const [, output] = [...stats.outputs.entries()][0];
const cfg = extractCFG(output.normalize);
const cfg = extractCFG(output.normalize, output.dataflow.graph);
statisticsFileProvider.append('output-json', 'parse', await printStepResult(PARSE_WITH_R_SHELL_STEP, output.parse, StepOutputFormat.Json));
statisticsFileProvider.append('output-json', 'normalize', await printStepResult(NORMALIZE, output.normalize, StepOutputFormat.Json));
statisticsFileProvider.append('output-json', 'dataflow', await printStepResult(STATIC_DATAFLOW, output.dataflow, StepOutputFormat.Json));
Expand Down
2 changes: 1 addition & 1 deletion src/dataflow/extractor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ function resolveLinkToSideEffects(ast: NormalizedAst, graph: DataflowGraph) {
if(typeof s !== 'object') {
continue;
}
cfg ??= extractCFG(ast).graph;
cfg ??= extractCFG(ast, graph).graph;
/* this has to change whenever we add a new link to relations because we currently offer no abstraction for the type */
const potentials = identifyLinkToLastCallRelation(s.id, cfg, graph, s.linkTo);
for(const pot of potentials) {
Expand Down
22 changes: 11 additions & 11 deletions src/dataflow/graph/dataflowgraph-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,8 +198,9 @@ export class DataflowGraphBuilder extends DataflowGraph {
/**
* Adds a **read edge** (E1).
*
* @param from - Vertex/NodeId
* @param to - see from
* @param from - NodeId of the source vertex
* @param to - Either a single or multiple target ids.
* If you pass multiple this will construct a single edge for each of them.
*/
public reads(from: NodeId, to: DataflowGraphEdgeTarget) {
return this.edgeHelper(from, to, EdgeType.Reads);
Expand All @@ -209,7 +210,7 @@ export class DataflowGraphBuilder extends DataflowGraph {
* Adds a **defined-by edge** (E2), with from as defined variable, and to
* as a variable/function contributing to its definition.
*
* @see reads for parameters.
* @see {@link DataflowGraphBuilder#reads|reads} for parameters.
*/
public definedBy(from: NodeId, to: DataflowGraphEdgeTarget) {
return this.edgeHelper(from, to, EdgeType.DefinedBy);
Expand All @@ -218,7 +219,7 @@ export class DataflowGraphBuilder extends DataflowGraph {
/**
* Adds a **call edge** (E5) with from as caller, and to as callee.
*
* @see reads for parameters.
* @see {@link DataflowGraphBuilder#reads|reads} for parameters.
*/
public calls(from: NodeId, to: DataflowGraphEdgeTarget) {
return this.edgeHelper(from, to, EdgeType.Calls);
Expand All @@ -227,7 +228,7 @@ export class DataflowGraphBuilder extends DataflowGraph {
/**
* Adds a **return edge** (E6) with from as function, and to as exit point.
*
* @see reads for parameters.
* @see {@link DataflowGraphBuilder#reads|reads} for parameters.
*/
public returns(from: NodeId, to: DataflowGraphEdgeTarget) {
return this.edgeHelper(from, to, EdgeType.Returns);
Expand All @@ -236,7 +237,7 @@ export class DataflowGraphBuilder extends DataflowGraph {
/**
* Adds a **defines-on-call edge** (E7) with from as variable, and to as its definition
*
* @see reads for parameters.
* @see {@link DataflowGraphBuilder#reads|reads} for parameters.
*/
public definesOnCall(from: NodeId, to: DataflowGraphEdgeTarget) {
return this.edgeHelper(from, to, EdgeType.DefinesOnCall);
Expand All @@ -245,7 +246,7 @@ export class DataflowGraphBuilder extends DataflowGraph {
/**
* Adds a **defined-by-on-call edge** with from as definition, and to as variable.
*
* @see reads for parameters.
* @see {@link DataflowGraphBuilder#reads|reads} for parameters.
*/
public definedByOnCall(from: NodeId, to: DataflowGraphEdgeTarget) {
return this.edgeHelper(from, to, EdgeType.DefinedByOnCall);
Expand All @@ -254,7 +255,7 @@ export class DataflowGraphBuilder extends DataflowGraph {
/**
* Adds an **argument edge** (E9) with from as function call, and to as argument.
*
* @see reads for parameters.
* @see {@link DataflowGraphBuilder#reads|reads} for parameters.
*/
public argument(from: NodeId, to: DataflowGraphEdgeTarget) {
return this.edgeHelper(from, to, EdgeType.Argument);
Expand All @@ -263,7 +264,7 @@ export class DataflowGraphBuilder extends DataflowGraph {
/**
* Adds a **non-standard evaluation edge** with from as vertex, and to as vertex.
*
* @see reads for parameters.
* @see {@link DataflowGraphBuilder#reads|reads} for parameters.
*/
public nse(from: NodeId, to: DataflowGraphEdgeTarget) {
return this.edgeHelper(from, to, EdgeType.NonStandardEvaluation);
Expand All @@ -272,13 +273,12 @@ export class DataflowGraphBuilder extends DataflowGraph {
/**
* Adds a **side-effect-on-call edge** with from as vertex, and to as vertex.
*
* @see reads for parameters.
* @see {@link DataflowGraphBuilder#reads|reads} for parameters.
*/
public sideEffectOnCall(from: NodeId, to: DataflowGraphEdgeTarget) {
return this.edgeHelper(from, to, EdgeType.SideEffectOnCall);
}


/**
* explicitly overwrite the root ids of the graph,
* this is just an easier variant in case you working with a lot of functions this saves you a lot of `false` flags.
Expand Down
5 changes: 2 additions & 3 deletions src/dataflow/graph/edge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ export interface DataflowGraphEdge {
types: EdgeTypeBits
}


/**
* Represents the relationship between the source and the target vertex in the dataflow graph.
* The actual value is represented as a bitmask so use {@link edgeTypesToNames} to get something more human-readable.
Expand Down Expand Up @@ -117,8 +116,8 @@ export const enum TraverseEdge {
*
* Counterpart of {@link edgeDoesNotIncludeType}.
*/
export function edgeIncludesType(type: EdgeTypeBits, types: EdgeTypeBits): boolean {
return (types & type) !== 0;
export function edgeIncludesType(type: EdgeTypeBits, typesToInclude: EdgeTypeBits): boolean {
return (typesToInclude & type) !== 0;
}

/**
Expand Down
22 changes: 11 additions & 11 deletions src/dataflow/graph/vertex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export const ValidVertexTypeReverse = Object.fromEntries(Object.entries(VertexTy
*/
export interface ContainerLeafIndex {
/**
* Destinctive lexeme of index e.g 'name' for `list(name = 'John')`
* Distinctive lexeme of index e.g. 'name' for `list(name = 'John')`
*/
readonly lexeme: string,

Expand Down Expand Up @@ -222,35 +222,35 @@ export type DataflowGraphVertices<Vertex extends DataflowGraphVertexInfo = Dataf
/**
* Check if the given vertex is a {@link DataflowGraphVertexValue|value vertex}.
*/
export function isValueVertex(vertex: DataflowGraphVertexBase): vertex is DataflowGraphVertexValue {
return vertex.tag === VertexType.Value;
export function isValueVertex(vertex?: DataflowGraphVertexBase): vertex is DataflowGraphVertexValue {
return vertex?.tag === VertexType.Value;
}

/**
* Check if the given vertex is a {@link DataflowGraphVertexUse|use vertex}.
*/
export function isUseVertex(vertex: DataflowGraphVertexBase): vertex is DataflowGraphVertexUse {
return vertex.tag === VertexType.Use;
export function isUseVertex(vertex?: DataflowGraphVertexBase): vertex is DataflowGraphVertexUse {
return vertex?.tag === VertexType.Use;
}

/**
* Check if the given vertex is a {@link DataflowGraphVertexFunctionCall|function call vertex}.
*/
export function isFunctionCallVertex(vertex: DataflowGraphVertexBase): vertex is DataflowGraphVertexFunctionCall {
return vertex.tag === VertexType.FunctionCall;
export function isFunctionCallVertex(vertex?: DataflowGraphVertexBase): vertex is DataflowGraphVertexFunctionCall {
return vertex?.tag === VertexType.FunctionCall;
}

/**
* Check if the given vertex is a {@link DataflowGraphVertexVariableDefinition|variable definition vertex}.
*/
export function isVariableDefinitionVertex(vertex: DataflowGraphVertexBase): vertex is DataflowGraphVertexVariableDefinition {
return vertex.tag === VertexType.VariableDefinition;
export function isVariableDefinitionVertex(vertex?: DataflowGraphVertexBase): vertex is DataflowGraphVertexVariableDefinition {
return vertex?.tag === VertexType.VariableDefinition;
}

/**
* Check if the given vertex is a {@link DataflowGraphVertexFunctionDefinition|function definition vertex}.
*/
export function isFunctionDefinitionVertex(vertex: DataflowGraphVertexBase): vertex is DataflowGraphVertexFunctionDefinition {
return vertex.tag === VertexType.FunctionDefinition;
export function isFunctionDefinitionVertex(vertex?: DataflowGraphVertexBase): vertex is DataflowGraphVertexFunctionDefinition {
return vertex?.tag === VertexType.FunctionDefinition;
}

Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,6 @@ export function processExpressionList<OtherInfo>(

processNextExpression(processed, environment, listEnvironments, remainingRead, nextGraph);


environment = exitPoints.length > 0 ? overwriteEnvironment(environment, processed.environment) : processed.environment;

const calledEnvs = linkFunctionCalls(nextGraph, data.completeAst.idMap, processed.graph);
Expand Down
6 changes: 3 additions & 3 deletions src/documentation/doc-util/doc-cfg.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { ControlFlowInformation } from '../../util/cfg/cfg';
import { extractCFG } from '../../util/cfg/cfg';
import { PipelineExecutor } from '../../core/pipeline-executor';
import { DEFAULT_NORMALIZE_PIPELINE } from '../../core/steps/pipeline/default-pipelines';
import { DEFAULT_DATAFLOW_PIPELINE } from '../../core/steps/pipeline/default-pipelines';
import { requestFromInput } from '../../r-bridge/retriever';
import type { RShell } from '../../r-bridge/shell';
import type { NormalizedAst } from '../../r-bridge/lang-4.x/ast/model/processing/decorate';
Expand All @@ -10,12 +10,12 @@ export async function getCfg(shell: RShell, code: string): Promise<{
info: ControlFlowInformation,
ast: NormalizedAst
}> {
const steps = await new PipelineExecutor(DEFAULT_NORMALIZE_PIPELINE, {
const steps = await new PipelineExecutor(DEFAULT_DATAFLOW_PIPELINE, {
parser: shell,
request: requestFromInput(code)
}).allRemainingSteps();
return {
info: extractCFG(steps.normalize),
info: extractCFG(steps.normalize, steps.dataflow?.graph),
ast: steps.normalize
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ export function executeCallContextQueries({ dataflow: { graph }, ast }: BasicQue

let cfg = undefined;
if(requiresCfg) {
cfg = extractCFG(ast);
cfg = extractCFG(ast, graph);
}

const queriesWhichWantAliases = promotedQueries.filter(q => q.includeAliases);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ import { extractCFG } from '../../../util/cfg/cfg';
import { happensBefore } from '../../../util/cfg/happens-before';
import { slicingCriterionToId } from '../../../slicing/criterion/parse';

export function executeHappensBefore({ ast }: BasicQueryData, queries: readonly HappensBeforeQuery[]): HappensBeforeQueryResult {
export function executeHappensBefore({ ast, dataflow }: BasicQueryData, queries: readonly HappensBeforeQuery[]): HappensBeforeQueryResult {
const start = Date.now();
const results: Record<string, Ternary> = {};
const cfg = extractCFG(ast);
const cfg = extractCFG(ast, dataflow.graph);
for(const query of queries) {
const { a, b } = query;
const fingerprint = `${a}<${b}`;
Expand Down
Loading

4 comments on commit a85d13b

@github-actions
Copy link

Choose a reason for hiding this comment

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

"artificial" Benchmark Suite

Benchmark suite Current: a85d13b Previous: a877df7 Ratio
Retrieve AST from R code 248.32334104545453 ms (106.81347795093653) 239.49771736363638 ms (101.48800821636837) 1.04
Normalize R AST 17.803583 ms (32.629295991359534) 17.41336081818182 ms (30.981566426513265) 1.02
Produce dataflow information 70.43754081818182 ms (152.96904594478139) 68.728513 ms (151.94316854576633) 1.02
Total per-file 858.4292594545454 ms (1566.6182700489467) 837.9476761818181 ms (1510.9729062977526) 1.02
Static slicing 2.0081414958962225 ms (1.1525853766703917) 2.0034774339582415 ms (1.1691051705207822) 1.00
Reconstruct code 0.2328410252468656 ms (0.18445349759234353) 0.22634653062782062 ms (0.168666676054442) 1.03
Total per-slice 2.255245174034879 ms (1.2286384829449617) 2.24374443748002 ms (1.2384172691894533) 1.01
failed to reconstruct/re-parse 0 # 0 # 1
times hit threshold 0 # 0 # 1
reduction (characters) 0.7891949660994808 # 0.7891949660994808 # 1
reduction (normalized tokens) 0.7665650684287274 # 0.7665650684287274 # 1
memory (df-graph) 95.19682173295455 KiB (244.24808975931026) 95.19682173295455 KiB (244.24808975931026) 1

This comment was automatically generated by workflow using github-action-benchmark.

@github-actions
Copy link

Choose a reason for hiding this comment

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

"social-science" Benchmark Suite

Benchmark suite Current: a85d13b Previous: a877df7 Ratio
Retrieve AST from R code 245.12588396 ms (46.31602992655432) 240.34012847999998 ms (43.93673078226616) 1.02
Normalize R AST 18.754497079999997 ms (14.1108254362404) 18.87248132 ms (14.186278380523435) 0.99
Produce dataflow information 78.7498075 ms (73.12711600555512) 78.15406248000001 ms (73.48454316898545) 1.01
Total per-file 7614.3063761 ms (30122.107163934794) 7582.25710378 ms (29767.39189439267) 1.00
Static slicing 15.271004775962375 ms (45.45808043905079) 15.270449566508105 ms (44.94967789767313) 1.00
Reconstruct code 0.2633794238041067 ms (0.15137845561756516) 0.2585950662014173 ms (0.1532502183629028) 1.02
Total per-slice 15.542288796787147 ms (45.49128336072756) 15.537331197364793 ms (44.98374295831238) 1.00
failed to reconstruct/re-parse 0 # 0 # 1
times hit threshold 0 # 0 # 1
reduction (characters) 0.8760481407790371 # 0.8760481407790371 # 1
reduction (normalized tokens) 0.8152466834674152 # 0.8152466834674152 # 1
memory (df-graph) 99.6448046875 KiB (113.2159841674677) 99.6448046875 KiB (113.2159841674677) 1

This comment was automatically generated by workflow using github-action-benchmark.

@github-actions
Copy link

Choose a reason for hiding this comment

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

"artificial" Benchmark Suite (tree-sitter)

Benchmark suite Current: a85d13b Previous: a877df7 Ratio
Retrieve AST from R code 7.878252454545454 ms (8.533091706684177) 8.145506545454547 ms (9.786080094734778) 0.97
Normalize R AST 16.973453681818185 ms (21.54502888700435) 17.78099590909091 ms (23.820712056308647) 0.95
Produce dataflow information 70.4022154090909 ms (143.62740718489636) 70.83680254545455 ms (150.5207252208632) 0.99
Total per-file 610.2485431363637 ms (1392.0259839720961) 627.2099676818182 ms (1407.4477264884629) 0.97
Static slicing 2.037858681466583 ms (1.1368547848550699) 2.0248883955065318 ms (1.1173257361688766) 1.01
Reconstruct code 0.22464761464280208 ms (0.1717905692529436) 0.23812396339689798 ms (0.1785232875956438) 0.94
Total per-slice 2.2756645753848415 ms (1.2020541914620437) 2.2780515267109083 ms (1.198556469122308) 1.00
failed to reconstruct/re-parse 0 # 0 # 1
times hit threshold 0 # 0 # 1
reduction (characters) 0.7891949660994808 # 0.7891949660994808 # 1
reduction (normalized tokens) 0.7665650684287274 # 0.7665650684287274 # 1
memory (df-graph) 95.19682173295455 KiB (244.24808975931026) 95.19682173295455 KiB (244.24808975931026) 1

This comment was automatically generated by workflow using github-action-benchmark.

@github-actions
Copy link

Choose a reason for hiding this comment

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

"social-science" Benchmark Suite (tree-sitter)

Benchmark suite Current: a85d13b Previous: a877df7 Ratio
Retrieve AST from R code 12.352666222222222 ms (8.020088350322169) 10.4682258 ms (6.536909762375299) 1.18
Normalize R AST 29.02011408888889 ms (13.887889786522873) 23.515930466666664 ms (11.460003796895084) 1.23
Produce dataflow information 94.89209242222223 ms (79.52732741572484) 77.72576402222222 ms (66.68368526289126) 1.22
Total per-file 9222.592383555555 ms (37495.14640681524) 7557.427238355555 ms (30739.010883053157) 1.22
Static slicing 19.324996357286814 ms (56.03172605251331) 15.83153546219062 ms (45.672079288893144) 1.22
Reconstruct code 0.3509510572684235 ms (0.37039376921183825) 0.2510293465879067 ms (0.15539877495324173) 1.40
Total per-slice 19.68591182468504 ms (56.074133488036566) 16.090141405670494 ms (45.71466701697095) 1.22
failed to reconstruct/re-parse 0 # 0 # 1
times hit threshold 0 # 0 # 1
reduction (characters) 0.8685107303274813 # 0.8685107303274813 # 1
reduction (normalized tokens) 0.8049905198879769 # 0.8049905198879769 # 1
memory (df-graph) 97.50590277777778 KiB (111.14577334743846) 97.50590277777778 KiB (111.14577334743846) 1

This comment was automatically generated by workflow using github-action-benchmark.

Please sign in to comment.