首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >速度测试导致奇怪的行为。在一个实例中花费的时间乘以100,在另一个实例中仅为10

速度测试导致奇怪的行为。在一个实例中花费的时间乘以100,在另一个实例中仅为10
EN

Stack Overflow用户
提问于 2013-06-20 02:55:39
回答 3查看 119关注 0票数 1

我正在用三个函数做速度测试,readFile,prepDict和test。测试就是简单的prepDict(readFile)。然后,我使用timeit模块多次运行这些代码。

当我将循环数增加10倍时,函数prepDict花费的时间大约是原来的100倍,但是使用函数prepDict的函数测试只增加了10倍。

下面是函数和测试。

代码语言:javascript
运行
复制
def readFile(filepath):
    tempDict = {}
    file = open(filepath,'rb')
    for line in file:
        split = line.split('\t')
        tempDict[split[1]] = split[2]
    return tempDict

def prepDict(tempDict):
    for key in tempDict.keys():
        tempDict[key+'a'] = tempDict[key].upper()
        del tempDict[key]
    return tempDict

def test():
    prepDict(readFile('two.txt'))

if __name__=='__main__':
    from timeit import Timer
    t = Timer(lambda: readFile('two.txt'))
    print 'readFile(10000): ' + str(t.timeit(number=10000))

    tempDict = readFile('two.txt')
    t = Timer(lambda: prepDict(tempDict))
    print 'prepDict (10000): ' + str(t.timeit(number=10000))

    t = Timer(lambda: test())
    print 'prepDict(readFile) (10000): ' + str(t.timeit(number=10000))

    t = Timer(lambda: readFile('two.txt'))
    print 'readFile(100000): ' + str(t.timeit(number=100000))

    tempDict = readFile('two.txt')
    t = Timer(lambda: prepDict(tempDict))
    print 'prepDict (100000): ' + str(t.timeit(number=100000))

    t = Timer(lambda: test())
    print 'prepDict(readFile) (100000): ' + str(t.timeit(number=100000))

我得到的结果如下:

代码语言:javascript
运行
复制
readFile(10000): 0.61602914474
prepDict (10000): 0.200615847469
prepDict(readFile) (10000): 0.609288647286
readFile(100000): 5.91858320729
prepDict (100000): 18.8842101717
prepDict(readFile) (100000): 6.45040039665

如果我多次运行它,也会得到类似的结果。为什么prepDict增加了~100倍,而prepDict(readFile)只增加了10倍,即使它使用的是prepDict函数?

two.txt是一个表格分隔文件,包含以下数据点:

代码语言:javascript
运行
复制
Item    Title   Hello2
Item    Desc    Testing1232
Item    Release 2011-02-03
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2013-06-20 03:12:31

这里的问题是您的prepDict函数扩展了输入。每次按顺序调用它时,它都会有更多的数据需要处理。而且数据呈线性增长,因此第10000次运行所需的时间大约是第一次的10000倍。*

当你调用test时,它每次都会创建一个新的字典,所以时间是恒定的。

通过将prepDict测试更改为每次在新的字典副本上运行,您可以很容易地看到这一点:

代码语言:javascript
运行
复制
t = Timer(lambda: prepDict(tempDict.copy()))

顺便说一句,使用number,你的prepDict实际上并不是成倍增长**,而是呈二次增长。通常,当某些东西呈超线性增长,并且您想要估计算法成本时,您确实需要获得两个以上的数据点。

*这并不完全正确-只有当字符串和散列操作(线性增长)的时间开始淹没其他所有操作(它们都是恒定的)所用的时间时,它才开始线性增长。

**你在这里没有提到任何关于指数增长的东西,但在your previous question中你提到了,所以你可能在你的实际问题中做出了同样的不合理的假设。

票数 3
EN

Stack Overflow用户

发布于 2013-06-20 03:12:29

您对prepDict的调用不是在隔离环境中进行的。每次调用prepDict都会修改tempDict --密钥每次都会变长一点。因此,在对prepDict进行10**5次调用之后,prepDict中的键是相当大的字符串。如果将一条print语句放在prepDict中,就可以(大量)看到这一点

代码语言:javascript
运行
复制
def prepDict(tempDict):
    for key in tempDict.keys():
        tempDict[key+'a'] = tempDict[key].upper()
        del tempDict[key]
    print(tempDict)
    return tempDict

解决这个问题的方法是确保对prepDict的每次调用--或者更广泛地说,您正在计时的语句--不会影响您正在计时的下一次调用(或语句)。abarnert已经展示了解决方案:prepDict(tempDict.copy())

顺便说一句,您可以使用for-loop来减少代码重复:

代码语言:javascript
运行
复制
import timeit
import collections    

if __name__=='__main__':
    Ns = [10**4, 10**5]
    timing = collections.defaultdict(list)
    for N in Ns:
        timing['readFile'].append(timeit.timeit(
            "readFile('two.txt')",
            "from __main__ import readFile",
            number = N))
        timing['prepDict'].append(timeit.timeit(
            "prepDict(tempDict.copy())",
            "from __main__ import readFile, prepDict; tempDict = readFile('two.txt')",
            number = N))
        timing['test'].append(timeit.timeit(
            "test()",
            "from __main__ import test",
            number = N))

    print('{k:10}: {N[0]:7} {N[1]:7} {r}'.format(k='key', N=Ns, r='ratio'))
    for key, t in timing.iteritems():
        print('{k:10}: {t[0]:0.5f} {t[1]:0.5f} {r:>5.2f}'.format(k=key, t=t, r=t[1]/t[0]))

产生计时,例如

代码语言:javascript
运行
复制
key       :   10000  100000 ratio
test      : 0.11320 1.12601  9.95
prepDict  : 0.01604 0.16167 10.08
readFile  : 0.08977 0.91053 10.14
票数 1
EN

Stack Overflow用户

发布于 2013-06-20 03:15:20

之所以会发生这种情况,是因为在仅测试prepDict时,对prepDict的所有调用都重用了tempDict。由于prepDict遍历了字典中给出的所有条目,然后基本上只是将每个字符串键的长度增加了1,因此最终会得到一堆非常长的键。随着它的进展,这开始减慢您的函数的速度,因为字符串连接操作正在使用/重新创建越来越大的字符串。

这在test中不是问题,因为每次都要重新初始化字典。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/17199108

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档