Skip to content

Commit

Permalink
Refactor to reduce the compiled size of the Elm compiler
Browse files Browse the repository at this point in the history
Find a refactoring that massively reduces the size of the compiled Elm compiler - by more than 80%. After running experiments this week, I found a problem with compiling the '++' operator, causing exponential growth of the emitted code. The refactoring in this commit replaces usages of this operator with more specific functions, 'String.join' and 'List.concat', depending on the argument type.
  • Loading branch information
Viir committed Apr 10, 2024
1 parent ae3b583 commit e0cbdcc
Show file tree
Hide file tree
Showing 7 changed files with 272 additions and 172 deletions.
6 changes: 3 additions & 3 deletions implement/Pine/Pine.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<AssemblyVersion>0.1.4</AssemblyVersion>
<FileVersion>0.1.4</FileVersion>
<AssemblyVersion>0.1.5</AssemblyVersion>
<FileVersion>0.1.5</FileVersion>
</PropertyGroup>

<PropertyGroup>
<PackageId>Pine</PackageId>
<Version>0.1.4</Version>
<Version>0.1.5</Version>
<Description>The cross-platform Elm runtime environment</Description>
<PackageTags>Functional;Elm;Runtime;Compiler;VM;DBMS</PackageTags>
<RepositoryUrl>https://github.com/elm-time/elm-time.git</RepositoryUrl>
Expand Down
131 changes: 74 additions & 57 deletions implement/elm-time/ElmTime/compile-elm-program/src/ElmCompiler.elm

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -1220,16 +1220,16 @@ join : String -> List String -> String
join sep chunks =
case sep of
String sepList ->
String (joinOnList sepList (List.map toList chunks))
String (joinOnList sepList chunks)
joinOnList : List Char -> List (List Char) -> List Char
joinOnList : List Char -> List String -> List Char
joinOnList sep chunks =
case chunks of
[] ->
[]
nextChunk :: remaining ->
(String nextChunk) :: remaining ->
if remaining == []
then
nextChunk
Expand Down
111 changes: 67 additions & 44 deletions implement/elm-time/ElmTime/compile-elm-program/src/FirCompiler.elm
Original file line number Diff line number Diff line change
Expand Up @@ -477,7 +477,8 @@ emitDeclarationBlock stackBefore blockDeclarations config =
}
-> List a
composeEnvironmentFunctions { prefix, forwarded, appendedFromDecls, appendedFromClosureCaptures } =
prefix ++ forwarded ++ appendedFromDecls ++ appendedFromClosureCaptures
List.concat
[ prefix, forwarded, appendedFromDecls, appendedFromClosureCaptures ]

