Skip to content

[browser] improve default initial memory size #80507

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

Merged
merged 20 commits into from
Jan 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,6 @@
<EnableAggressiveTrimming Condition="'$(EnableAOTAndTrimming)' != ''">$(EnableAOTAndTrimming)</EnableAggressiveTrimming>
<PublishTrimmed Condition="'$(EnableAOTAndTrimming)' != ''">$(EnableAOTAndTrimming)</PublishTrimmed>
<RunAOTCompilation Condition="'$(EnableAOTAndTrimming)' != ''">$(EnableAOTAndTrimming)</RunAOTCompilation>
<!-- the default heap size is ~512MB, which is too much because AppStart loads more copies
of the wasm runtime and can leak a few of them. the result is that browser-bench's memory
usage can climb as high as 3GB or more and then fail -->
<EmccInitialHeapSize>83886080</EmccInitialHeapSize>
</PropertyGroup>

<ItemGroup>
Expand Down
38 changes: 35 additions & 3 deletions src/mono/wasm/build/WasmApp.Native.targets
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,6 @@
<_EmccLinkRsp>$(_WasmIntermediateOutputPath)emcc-link.rsp</_EmccLinkRsp>

<EmccInitialHeapSize Condition="'$(EmccInitialHeapSize)' == ''">$(EmccTotalMemory)</EmccInitialHeapSize>
<EmccInitialHeapSize Condition="'$(EmccInitialHeapSize)' == ''">536870912</EmccInitialHeapSize>
</PropertyGroup>

<ItemGroup>
Expand Down Expand Up @@ -231,7 +230,6 @@
<_EmccLDFlags Include="-s ASSERTIONS=$(_EmccAssertionLevelDefault)" Condition="'$(_WasmDevel)' == 'true'" />
<_EmccLDFlags Include="@(_EmccCommonFlags)" />
<_EmccLDSFlags Include="-Wl,--allow-undefined" />
<_EmccLDSFlags Include="-s INITIAL_MEMORY=$(EmccInitialHeapSize)" />

<!-- ILLinker should have removed unused imports, so error for Publish -->
<_EmccLDSFlags Include="-s ERROR_ON_UNDEFINED_SYMBOLS=0" Condition="'$(WasmBuildingForNestedPublish)' != 'true'" />
Expand Down Expand Up @@ -372,7 +370,39 @@
</ItemGroup>
</Target>

<Target Name="_WasmWriteRspFilesForLinking" DependsOnTargets="_CheckEmccIsExpectedVersion">
<Target Name="_WasmCalculateInitialHeapSize"
Condition="'$(EmccInitialHeapSize)' == ''"
DependsOnTargets="_CheckEmccIsExpectedVersion">
<ItemGroup>
<_AOTObjectFile Include="%(_BitcodeFile.ObjectFile)" />
</ItemGroup>

<!-- for AOT builds we use llvm-size tool to collect size of the DATA segment in each object file -->
<Exec Command="llvm-size$(_ExeExt) -d --format=sysv @(_AOTObjectFile, ' ')"
Condition="'$(_WasmShouldAOT)' == 'true'"
IgnoreStandardErrorWarningFormat="true"
ConsoleToMsBuild="true"
StandardOutputImportance="low" StandardErrorImportance="low"
EnvironmentVariables="@(EmscriptenEnvVars)" >
<Output TaskParameter="ConsoleOutput" ItemName="LlvmAotSizeOutput" />
</Exec>
<ItemGroup Condition="'$(_WasmShouldAOT)' == 'true'">
<_AOTDataSegmentSize Condition="$([System.String]::Copy('%(LlvmAotSizeOutput.Identity)').StartsWith('DATA '))"
Include="$([System.String]::Copy('%(LlvmAotSizeOutput.Identity)').Replace(&quot;DATA &quot;, &quot;&quot;).Replace(&quot; 0&quot;, &quot;&quot;).Trim())" />
</ItemGroup>

<WasmCalculateInitialHeapSize
Assemblies="@(WasmAssembliesToBundle)"
AOTDataSegmentSizes="@(_AOTDataSegmentSize)">
<Output TaskParameter="InitialHeapSize" PropertyName="_WasmCalculatedInitialHeapSize" />
</WasmCalculateInitialHeapSize>
<PropertyGroup>
<EmccInitialHeapSize Condition="'$(EmccInitialHeapSize)' == '' and '$(_WasmCalculatedInitialHeapSize)' != '' and $(_WasmCalculatedInitialHeapSize) > 16777216">$(_WasmCalculatedInitialHeapSize)</EmccInitialHeapSize>
<EmccInitialHeapSize Condition="'$(EmccInitialHeapSize)' == ''">16777216</EmccInitialHeapSize>
</PropertyGroup>
</Target>

