Boyer Moore在C#中的实践?

Boyer Moore在C#中的实践?,c#,.net,algorithm,performance,boyer-moore,C#,.net,Algorithm,Performance,Boyer Moore,Boyer Moore可能是已知最快的非索引文本搜索算法。所以我在我的网站上用C#实现它 我让它工作起来,与String.IndexOf()相比,它大致显示了预期的性能改进。然而,当我将StringComparison.Ordinal参数添加到IndexOf时,它的性能开始超过我的Boyer-Moore实现。有时,会有相当大的数量 我想知道是否有人能帮我找出原因。我理解为什么StringComparison.Ordinal可能会加快速度,但它怎么能比Boyer Moore更快呢?这是因为.NET

Boyer Moore可能是已知最快的非索引文本搜索算法。所以我在我的网站上用C#实现它

我让它工作起来,与
String.IndexOf()
相比,它大致显示了预期的性能改进。然而,当我将
StringComparison.Ordinal
参数添加到
IndexOf
时,它的性能开始超过我的Boyer-Moore实现。有时,会有相当大的数量

我想知道是否有人能帮我找出原因。我理解为什么StringComparison.Ordinal可能会加快速度,但它怎么能比Boyer Moore更快呢?这是因为.NET平台本身的开销,可能是因为必须验证数组索引以确保它们在范围内,或者是其他原因。有些算法在C#NET中不实用吗

下面是关键代码

// Base for search classes
abstract class SearchBase
{
    public const int InvalidIndex = -1;
    protected string _pattern;
    public SearchBase(string pattern) { _pattern = pattern; }
    public abstract int Search(string text, int startIndex);
    public int Search(string text) { return Search(text, 0); }
}

/// <summary>
/// A simplified Boyer-Moore implementation.
/// 
/// Note: Uses a single skip array, which uses more memory than needed and
/// may not be large enough. Will be replaced with multi-stage table.
/// </summary>
class BoyerMoore2 : SearchBase
{
    private byte[] _skipArray;

    public BoyerMoore2(string pattern)
        : base(pattern)
    {
        // TODO: To be replaced with multi-stage table
        _skipArray = new byte[0x10000];

        for (int i = 0; i < _skipArray.Length; i++)
            _skipArray[i] = (byte)_pattern.Length;
        for (int i = 0; i < _pattern.Length - 1; i++)
            _skipArray[_pattern[i]] = (byte)(_pattern.Length - i - 1);
    }

    public override int Search(string text, int startIndex)
    {
        int i = startIndex;

        // Loop while there's still room for search term
        while (i <= (text.Length - _pattern.Length))
        {
            // Look if we have a match at this position
            int j = _pattern.Length - 1;
            while (j >= 0 && _pattern[j] == text[i + j])
                j--;

            if (j < 0)
            {
                // Match found
                return i;
            }

            // Advance to next comparision
            i += Math.Max(_skipArray[text[i + j]] - _pattern.Length + 1 + j, 1);
        }
        // No match found
        return InvalidIndex;
    }
}
//搜索类的基
抽象类搜索库
{
公共常数无效指数=-1;
受保护的字符串\u模式;
公共搜索库(字符串模式){u pattern=pattern;}
公共摘要int搜索(字符串文本,int startIndex);
公共整数搜索(字符串文本){返回搜索(文本,0);}
}
/// 
///简化的Boyer-Moore实现。
/// 
///注意:使用单个跳过数组,占用的内存比需要的多,并且
///可能不够大。将替换为多级表。
/// 
类别BoyerMoore2:搜索库
{
专用字节[]_skipArray;
公共BoyerMoore2(字符串模式)
:基础(图案)
{
//TODO:将替换为多级表
_skipArray=新字节[0x10000];
对于(int i=0;i<\u skipArray.Length;i++)
_skipArray[i]=(字节)_pattern.Length;
对于(int i=0;i<_pattern.Length-1;i++)
_skipArray[_pattern[i]]=(字节)(_pattern.Length-i-1);
}
公共覆盖整型搜索(字符串文本,整型起始索引)
{
int i=起始索引;
//在搜索词仍有空间时循环
而(i=0&&&u模式[j]==text[i+j])
j--;
if(j<0)
{
//找到匹配项
返回i;
}
//进行下一次比较
i+=Math.Max(_skipArray[text[i+j]]-_pattern.Length+1+j,1);
}
//没有找到匹配项
返回无效索引;
}
}

编辑:我已经在上发布了关于这件事的所有测试代码和结论。

我打赌设置该标志允许String.IndexOf使用Boyer Moore本身。它的实现比你的好


如果没有这个标志,它必须小心使用Boyer Moore(可能不会),因为Unicode可能存在潜在问题。特别是Unicode的可能性导致Boyer Moore使用的转换表爆炸。

