【问题标题】:Word Fast Counting单词快速计数
【发布时间】:2015-03-04 00:58:06
【问题描述】:

我正在为我的 ASP.NET 服务器实现字数统计功能,我想知道这样做的最快方法是什么,因为我不确定是否使用简单

text.AsParallel().Count(Char.IsWhiteSpace);

是最快的方法。由于此功能可能会在相对较长的文本墙上使用相当多,因此我希望它尽可能快,即使这意味着使用不安全的方法。

编辑:使用 Rufus L 的代码以及我自己的不安全方法进行一些基准测试:

public static unsafe int CountWords(string s)
    {
        int count = 0;
        fixed (char* ps = s)
        {
            int len = s.Length;
            char* pc = ps;
            while (len-- > 0)
            {
                if (char.IsWhiteSpace(*pc++))
                {
                    count++;
                }
            }
        }
        return count;
    }

Split(null):681979 个单词,415867 个刻度。

Count(WhiteSpace):681978 个单词,147860 个刻度。

AsParallel:401077 个刻度中的 681978 个单词。

不安全:681978 个单词,98139 个刻度。

我仍然对任何更好的想法持开放态度:)

EDIT2:

重写函数,同时处理多个空格:

public static unsafe int CountWords(string s)
    {
        int count = 0;
        fixed (char* ps = s)
        {
            int len = s.Length;
            bool inWord = false;
            char* pc = ps;
            while (len-- > 0)
            {
                if (char.IsWhiteSpace(*pc++))
                {
                    if (!inWord)
                    {
                        inWord = true;
                    }
                }
                else
                {
                    if (inWord)
                    {
                        inWord = false;
                        count++;
                    }
                }
                if (len == 0)
                {
                    if (inWord)
                    {
                        count++;
                    }
                }
            }
        }
        return count;
    }

Split(null):681979 个单词,517055 个刻度。

Count(WhiteSpace):681978 个单词,148952 个刻度。

AsParallel:681978 个单词,410289 个刻度。

不安全:660000 个单词,114833 个刻度。

【问题讨论】:

  • 两点 1) 先做基准。它是线性且便宜的,即使在“长长的文本墙”上也意味着非常非常快 2) AsParallel 在服务器端应用程序上没有多大意义。
  • 我已经编辑了你的标题。请参阅“Should questions include “tags” in their titles?”,其中的共识是“不,他们不应该”。

标签: c# asp.net performance optimization count


【解决方案1】:

根据我的测试,这要快得多,提高了 4 倍(但请参阅下面的更新了解不同的结果):

wordCount = text.Split(null).Length;

这是测试,如果您想尝试一下。请注意,由于任务切换的成本,添加 AsParallel() 会减慢我机器上的进程:

public static void Main()
{
    var text = File.ReadAllText("d:\\public\\temp\\temp.txt");
    int wordCount;
    var sw = new Stopwatch();

    sw.Start();
    wordCount = text.Split(null).Length;
    sw.Stop();

    Console.WriteLine("Split(null): {0} words in {1} ticks.", wordCount, 
        sw.ElapsedTicks);

    sw.Restart();
    wordCount = text.Count(Char.IsWhiteSpace);
    sw.Stop();

    Console.WriteLine("Count(WhiteSpace): {0} words in {1} ticks.", wordCount, 
        sw.ElapsedTicks);

    sw.Restart();
    wordCount = text.AsParallel().Count(Char.IsWhiteSpace);
    sw.Stop();

    Console.WriteLine("AsParallel: {0} words in {1} ticks.", wordCount, 
        sw.ElapsedTicks);
}

输出:

Split(null):629 个刻度中的 964 个单词。

Count(WhiteSpace):2377 个刻度中的 963 个单词。

AsParallel:963 个单词,208983 个刻度。

更新

在使字符串变得更长(OP提到100个单词中的100个单词)之后,结果变得更加相似,并且Count(WhiteSpace)方法变得比Split(null)方法更快:

代码更改:

var text = File.ReadAllText("d:\\public\\temp\\temp.txt");
var textToSearch = new StringBuilder();
for (int i = 0; i < 500; i++) textToSearch.Append(text);
text = textToSearch.ToString();

输出:

Split(null):185135 个刻度中的 481501 个单词。

Count(WhiteSpace):481500 个单词,101373 个刻度。

AsParallel:481500 个单词,336117 个刻度。

【讨论】:

  • 除了它会创建一堆对象。就我而言,这可能意味着成千上万的对象 >_>
  • 对不起,我以为你要求的是最快的,而不是最节省内存的......
  • 这就是回答这些跑题的性能问题的问题。
  • @Dracor 我更新了我的代码以解析接近 500,000 个单词,这稍微改变了结果,Count(WhiteSpace) 超过了Split(null)。我的机器上的内存不是问题(我有 30 GB)。我猜你只需要在你的服务器上进行测试!
  • @RufusL 谢谢,这是一个很好的起点,也可以用于以后的基准比较^^
【解决方案2】:

经过一些基准测试,以下 unsage 代码在任何情况下都产生了最快的结果,包含 500 多个单词:

public static unsafe int CountWords(string s)
{
    int count = 0;
    fixed (char* ps = s)
    {
        int len = s.Length;
        bool inWord = false;
        char* pc = ps;
        while (len-- > 0)
        {
            if (char.IsWhiteSpace(*pc++))
            {
                if (!inWord)
                {
                    inWord = true;
                }
            }
            else
            {
                if (inWord)
                {
                    inWord = false;
                    count++;
                }
            }
            if (len == 0)
            {
                if (inWord)
                {
                    count++;
                }
            }
        }
    }
    return count;
}

【讨论】:

    【解决方案3】:

    回答您的问题,方法 AsParallel() 非常快。但存在更多选择,例如:

    使用正则表达式:

    string input = "text text text text";
    string pattern = "(-)";
    
    string[] substrings = Regex.Split(input, pattern);    // Split on hyphens
    Console.WriteLine("Words: {0}", substrings.count());
    

    但我重申,AsParallel() 方法非常快。你可以做一个概念验证,找出哪个更好。在代码的开头放置一个秒表 (),在代码的末尾放置另一个,并将 AsParallel 运行时 () 与正则表达式进行比较,这样会有一个更“准确”的答案。

    更新

    使用 Parallel.For:

    static void Main(string[] args)
            {
                string text = @"text text text text text text text text text text ";
                int count = 0;
    
                Console.WriteLine("Generating words, wait...");
                Parallel.For(0, 100000, i =>
                {
                    text += @"text text text text text text text text text text ";
                });
    
    
                var sw = new Stopwatch();
                sw.Start();
                Parallel.For(0, text.Length, i =>
                {
                    if (char.IsWhiteSpace(text[i]))
                        count++;
                });
                sw.Stop();
    
                Console.WriteLine("Words: {0} in {1} ticks", count, sw.ElapsedTicks);
                Console.ReadLine();
            }
    

    结果:

    PS:请注意,Parallel.For used 是不受管理的

    【讨论】:

    • 在我的测试中,AsParallel 对于这个特定问题要慢得多。我认为对于非计算密集型操作来说,任务切换花费了太多时间。
    • 你可以处理多少个“~”字?
    猜你喜欢
    • 1970-01-01
    • 2013-12-12
    • 2020-09-23
    • 2014-11-07
    • 1970-01-01
    • 2014-05-02
    • 2016-02-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多