// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
usingSystem;
usingSystem.Collections.Generic;
usingSystem.Collections.Immutable;
usingSystem.Diagnostics;
usingSystem.Linq;
usingMicrosoft.CodeAnalysis;
usingMicrosoft.CodeAnalysis.Text;
namespaceRoslyn.Utilities
{
internalpartialclassBKTree
{
privateclassBuilder
{
// The number of edges we pre-allocate space for for each node in _compactEdges.
//
// To make the comments simpler below, i'll use '4' as a synonym for CompactEdgeAllocationSize.
// '4' simply reads better and makes it clearer what's going on.
privateconstintCompactEdgeAllocationSize=4;
// Instead of producing a char[] for each string we're building a node for, we instead
// have one long char[] with all the chracters of each string concatenated. i.e.
// "foo" "bar" and "baz" becomes { f, o, o, b, a, r, b, a, z }. Then in _wordSpans
// we have the text spans for each of those words in this array. This gives us only
// two allocations instead of as many allocations as the number of strings we have.
//
// Once we are done building, we pass this to the BKTree and its nodes also state the
// span of this array that corresponds to the word they were created for. This works
// well as other dependent facilities (like EditDistance) can work on sub-arrays without
// any problems.
privatereadonlychar[]_concatenatedLowerCaseWords;
privatereadonlyTextSpan[]_wordSpans;
// Note: while building a BKTree we have to store children with parents, keyed by the
// edit distance between the two. Naive implementations might store a list or dictionary
// of children along with each node. However, this would be very inefficient and would
// put an enormous amount of memory pressure on the system.
//
// Emperical data for a nice large assembly like mscorlib gives us the following
// information:
//
// Unique-Words (ignoring case): 9662
//
// For each unique word we need a node in the BKTree. If we stored a list or dictionary
// with each node, that would be 10s of thousands of objects created that would then
// just have to be GCed. That's a lot of garbage pressure we'd like to avoid.
//
// Now if we look at all those nodes, we can see the following information about how many
// children each has.
//
// Edge counts:
// 0 5560
// 1 1884
// 2 887
// 3 527
// 4 322
// 5 200
// 6 114
// 7 69
// 8 47
// 9 20
// 10 8
// 11 10
// 12 7
// 13 4
// 15 1
// 16 1
// 54 1
//
//
// i.e. The number of nodes with edge-counts less than or equal to four is: 5560+1884+887+527+322=9180.
// This is 95% of the total number of edges we are adding. Looking at many other dlls
// we found that this ratio stays true across the board. i.e. with all dlls, 95% of nodes
// have 4 or less edges.
//
// So, to optimize things, we pre-alloc a single array with space for 4 edges for each
// node we're going to add. Each node then gets that much space to store edge information.
// If it needs more than that space, then we have a fall-over dictionary that it can store
// information in.
//
// Once building is complete, the GC only needs to deallocate this single array and the
// spillover dictionaries.
//
// This approach produces 1/20th the amount of garbage while building the tree.
//
// Each node at index i has its edges in this array in the range [4*i, 4*i + 4);
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespaceRoslyn.Utilities
{
internalpartialclassBKTree
{
privatestructEdge
{
// The edit distance between the child and parent connected by this edge.
// The child can be found in _nodes at ChildNodeIndex.
publicreadonlyintEditDistance;
/// <summary>Where the child node can be found in <see cref="_nodes"/>.</summary>
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
usingMicrosoft.CodeAnalysis.Text;
namespaceRoslyn.Utilities
{
internalpartialclassBKTree
{
privatestructNode
{
/// <summary>
/// The string this node corresponds to. Specifically, this span is the range of
/// <see cref="_concatenatedLowerCaseWords"/> for that string.
/// </summary>
publicreadonlyTextSpanWordSpan;
///<summary>How many child edges this node has.</summary>
publicreadonlyintEdgeCount;
///<summary>Where the first edge can be found in <see cref="_edges"/>. The edges
///are in the range _edges[FirstEdgeIndex, FirstEdgeIndex + EdgeCount)
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
usingSystem.Collections.Generic;
usingSystem.Collections.Immutable;
usingSystem.Linq;
usingSystem.Text;
usingMicrosoft.CodeAnalysis;
usingRoslyn.Utilities;
usingstaticSystem.Math;
namespaceRoslyn.Utilities
{
/// <summary>
/// NOTE: Only use if you truly need a BK-tree. If you just want to compare words, use
/// the <see cref="SpellChecker"/> type instead.
///
/// An implementation of a Burkhard-Keller tree. Introduced in:
///
/// 'Some approaches to best-match file searching.'
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.