Skip to content

Commit 874d9c1

Browse files
committed
try to deal with CI memory overload
1 parent a269b6a commit 874d9c1

File tree

5 files changed

+44
-26
lines changed

5 files changed

+44
-26
lines changed

src/Compiler/Checking/import.fs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,10 +106,10 @@ let getOrCreateTypeSubsumptionCache =
106106
| CompilationMode.OneOff ->
107107
// This is a one-off compilation, so we don't need to worry about eviction.
108108
{ CacheOptions.Default with
109-
MaximumCapacity = 200_000
109+
MaximumCapacity = 4 * 1024
110110
EvictionMethod = EvictionMethod.NoEviction }
111111
| _ ->
112-
// Oncremental use, so we need to set up the cache with eviction.
112+
// Incremental use, so we need to set up the cache with eviction.
113113
{ CacheOptions.Default with
114114
EvictionMethod = EvictionMethod.Background
115115
PercentageToEvict = 5

src/Compiler/Utilities/Caches.fs

Lines changed: 29 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ open System.Diagnostics.Metrics
1010

1111
[<Struct; RequireQualifiedAccess; NoComparison>]
1212
type EvictionMethod =
13-
| Blocking
1413
| Background
1514
| NoEviction
1615

@@ -28,7 +27,7 @@ type CacheOptions =
2827
MaximumCapacity = 1024
2928
PercentageToEvict = 5
3029
LevelOfConcurrency = Environment.ProcessorCount
31-
EvictionMethod = EvictionMethod.Blocking
30+
EvictionMethod = EvictionMethod.Background
3231
}
3332

