【问题标题】:Why is collections.Counter much slower than ''.count?为什么 collections.Counter 比 ''.count 慢得多?
【发布时间】:2012-12-20 15:02:06
【问题描述】:

我有一个简单的任务:计算每个字母在字符串中出现的次数。我使用了Counter(),但在一个论坛上我看到使用dict() / Counter() 比使用string.count() 慢得多。我认为它只会遍历字符串一次,而string.count() 解决方案必须遍历它四次(在这种情况下)。为什么Counter() 这么慢?

>>> timeit.timeit('x.count("A");x.count("G");x.count("C");x.count("T")', setup="x='GAAAAAGTCGTAGGGTTCCTTCACTCGAGGAATGCTGCGACAGTAAAGGAGGCCACGTGGTTGAGAGTTCCTAAGCATTCGTATGTACACCCGGACTCGATGCACTCAAACGTGCTTAAGGGTAAAGAAGGTCGAGAGGTATACTGGGGCACTCCCCTTAGAATTATATCTTGGTCAACTACAATATGGATGGAAATTCTAAGCCGAAAACGACCCGCTAGCGGATTGTGTATGTATCACAACGGTTTCGGTTCATACGCAAAATCATCCCATTTCAAGGCCACTCAAGGACATGACGCCGTGCAACTCCGAGGACATCCCTCAGCGATTGATGCAACCTGGTCATCTAATAATCCTTAGAACGGATGTGCCCTCTACTGGGAGAGCCGGCTAGACTGGCATCTCGCGTTGTTCGTACGAGCTCCGGGCGCCCGGGCGGTGTACGTTGATGTACAGCCTAAGAGCTTTCCACCTATGCTACGAACTAATTTCCCGTCCATCGTTCCTCGGACTGAGGTCAAAGTAACCCGGAAGTACATGGATCAGATACACTCACAGTCCCCTTTAATGACTGAGCTGGACGCTATTGATTGCTTTATAAGTGTTATGGTGAACTCGAAGACTTAGCTAGGAATTTCGCTATACCCGGGTAATGAGCTTAATACCTCACAGCATGTACGCTCTGAATATATGTAGCGATGCTAGCGGAACGTAAGCGTGAGCGTTATGCAGGGCTCCGCACCTCGTGGCCACTCGCCCAATGCCCGAGTTTTTGAGCAATGCCATGCCCTCCAGGTGAAGCGTGCTGAATATGTTCCGCCTCCGCACACCTACCCTACGGGCCTTACGCCATAGCTGAGGATACGCGAGTTGGTTAGCGATTACGTCATTCCAGGTGGTCGTTC'", number=10000)
0.07911698750407936
>>> timeit.timeit('Counter(x)', setup="from collections import Counter;x='GAAAAAGTCGTAGGGTTCCTTCACTCGAGGAATGCTGCGACAGTAAAGGAGGCCACGTGGTTGAGAGTTCCTAAGCATTCGTATGTACACCCGGACTCGATGCACTCAAACGTGCTTAAGGGTAAAGAAGGTCGAGAGGTATACTGGGGCACTCCCCTTAGAATTATATCTTGGTCAACTACAATATGGATGGAAATTCTAAGCCGAAAACGACCCGCTAGCGGATTGTGTATGTATCACAACGGTTTCGGTTCATACGCAAAATCATCCCATTTCAAGGCCACTCAAGGACATGACGCCGTGCAACTCCGAGGACATCCCTCAGCGATTGATGCAACCTGGTCATCTAATAATCCTTAGAACGGATGTGCCCTCTACTGGGAGAGCCGGCTAGACTGGCATCTCGCGTTGTTCGTACGAGCTCCGGGCGCCCGGGCGGTGTACGTTGATGTACAGCCTAAGAGCTTTCCACCTATGCTACGAACTAATTTCCCGTCCATCGTTCCTCGGACTGAGGTCAAAGTAACCCGGAAGTACATGGATCAGATACACTCACAGTCCCCTTTAATGACTGAGCTGGACGCTATTGATTGCTTTATAAGTGTTATGGTGAACTCGAAGACTTAGCTAGGAATTTCGCTATACCCGGGTAATGAGCTTAATACCTCACAGCATGTACGCTCTGAATATATGTAGCGATGCTAGCGGAACGTAAGCGTGAGCGTTATGCAGGGCTCCGCACCTCGTGGCCACTCGCCCAATGCCCGAGTTTTTGAGCAATGCCATGCCCTCCAGGTGAAGCGTGCTGAATATGTTCCGCCTCCGCACACCTACCCTACGGGCCTTACGCCATAGCTGAGGATACGCGAGTTGGTTAGCGATTACGTCATTCCAGGTGGTCGTTC'", number=10000)
2.1727447831030844
>>> 2.1727447831030844 / 0.07911698750407936
27.462430656767047
>>> 

【问题讨论】:

标签: python performance counter


【解决方案1】:

Counter() 允许您计算任何可散列的对象,而不仅仅是子字符串。两种解决方案都是O(n)-time。您的测量结果表明,通过 Counter() 迭代和散列单个字符的开销大于运行 s.count() 4 次。

Counter() 可以使用 C 助手来计算元素,但它似乎不是特殊情况下的字符串,而是使用适用于任何其他可迭代的通用算法,即processing a single character involves multiple Python C API calls to advance the iterator, get previous value (a lookup in the hash table), increment counter, set new value (a lookup in the hash table)

    while (1) {
        key = PyIter_Next(it);
        if (key == NULL)
            break;
        oldval = PyObject_GetItem(mapping, key);
        if (oldval == NULL) {
            if (!PyErr_Occurred() || !PyErr_ExceptionMatches(PyExc_KeyError))
                break;
            PyErr_Clear();
            Py_INCREF(one);
            newval = one;
        } else {
            newval = PyNumber_Add(oldval, one);
            Py_DECREF(oldval);
            if (newval == NULL)
                break;
        }
        if (PyObject_SetItem(mapping, key, newval) == -1)
            break;
        Py_CLEAR(newval);
        Py_DECREF(key);
    }

将其与字节串的FASTSEARCH() 开销进行比较:

    for (i = 0; i < n; i++)
        if (s[i] == p[0]) {
           count++;
           if (count == maxcount)
              return maxcount;
        }
    return count;

【讨论】:

    【解决方案2】:

    Counter 类继承自 dict,而 string.count 是以下 C 实现 (CPython 3.3):

    /* stringlib: count implementation */
    
    #ifndef STRINGLIB_FASTSEARCH_H
    #error must include "stringlib/fastsearch.h" before including this module
    #endif
    
    
    Py_LOCAL_INLINE(Py_ssize_t)
    STRINGLIB(count)(const STRINGLIB_CHAR* str, Py_ssize_t str_len,
                    const STRINGLIB_CHAR* sub, Py_ssize_t sub_len,
                    Py_ssize_t maxcount)
    {
        Py_ssize_t count;
    
        if (str_len < 0)
            return 0; /* start > len(str) */
        if (sub_len == 0)
            return (str_len < maxcount) ? str_len + 1 : maxcount;
    
        count = FASTSEARCH(str, str_len, sub, sub_len, maxcount, FAST_COUNT);
    
        if (count < 0)
            return 0; /* no match */
    
        return count;
    }
    

    猜猜,哪个更快? :)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-05-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-12-26
      • 2011-01-25
      • 1970-01-01
      • 2017-05-10
      相关资源
      最近更新 更多