prefixEnvironmentFunctions : List EnvironmentFunctionEntry
prefixEnvironmentFunctions =
Expand Down Expand Up @@ -658,7 +659,7 @@ emitDeclarationBlock stackBefore blockDeclarations config =
(\( functionName, blockDeclAsFunction ) ->
case emitFunction blockDeclAsFunction of
Err err ->
Err ("Failed to emit '" ++ functionName ++ "': " ++ err)
Err (String.join "" [ "Failed to emit '", functionName, "': ", err ])

Ok emittedExpression ->
Ok ( functionName, ( blockDeclAsFunction, emittedExpression ) )
Expand Down Expand Up @@ -716,8 +717,10 @@ emitDeclarationBlock stackBefore blockDeclarations config =

appendedEnvFunctionsExpressions : List Pine.Expression
appendedEnvFunctionsExpressions =
newEnvFunctionsExpressionsFromDecls
++ closureCapturesExpressions
List.concat
[ newEnvFunctionsExpressionsFromDecls
, closureCapturesExpressions
]

prevEnvFunctionsExpr : Pine.Expression
prevEnvFunctionsExpr =
Expand All @@ -733,9 +736,11 @@ emitDeclarationBlock stackBefore blockDeclarations config =

envFunctionsExpression =
Pine.ListExpression
(prependedEnvFunctionsExpressions
++ forwardedItems
++ appendedEnvFunctionsExpressions
(List.concat
[ prependedEnvFunctionsExpressions
, forwardedItems
, appendedEnvFunctionsExpressions
]
)

parseAndEmitFunction : Expression -> ( DeclarationBlockFunctionEntry, Result String Pine.Expression )
Expand Down Expand Up @@ -865,13 +870,15 @@ recursionDomainsFromDeclarationDependencies declarationDependencies =
if dependingOnAnyCurrentOrFollowing then
if nextDependingOnNewDomain then
-- Merge the new domain into the current domain
skipped ++ [ Set.union domainToInsert next ] ++ rest
List.concat
[ skipped, [ Set.union domainToInsert next ], rest ]

else
insertDomainRecursive domainToInsert (skipped ++ [ next ]) rest

else
skipped ++ [ domainToInsert ] ++ following
List.concat
[ skipped, [ domainToInsert ], following ]
in
insertDomainRecursive (Set.singleton declName) [] recursionDomains
in
Expand Down Expand Up @@ -914,16 +921,18 @@ emitReferenceExpression name compilation =
case Dict.get name compilation.environmentDeconstructions of
Nothing ->
Err
("Failed referencing '"
++ name
++ "'. "
++ String.fromInt (Dict.size compilation.environmentDeconstructions)
++ " deconstructions in scope: "
++ String.join ", " (Dict.keys compilation.environmentDeconstructions)
++ ". "
++ String.fromInt (List.length compilation.environmentFunctions)
++ " functions in scope: "
++ String.join ", " (List.map .functionName compilation.environmentFunctions)
(String.join ""
[ "Failed referencing '"
, name
, "'. "
, String.fromInt (Dict.size compilation.environmentDeconstructions)
, " deconstructions in scope: "
, String.join ", " (Dict.keys compilation.environmentDeconstructions)
, ". "
, String.fromInt (List.length compilation.environmentFunctions)
, " functions in scope: "
, String.join ", " (List.map .functionName compilation.environmentFunctions)
]
)

Just deconstruction ->
Expand Down Expand Up @@ -1092,10 +1101,12 @@ emitFunctionApplication functionExpression arguments compilation =
case emitExpression compilation argumentExpression of
Err err ->
Err
("Failed emitting argument "
++ String.fromInt argumentIndex
++ " for function application: "
++ err
(String.join ""
[ "Failed emitting argument "
, String.fromInt argumentIndex
, " for function application: "
, err
]
)

Ok result ->
Expand Down Expand Up @@ -1157,7 +1168,8 @@ emitFunctionApplication functionExpression arguments compilation =

environmentFunctions : List EnvironmentFunctionEntry
environmentFunctions =
compilation.environmentFunctions ++ envFunctionsFromClosureCaptures
List.concat
[ compilation.environmentFunctions, envFunctionsFromClosureCaptures ]

newEmitStack =
{ compilation
Expand All @@ -1181,8 +1193,10 @@ emitFunctionApplication functionExpression arguments compilation =
envFunctionsExpr : Pine.Expression
envFunctionsExpr =
Pine.ListExpression
(forwardedItems
++ appendedEnvFunctionsExpressions
(List.concat
[ forwardedItems
, appendedEnvFunctionsExpressions
]
)
in
case emitExpression newEmitStack funcBody of
Expand Down Expand Up @@ -1382,20 +1396,24 @@ emitApplyFunctionFromCurrentEnvironment compilation { functionName } arguments =
case currentEnvironmentFunctionEntryFromName nextExpectedFunctionName of
Nothing ->
Err
("Function '"
++ functionName
++ "' expects environment function '"
++ nextExpectedFunctionName
++ "' but it is not in the environment"
(String.join ""
[ "Function '"
, functionName
, "' expects environment function '"
, nextExpectedFunctionName
, "' but it is not in the environment"
]
)

Just ( indexInEnv, _ ) ->
buildEnvironmentRecursive
(alreadyMapped
++ [ listItemFromIndexExpression_Pine
(List.concat
[ alreadyMapped
, [ listItemFromIndexExpression_Pine
indexInEnv
getEnvFunctionsExpression
]
]
]
)
remainingExpectedFunctions

Expand Down Expand Up @@ -1527,24 +1545,27 @@ adaptivePartialApplicationExpression :
, applicationFunctionSource : Maybe Pine.Expression
}
-> Pine.Expression
adaptivePartialApplicationExpression { function, arguments, applicationFunctionSource } =
if arguments == [] then
function
adaptivePartialApplicationExpression config =
if config.arguments == [] then
config.function

else
let
applicationFunctionExpr =
Maybe.withDefault
(Pine.LiteralExpression adaptivePartialApplicationRecursiveValue)
applicationFunctionSource
case config.applicationFunctionSource of
Just applicationFunctionSource ->
applicationFunctionSource

Nothing ->
Pine.LiteralExpression adaptivePartialApplicationRecursiveValue
in
Pine.ParseAndEvalExpression
{ expression = applicationFunctionExpr
, environment =
Pine.ListExpression
[ applicationFunctionExpr
, function
, Pine.ListExpression arguments
, config.function
, Pine.ListExpression config.arguments
]
}

Expand Down Expand Up @@ -1741,8 +1762,10 @@ parseFunctionRecordFromValueTagged value =

_ ->
Err
("List does not have the expected number of items: "
++ String.fromInt (List.length listItems)
(String.join ""
[ "List does not have the expected number of items: "
, String.fromInt (List.length listItems)
]
)


Expand Down
Loading

0 comments on commit e0cbdcc

Please sign in to comment.