Skip to content
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

TcSymbolUseData cleanup per #6084 #6089

Merged
merged 5 commits into from
Jan 17, 2019
Merged
Show file tree
Hide file tree
Changes from 3 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
48 changes: 47 additions & 1 deletion src/absil/illib.fs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ let inline isNonNull x = not (isNull x)
let inline nonNull msg x = if isNull x then failwith ("null: " + msg) else x
let inline (===) x y = LanguagePrimitives.PhysicalEquality x y

/// Per the docs the threshold for the Large Object Heap is 85000 bytes: https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/large-object-heap#how-an-object-ends-up-on-the-large-object-heap-and-how-gc-handles-them
/// We set the limit to slightly under that to allow for some 'slop'
let LOH_SIZE_THRESHOLD_BYTES = 84_900

//---------------------------------------------------------------------
// Library: ReportTime
//---------------------------------------------------------------------
Expand Down Expand Up @@ -91,7 +95,7 @@ module Order =
let toFunction (pxOrder: IComparer<'U>) x y = pxOrder.Compare(x,y)

//-------------------------------------------------------------------------
// Library: arrays,lists,options
// Library: arrays,lists,options,resizearrays
//-------------------------------------------------------------------------

module Array =
Expand Down Expand Up @@ -432,6 +436,48 @@ module List =
let existsSquared f xss = xss |> List.exists (fun xs -> xs |> List.exists (fun x -> f x))
let mapiFoldSquared f z xss = mapFoldSquared f z (xss |> mapiSquared (fun i j x -> (i,j,x)))

module ResizeArray =

/// Split a ResizeArray into an array of smaller chunks.
/// This requires `items/chunkSize` Array copies of length `chunkSize` if `items/chunkSize % 0 = 0`,
/// otherwise `items/chunkSize + 1` Array copies.
let chunkBySize chunkSize f (items: ResizeArray<'t>) =
// we could use Seq.chunkBySize here, but that would involve many enumerator.MoveNext() calls that we can sidestep with a bit of math
let itemCount = items.Count
if itemCount = 0
then [||]
else
let chunksCount =
match itemCount / chunkSize with
| n when itemCount % chunkSize = 0 -> n
| n -> n + 1 // any remainder means we need an additional chunk to store it

[| for index in 0..chunksCount-1 do
let startIndex = index * chunkSize
let takeCount = min (itemCount - startIndex) chunkSize

let holder = Array.zeroCreate takeCount
// we take a bounds-check hit here on each access.
// other alternatives here include
// * iterating across an IEnumerator (incurs MoveNext penalty)
// * doing a block copy using `List.CopyTo(index, array, index, count)` (requires more copies to do the mapping)
// none are significantly better.
for i in 0 .. takeCount - 1 do
holder.[i] <- f items.[i]
yield holder |]

/// Split a large ResizeArray into a series of array chunks that are each under the
/// Large Object Heap limit, in order to prevent the entire array from not being garbage-collected.
cartermp marked this conversation as resolved.
Show resolved Hide resolved
let mapToSmallArrayChunks f (inp: ResizeArray<'t>) =
let itemSizeBytes = sizeof<'t>
// rounding down here is good because it ensures we don't go over
let maxArrayItemCount = LOH_SIZE_THRESHOLD_BYTES / itemSizeBytes

/// chunk the provided input into arrays that are smaller than the LOH limit
/// in order to prevent long-term storage of those values
chunkBySize maxArrayItemCount f inp


/// Because FSharp.Compiler.Service is a library that will target FSharp.Core 4.5.2 for the forseeable future,
/// we need to stick these functions in this module rather than using the module functions for ValueOption
/// that come after FSharp.Core 4.5.2.
Expand Down
18 changes: 11 additions & 7 deletions src/fsharp/NameResolution.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1212,7 +1212,7 @@ let GetNestedTypesOfType (ad, ncenv:NameResolver, optFilter, staticResInfo, chec
//-------------------------------------------------------------------------

/// Represents the kind of the occurrence when reporting a name in name resolution
[<RequireQualifiedAccess>]
[<RequireQualifiedAccess; Struct>]
type ItemOccurence =
/// This is a binding / declaration of the item
| Binding
Expand Down Expand Up @@ -1496,17 +1496,21 @@ type TcSymbolUseData =
/// This is a memory-critical data structure - allocations of this data structure and its immediate contents
/// is one of the highest memory long-lived data structures in typical uses of IDEs. Not many of these objects
/// are allocated (one per file), but they are large because the allUsesOfAllSymbols array is large.
type TcSymbolUses(g, capturedNameResolutions : ResizeArray<CapturedNameResolution>, formatSpecifierLocations: (range * int)[]) =
type TcSymbolUses(g, capturedNameResolutions : ResizeArray<CapturedNameResolution>, formatSpecifierLocations: (range * int)[]) =
cartermp marked this conversation as resolved.
Show resolved Hide resolved

// Make sure we only capture the information we really need to report symbol uses
let allUsesOfSymbols = [| for cnr in capturedNameResolutions -> { Item=cnr.Item; ItemOccurence=cnr.ItemOccurence; DisplayEnv=cnr.DisplayEnv; Range=cnr.Range } |]
let allUsesOfSymbols =
capturedNameResolutions
|> ResizeArray.mapToSmallArrayChunks (fun cnr -> { Item=cnr.Item; ItemOccurence=cnr.ItemOccurence; DisplayEnv=cnr.DisplayEnv; Range=cnr.Range })

let capturedNameResolutions = ()
do ignore capturedNameResolutions // don't capture this!

member this.GetUsesOfSymbol(item) =
[| for symbolUse in allUsesOfSymbols do
if protectAssemblyExploration false (fun () -> ItemsAreEffectivelyEqual g item symbolUse.Item) then
yield symbolUse |]
[| for symbolUseChunk in allUsesOfSymbols do
for symbolUse in symbolUseChunk do
if protectAssemblyExploration false (fun () -> ItemsAreEffectivelyEqual g item symbolUse.Item) then
yield symbolUse |]
baronfel marked this conversation as resolved.
Show resolved Hide resolved

member this.AllUsesOfSymbols = allUsesOfSymbols

Expand Down
4 changes: 2 additions & 2 deletions src/fsharp/NameResolution.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ type TypeNameResolutionInfo =
static member ResolveToTypeRefs : TypeNameResolutionStaticArgsInfo -> TypeNameResolutionInfo

/// Represents the kind of the occurrence when reporting a name in name resolution
[<RequireQualifiedAccess>]
[<RequireQualifiedAccess; Struct>]
type internal ItemOccurence =
| Binding
| Use
Expand Down Expand Up @@ -319,7 +319,7 @@ type internal TcSymbolUses =
member GetUsesOfSymbol : Item -> TcSymbolUseData[]

/// All the uses of all items within the file
member AllUsesOfSymbols : TcSymbolUseData[]
member AllUsesOfSymbols : TcSymbolUseData[][]

/// Get the locations of all the printf format specifiers in the file
member GetFormatSpecifierLocationsAndArity : unit -> (range * int)[]
Expand Down
6 changes: 4 additions & 2 deletions src/fsharp/service/service.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1913,7 +1913,8 @@ type FSharpCheckProjectResults(projectFileName:string, tcConfigOption, keepAssem
let cenv = SymbolEnv(tcGlobals, thisCcu, Some ccuSig, tcImports)

[| for r in tcSymbolUses do
for symbolUse in r.AllUsesOfSymbols do
for symbolUseChunk in r.AllUsesOfSymbols do
for symbolUse in symbolUseChunk do
if symbolUse.ItemOccurence <> ItemOccurence.RelatedText then
let symbol = FSharpSymbol.Create(cenv, symbolUse.Item)
yield FSharpSymbolUse(tcGlobals, symbolUse.DisplayEnv, symbol, symbolUse.ItemOccurence, symbolUse.Range) |]
Expand Down Expand Up @@ -2107,7 +2108,8 @@ type FSharpCheckFileResults(filename: string, errors: FSharpErrorInfo[], scopeOp
(fun () -> [| |])
(fun scope ->
let cenv = scope.SymbolEnv
[| for symbolUse in scope.ScopeSymbolUses.AllUsesOfSymbols do
[| for symbolUseChunk in scope.ScopeSymbolUses.AllUsesOfSymbols do
for symbolUse in symbolUseChunk do
if symbolUse.ItemOccurence <> ItemOccurence.RelatedText then
let symbol = FSharpSymbol.Create(cenv, symbolUse.Item)
yield FSharpSymbolUse(scope.TcGlobals, symbolUse.DisplayEnv, symbol, symbolUse.ItemOccurence, symbolUse.Range) |])
Expand Down