python程序的优化经验

经验一:

       在机器学习算法中,有时候会写多重for循环,在多重循环的循环体内会调用某些工具函数。这时候,工具函数的效率就非常关键,因为工具函数经常会被调用100w+次。如果工具函数多执行10000次,那么就要整个for循环跑完就要多执行100亿次。

userIdList = normUserCountVectDict.keys()
actionTypeList = ['0','2','3']
fieldList = ['cat_id','seller_id','brand_id','item_id']

#建立行为的下标映射,方便存储相似性得分
fieldIndex = {}
index = 0
for action in actionTypeList:
        for field in fieldList:
                fieldIndex[action+'#'+field] = index
                index += 1
#这个循环体必须得优化
#因为userIdList的长度是3w+
#循环最多次的循环放在最内层
for classNum in classNumList:
        for actionType in actionTypeList:
                for field in fieldList:
                        normClassVec = normClassCountVectDict[classNum][actionType][field]
                        index = fieldIndex[actionType+'#'+field]
                        for userId in userIdList:
                                normUserVec = normUserCountVectDict[userId][actionType][field]
                                #有些用户的特征向量会是零向量,比如用户无点击行为,必须考虑这种异常情况
                                if len(normUserVec) == 0:
                                        similarity[classNum][userId][index] = 0
                                else:
                                        similarity[classNum][userId][index] = cosSim(normUserVec,normClassVec)

分析:上述4层for循环,第一层12次,第二层3,第三次4次,第四层10w+。总共循环体需要执行1000w+次左右。并且在循环体中,需要调用函数cosSim(normUserVec,normClassVec)。刚开始我没注意到,在cosSim函数里面有些无意义的操作,导致程序非常慢。无意义的操作后来注释掉,程序性能就提高。注释掉的函数vecLength()是比较耗时的,每次只需1000运算,那么1000*1000w=100亿次操作。

def cosSim(dictA,dictB):
        #先判断dictA,dictB的模长是否为1
        #这个太误差检验太耗时,因为这个函数频繁被调用,上亿次,核心函数
        #lengthA = vecLength(dictA)
        #lengthB = vecLength(dictB)
        #数值计算问题,不适合用lengthA=1来判断是否已归一化
        #if (abs(lengthA-1)>0.01) or (abs(lengthB-1)>0.01):
        #       print dictA
        #       print lengthA
        #       print "error:vector not normal"
        commonKey = set(dictA.keys()) & set(dictB.keys())
        sim = 0.0
        for key in commonKey:
                sim += dictA[key]*dictB[key]
        return sim


经验二:多重for循环,最好把循环次数多的循环放在最内层。并且,如果能在外层循环就计算出结果,就不放到内层循环里面

#这个循环体必须得优化
#因为userIdList的长度是3w+
#循环最多次的循环放在最内层
for classNum in classNumList:
        for actionType in actionTypeList:
                for field in fieldList:
                        normClassVec = normClassCountVectDict[classNum][actionType][field]
                        index = fieldIndex[actionType+'#'+field]
                        for userId in userIdList:
                                normUserVec = normUserCountVectDict[userId][actionType][field]
                                #有些用户的特征向量会是零向量,比如用户无点击行为,必须考虑这种异常情况
                                if len(normUserVec) == 0:
                                        similarity[classNum][userId][index] = 0
                                else:
                                        similarity[classNum][userId][index] = cosSim(normUserVec,normClassVec)
分析:上述userId的循环次数是最多的,所以放在内层循环。

normClassVec = normClassCountVectDict[classNum][actionType][field]
index = fieldIndex[actionType+'#'+field]

并且, 上述 两条语句可以在外层循环计算得到结果,就不要放到内层循环里面计算。效率低下。


经验三:

如果需要查询元素达到上亿次,那么就不要把待查询的元素放在list里面,因为list的查询是遍历法,即 if a in list 采用的是遍历的方法去查询,所以,这时候,就要把待查询的元素放在dict或set里面,用空间换时间。


经验四:

在多层循环体内部,最好不要调用函数,因为调用函数需要出栈,入栈操作,会非常耗时间。c++可以使用内联函数,python好像没有这个机制,所以,多重for循环尽量避免调用函数。上面的例子中,多重for循环调用了函数cosSim。是程序性能上的瓶颈,最好不要调用,直接在循环体内实现。

 

for classNum in classNumList:
        for actionType in actionTypeList:
                for field in fieldList:
                        normClassVec = normClassCountVectDict[classNum][actionType][field]
                        index = fieldIndex[actionType+'#'+field]
                        dictAKeys = set(normClassVec.keys())
                        for userId in userIdList:
                                normUserVec = normUserCountVectDict[userId][actionType][field]
                                #有些用户的特征向量会是零向量,比如用户无点击行为,必须考虑这种异常情况
                                if len(normUserVec) == 0:
                                        similarity[classNum][userId][index] = 0
                                else:
                                        #内层循环里面不适合调用函数,耗时
                                        dictBKeys = set(normUserVec.keys())
                                        commonKey = dictAKeys & dictBKeys
                                        sim = 0.0
                                        for key in commonKey:
                                                sim += normClassVec[key]*normUserVec[key]
                                        similarity[classNum][userId][index] = sim

解析:上述程序中,将cosSim函数直接在循环体内实现,使性能有了极大的提高,可以看出在循环体内调用函数确实是非常耗时的。


 






 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值