python - sparse_hash_map 对于特定数据非常慢

标签 python c++ performance hashmap cython

tl;dr: 为什么要在 sparse_hash_map 中查找 key 特定数据的速度变慢了大约 50 倍?


我正在测试 sparse_hash_map键查找速度来自 Google 的 sparsehash 库,使用我编写的非常简单的 Cython 包装器。哈希表包含 uint32_t键和 uint16_t值。对于随机键、值和查询,我得到超过 1M 的查找/秒。但是,对于我需要的特定数据,性能几乎不会超过每秒 2 万次查找。

包装器是 here .运行缓慢的表是here .基准测试代码是:

benchmark.pyx :

from sparsehash cimport SparseHashMap
from libc.stdint cimport uint32_t
from libcpp.vector cimport vector
import time
import numpy as np

def fill_randomly(m, size):
    keys = np.random.random_integers(0, 0xFFFFFFFF, size)
    # 0 is a special domain-specific value
    values = np.random.random_integers(1, 0xFFFF, size)
    for j in range(size):
        m[keys[j]] = values[j]

def benchmark_get():
    cdef int dummy
    cdef uint32_t i, j, table_key
    cdef SparseHashMap m
    cdef vector[uint32_t] q_keys
    cdef int NUM_QUERIES = 1000000
    cdef uint32_t MAX_REQUEST = 7448 * 2**19 - 1  # this is domain-specific

    time_start = time.time()

    ### OPTION 1 ###
    m = SparseHashMap('17.shash')

    ### OPTION 2 ###
    # m = SparseHashMap(16130443)
    # fill_randomly(m, 16130443)

    q_keys = np.random.random_integers(0, MAX_REQUEST, NUM_QUERIES)

    print("Initialization: %.3f" % (time.time() - time_start))

    dummy = 0

    time_start = time.time()

    for i in range(NUM_QUERIES):
        table_key = q_keys[i]
        dummy += m.get(table_key)
        dummy %= 0xFFFFFF  # to prevent overflow error

    time_elapsed = time.time() - time_start

    if dummy == 42:
        # So that the unused variable is not optimized away
        print("Wow, lucky!")

    print("Table size: %d" % len(m))
    print("Total time: %.3f" % time_elapsed)
    print("Seconds per query: %.8f" % (time_elapsed / NUM_QUERIES))
    print("Queries per second: %.1f" % (NUM_QUERIES / time_elapsed))

def main():
    benchmark_get()

benchmark.pyxbld (因为 pyximport 应该在 C++ 模式下编译):

def make_ext(modname, pyxfilename):
    from distutils.extension import Extension
    return Extension(
        name=modname,
        sources=[pyxfilename],
        language='c++'
    )

run.py :

import pyximport
pyximport.install()

import benchmark
benchmark.main()

17.shash 的结果是:

Initialization: 2.612
Table size: 16130443
Total time: 48.568
Seconds per query: 0.00004857
Queries per second: 20589.8

对于随机数据:

Initialization: 25.853
Table size: 16100260
Total time: 0.891
Seconds per query: 0.00000089
Queries per second: 1122356.3

17.shash中的 key 分布这是 ( plt.hist(np.fromiter(m.keys(), dtype=np.uint32, count=len(m)), bins=50) ):

histogram

来自 sparsehash 上的文档和 gcc 似乎这里使用了普通散列(即 x 散列为 x )。

除了哈希冲突之外,是否有任何明显的因素可能导致此行为?根据我的发现,在 Cython 包装器中集成自定义哈希函数(即重载 std::hash<uint32_t>)并非易事。

最佳答案

我找到了一个可行的解决方案,但它并不完美。

sparsehash_wrapper.cpp:

#include "sparsehash/sparse_hash_map"
#include "stdint.h"

// syntax borrowed from
// https://stackoverflow.com/questions/14094104/google-sparse-hash-with-murmur-hash-function

struct UInt32Hasher {
    size_t operator()(const uint32_t& x) const {
        return (x ^ (x << 17) ^ (x >> 13) + 3238229671);
    }    
};

template<class Key, class T>
class sparse_hash_map : public google::sparse_hash_map<Key, T, UInt32Hasher> {};

这是一个自定义哈希函数,我可以将其集成到现有的包装器中,只需进行最少的代码更改:我只需将 sparsehash/sparse_hash_map 替换为 sparsehash_wrapper.cpp 的路径> 在 Cython .pxd 文件中。到目前为止,唯一的问题是 pyximport 找不到 sparsehash_wrapper.cpp 除非我在 .pxd 中指定完整的绝对路径。

问题确实与冲突有关:在从头开始重新创建与 17.shash 具有相同内容的 HashMap 之后(即,创建一个空映射并从中插入每个(键,值)对17.shash 到其中),性能高达 1M+ 请求/秒。

关于python - sparse_hash_map 对于特定数据非常慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34296868/

相关文章:

c++ - 为 LD_PRELOAD 设置我的库会使某些进程产生加载程序错误

algorithm - 三重嵌套循环的复杂性

python - 将菜单中复选按钮的默认值设置为 True

Python Pandas 计算不包括当前组的标准偏差,带有矢量化解决方案

python - Pandas csv 阅读器无法识别分隔符

c++ - string_t from(wstring OR string) when string_t 也可以是两者的类型定义

Python falcon 和异步操作

c++ - for 循环中条件表达式的计算

MySQL DB 每天大量记录会影响性能吗?

c# - Windows 性能监视器中的性能计数器计时器值不准确