<Target Name="_WasmWriteRspFilesForLinking" DependsOnTargets="_CheckEmccIsExpectedVersion;_WasmCalculateInitialHeapSize">
<PropertyGroup>
<_WasmEHLib Condition="'$(WasmEnableExceptionHandling)' == 'true'">libmono-wasm-eh-wasm.a</_WasmEHLib>
<_WasmEHLib Condition="'$(WasmEnableExceptionHandling)' != 'true'">libmono-wasm-eh-js.a</_WasmEHLib>
Expand All @@ -383,6 +413,8 @@
</PropertyGroup>
<ItemGroup>
<!-- order matters -->
<_EmccLDSFlags Include="-s INITIAL_MEMORY=$(EmccInitialHeapSize)" />

<_WasmNativeFileForLinking Include="%(_BitcodeFile.ObjectFile)" />
<_WasmNativeFileForLinking Include="%(_WasmSourceFileToCompile.ObjectFile)" />

Expand Down
3 changes: 2 additions & 1 deletion src/mono/wasm/build/WasmApp.targets
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<Project>
<UsingTask TaskName="WasmAppBuilder" AssemblyFile="$(WasmAppBuilderTasksAssemblyPath)" />
<UsingTask TaskName="WasmLoadAssembliesAndReferences" AssemblyFile="$(WasmAppBuilderTasksAssemblyPath)" />
<UsingTask TaskName="WasmCalculateInitialHeapSize" AssemblyFile="$(WasmAppBuilderTasksAssemblyPath)" />

<!--
Required public items/properties:
Expand Down Expand Up @@ -51,7 +52,7 @@
- $(EmccFlags) - Emcc flags used for both compiling native files, and linking
- $(EmccExtraLDFlags) - Extra emcc flags for linking
- $(EmccExtraCFlags) - Extra emcc flags for compiling native files
- $(EmccInitialHeapSize) - Initial heap size specified with `emcc`. Default value: 536870912
- $(EmccInitialHeapSize) - Initial heap size specified with `emcc`. Default value: 16777216 or size of the DLLs, whichever is larger.
Corresponds to `INITIAL_MEMORY` arg for emcc.
(previously named EmccTotalMemory, which is still kept as an alias)

Expand Down
2 changes: 1 addition & 1 deletion src/mono/wasm/wasm.proj
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@
<PropertyGroup>
<_EmccExportedRuntimeMethods>&quot;[@(EmccExportedRuntimeMethod -> '%27%(Identity)%27', ',')]&quot;</_EmccExportedRuntimeMethods>
<_EmccExportedFunctions>@(EmccExportedFunction -> '%(Identity)',',')</_EmccExportedFunctions>
<EmccInitialHeapSize>536870912</EmccInitialHeapSize>
<EmccInitialHeapSize>16777216</EmccInitialHeapSize>
</PropertyGroup>
<ItemGroup>
<_EmccLinkFlags Include="-s INITIAL_MEMORY=$(EmccInitialHeapSize)" />
Expand Down
59 changes: 59 additions & 0 deletions src/tasks/WasmAppBuilder/WasmCalculateInitialHeapSize.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.IO;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;

namespace Microsoft.WebAssembly.Build.Tasks;

/// <summary>estimate the total memory needed for the assemblies and AOT data segments.</summary>
public class WasmCalculateInitialHeapSize : Task
{
[Required]
public string[] Assemblies { get; set; } = Array.Empty<string>();

public string[] AOTDataSegmentSizes { get; set; } = Array.Empty<string>();

[Output]
public long InitialHeapSize { get; private set; }

public override bool Execute()
{
long totalDllSize = 0;
long totalDataSize = 0;

foreach (string asm in Assemblies)
{
var info = new FileInfo(asm);
if (!info.Exists)
{
Log.LogError($"Could not find assembly '{asm}'");
return false;
}
totalDllSize += info.Length;
}

// during non-AOT builds, AOTDataSegmentSizes is empty
foreach (string segment in AOTDataSegmentSizes)
{
if (!long.TryParse(segment, out long segmentSize))
{
Log.LogError($"Could not parse AOT Data segment size '{segment}");
return false;
}
totalDataSize += segmentSize;
}

// this is arbitrary guess about memory overhead of the runtime, after the assemblies are loaded
const double extraMemoryRatio = 1.2;
// plus size of data segments generated by AOT
long memorySize = totalDataSize + (long)(totalDllSize * extraMemoryRatio);

// round it up to 64KB page size for wasm
InitialHeapSize = (memorySize + 0x10000) & 0xFFFF0000;

return true;
}
}