3433
[<Sealed; NoComparison; NoEquality>]
@@ -219,9 +218,6 @@ type Cache<'Key, 'Value when 'Key: not null and 'Key: equality> internal (option
219218
false
220219

221220
member _.TryAdd(key: 'Key, value: 'Value) =
222-
if options.EvictionMethod.IsBlocking then
223-
tryEvictItems ()
224-
225221
let cachedEntity = pool.Acquire(key, value)
226222

227223
if store.TryAdd(key, cachedEntity) then
@@ -232,9 +228,6 @@ type Cache<'Key, 'Value when 'Key: not null and 'Key: equality> internal (option
232228
false
233229

234230
member _.AddOrUpdate(key: 'Key, value: 'Value) =
235-
if options.EvictionMethod.IsBlocking then
236-
tryEvictItems ()
237-
238231
let aquired = pool.Acquire(key, value)
239232

240233
let entity =
@@ -277,17 +270,6 @@ type Cache<'Key, 'Value when 'Key: not null and 'Key: equality> internal (option
277270

278271
override this.Finalize() : unit = this.Dispose()
279272

280-
static member Create<'Key, 'Value>(options: CacheOptions) =
281-
// Increase expected capacity by the percentage to evict, since we want to not resize the dictionary.
282-
let capacity =
283-
options.MaximumCapacity
284-
+ int (float options.MaximumCapacity * float options.PercentageToEvict / 100.0)
285-
286-
let cts = new CancellationTokenSource()
287-
let cache = new Cache<'Key, 'Value>(options, capacity, cts)
288-
CacheInstrumentation.AddInstrumentation cache |> ignore
289-
cache
290-
291273
member this.GetStats() = CacheInstrumentation.GetStats(this)
292274

293275
and CacheInstrumentation(cache: ICacheEvents) =
@@ -382,3 +364,31 @@ and CacheInstrumentation(cache: ICacheEvents) =
382364
static member RemoveInstrumentation(cache: ICacheEvents) =
383365
instrumentedCaches[cache].Dispose()
384366
instrumentedCaches.TryRemove(cache) |> ignore
367+
368+
module Cache =
369+
370+
// During testing a lot of compilations are started in app domains and subprocesses.
371+
// This is a reliable way to pass the override to all of them.
372+
[<Literal>]
373+
let private overrideVariable = "FSHARP_CACHE_OVERRIDE"
374+
375+
/// Use for testing purposes to reduce memory consumption in testhost and its subprocesses.
376+
let OverrideMaxCapacityForTesting () =
377+
Environment.SetEnvironmentVariable(overrideVariable, "true", EnvironmentVariableTarget.Process)
378+
379+
let Create<'Key, 'Value when 'Key: not null and 'Key: equality> (options: CacheOptions) =
380+
381+
let options =
382+
match Environment.GetEnvironmentVariable(overrideVariable) with
383+
| null -> options
384+
| _ -> { options with MaximumCapacity = 8 * 1024 }
385+
386+
// Increase expected capacity by the percentage to evict, since we want to not resize the dictionary.
387+
let capacity =
388+
options.MaximumCapacity
389+
+ int (float options.MaximumCapacity * float options.PercentageToEvict / 100.0)
390+
391+
let cts = new CancellationTokenSource()
392+
let cache = new Cache<'Key, 'Value>(options, capacity, cts)
393+
CacheInstrumentation.AddInstrumentation cache |> ignore
394+
cache

src/Compiler/Utilities/Caches.fsi

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ open System.Threading
55

66
[<Struct; RequireQualifiedAccess; NoComparison>]
77
type internal EvictionMethod =
8-
| Blocking
98
| Background
109
| NoEviction
1110

@@ -63,8 +62,6 @@ type internal Cache<'Key, 'Value when 'Key: not null and 'Key: equality> =
6362
member Dispose: unit -> unit
6463
member GetStats: unit -> string
6564

66-
static member Create<'Key, 'Value> : options: CacheOptions -> Cache<'Key, 'Value>
67-
6865
interface ICacheEvents
6966
interface IDisposable
7067

@@ -77,3 +74,7 @@ type internal CacheInstrumentation =
7774
static member GetStatsUpdateForAllCaches: clearCounts: bool -> string
7875
static member AddInstrumentation: cache: ICacheEvents -> unit
7976
static member RemoveInstrumentation: cache: ICacheEvents -> unit
77+
78+
module internal Cache =
79+
val OverrideMaxCapacityForTesting: unit -> unit
80+
val Create<'Key, 'Value when 'Key: not null and 'Key: equality> : options: CacheOptions -> Cache<'Key, 'Value>

tests/FSharp.Test.Utilities/XunitHelpers.fs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,11 @@ type FSharpXunitFramework(sink: IMessageSink) =
145145
// We need AssemblyResolver already here, because OpenTelemetry loads some assemblies dynamically.
146146
AssemblyResolver.addResolver ()
147147
#endif
148+
149+
// Override cache capacity to reduce memory usage in CI.
150+
FSharp.Compiler.Cache.OverrideMaxCapacityForTesting()
151+
152+
let testRunName = $"RunTests_{assemblyName.Name} {Runtime.InteropServices.RuntimeInformation.FrameworkDescription}"
148153

149154
// On Windows forwarding localhost to wsl2 docker container sometimes does not work. Use IP address instead.
150155
let otlpEndpoint = Uri("http://127.0.0.1:4317")
@@ -167,7 +172,8 @@ type FSharpXunitFramework(sink: IMessageSink) =
167172
use meterProvider =
168173
OpenTelemetry.Sdk.CreateMeterProviderBuilder()
169174
.AddMeter(nameof FSharp.Compiler.CacheInstrumentation)
170-
.ConfigureResource(fun r -> r.AddService("F#") |> ignore)
175+
.AddMeter("System.Runtime")
176+
.ConfigureResource(fun r -> r.AddService(testRunName) |> ignore)
171177
.AddOtlpExporter(fun e m ->
172178
e.Endpoint <- otlpEndpoint
173179
e.Protocol <- OpenTelemetry.Exporter.OtlpExportProtocol.Grpc
@@ -180,7 +186,7 @@ type FSharpXunitFramework(sink: IMessageSink) =
180186
TestConsole.install()
181187

182188
begin
183-
use _ = Activity.startNoTags $"RunTests_{assemblyName.Name} {Runtime.InteropServices.RuntimeInformation.FrameworkDescription}"
189+
use _ = Activity.startNoTags testRunName
184190
// We can't just call base.RunTestCases here, because it's implementation is async void.
185191
use runner = new XunitTestAssemblyRunner (x.TestAssembly, testCases, x.DiagnosticMessageSink, executionMessageSink, executionOptions)
186192
runner.RunAsync().Wait()

vsintegration/src/FSharp.Editor/Common/Logging.fs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ module FSharpServiceTelemetry =
177177
.CreateMeterProviderBuilder()
178178
.ConfigureResource(fun r -> r.AddService("F#") |> ignore)
179179
.AddMeter(nameof FSharp.Compiler.CacheInstrumentation)
180+
.AddMeter("System.Runtime")
180181
.AddOtlpExporter(fun e m ->
181182
e.Endpoint <- otlpEndpoint
182183
m.PeriodicExportingMetricReaderOptions.ExportIntervalMilliseconds <- 1000

0 commit comments

Comments
 (0)