根据我自己的测试和这里的评论,我得出结论,原因是
String.IndexOf()
stringcomparison中执行得非常好。Ordinal
是因为该方法调用可能使用手工优化汇编语言的非托管代码

我已经运行了许多不同的测试,
String.IndexOf()
似乎比使用托管C#code实现的任何测试都要快


如果有人感兴趣的话,我已经写了我所发现的关于这一点的所有东西,并在C#at中发布了Boyer-Moore算法的几个变体。

我对您的代码做了一些小改动,并对Boyer-Moore算法进行了不同的实现,获得了更好的结果。 我的想法是从

但老实说,我希望达到比
IndexOf
更高的速度


Jonathan,没有“C#NET”这样的东西。你是否完全排除了Boyer Moore在.NET内部工作的可能性?只是好奇而已。请看,尤其是公认答案下的评论。@Nikita:没有,在红门几乎毁了.NET Reflector之后,我花了最后30分钟试图启动它。然而,它似乎进入了一个.NETReflector无法显示的例程。(可能是非托管代码?)?我甚至没有在计时中包含跳过数组设置。非托管代码能提供那么多性能优势吗?@Hightechrider:谢谢你的参考,不过没有那么多帮助。他们基本上猜测使用Boyer Moore的
IndexOf()
,并讨论Unicode的问题(我已经开发了正确处理的代码)。鉴于我使用的是一些非常标准的实现,这将是一个巧妙的技巧。(顺便说一句,它们不是我的。)但是,如果它在非托管代码中运行,这可能会提供一些性能优势;搜索长字符串(“abcdefghijklmnopqrstuvwxyz”)应该比搜索短字符串(“a”)快得多。@Gabe:这一点很好,但似乎不是。不管怎样,它看起来都很快。另一方面,我的Boyer-Moore例程肯定会受到搜索模式长度的影响。嗯,是不是.NET在不同的情况下使用不同的算法?你没有理由在这里下注。有人在Reflector中打开它并检查了吗?对于可能发现它有用的其他人,String.Contains()调用String.IndexOf(value,StringComparison.Ordinal)>=0。因此,结论是使用String.Conatinst()进行字符串搜索。此结论依赖于数据。Boyer Moore可以在适当的数据上任意加快速度。@usr:好吧,如果你有证据表明在某些情况下速度更快,请随时提供证据。我对Boyer Moore有着透彻的理解,并且完全理解它对于某些数据是如何更快的,但我测试了各种数据,并且
String.IndexOf()
似乎更快,几乎是一致的。@ValidfroM:
String.Contains()
没有告诉你答案
class SearchResults
{
    public int Matches { get; set; }
    public long Ticks { get; set; }
}

abstract class SearchBase
{
    public const int InvalidIndex = -1;
    protected string _pattern;
    protected string _text;
    public SearchBase(string text, string pattern) { _text = text; _pattern = pattern; }
    public abstract int Search(int startIndex);
}

internal class BoyerMoore3 : SearchBase
{
    readonly byte[] textBytes;
    readonly byte[] patternBytes;
    readonly int valueLength;
    readonly int patternLength;
    private readonly int[] badCharacters = new int[256];
    private readonly int lastPatternByte;

    public BoyerMoore3(string text, string pattern) : base(text, pattern)
    {
        textBytes = Encoding.UTF8.GetBytes(text);
        patternBytes = Encoding.UTF8.GetBytes(pattern);
        valueLength = textBytes.Length;
        patternLength = patternBytes.Length;

        for (int i = 0; i < 256; ++i)
            badCharacters[i] = patternLength;

        lastPatternByte = patternLength - 1;

        for (int i = 0; i < lastPatternByte; ++i)
            badCharacters[patternBytes[i]] = lastPatternByte - i;
    }

    public override int Search(int startIndex)
    {
        int index = startIndex;

        while (index <= (valueLength - patternLength))
        {
            for (int i = lastPatternByte; textBytes[index + i] == patternBytes[i]; --i)
            {
                if (i == 0)
                    return index;
            }

            index += badCharacters[textBytes[index + lastPatternByte]];
        }

        // Text not found
        return InvalidIndex;
    }
}
    private void RunSearch(string pattern, SearchBase search, SearchResults results)
    {
        var timer = new Stopwatch();

        // Start timer
        timer.Start();

        // Find all matches
        int pos = search.Search(0);
        while (pos != -1)
        {
            results.Matches++;
            pos = search.Search(pos + pattern.Length);
        }

        // Stop timer
        timer.Stop();

        // Add to total Ticks
        results.Ticks += timer.ElapsedTicks;
    }