python脚本速度改进

时间:2014-05-26 14:51:20

标签: python performance

我的机器人我正在分析激光测距数据。我需要每秒分析大量样本。所以速度是必需的。 我知道python不是基于此的正确语言 - 但是我现在不想切换,因为我处于原型设计阶段(将会看到我是否已经离开它:-))。

目前我一直在努力从我的分析代码中挤出更多的速度。

我拿出相关代码并创建了一个小测试。如果有人能给我一些关于在这个测试脚本中提高速度的提示,那就太棒了。

from math import degrees, radians, sin, cos, fabs
import time

class NewRobotMap(object):
    def __init__(self, sizeX, sizeY, Resolution, RobotPosX, RobotPosY, RobotTheta, ServoPos, mapMaxOcc, mapMaxFree, OccValue, EmptyValue):
        self.sizeX = sizeX
        self.sizeY = sizeY
        self.RobotPosX = int(RobotPosX)
        self.RobotPosY = int(RobotPosY)
        self.mapResolution = int(Resolution)
        self.StartPosX = int(RobotPosX)
        self.StartPosY = int(RobotPosY)
        self.RobotTheta = float(RobotTheta)
        self.EmptyValue = EmptyValue
        self.ServoPos = ServoPos
        self.mapMaxOcc = mapMaxOcc
        self.mapMaxFree = mapMaxFree
        self.mapOccValue = OccValue
        self.RobotPosOldX = ""
        self.RobotPosOldY = ""

    def clear(self):
        self.RobotMap = [[self.EmptyValue for i in xrange(self.sizeY)] for j in xrange(self.sizeX)]
    def updateMap(self ,x ,y , Val):
        oldval = self.RobotMap[x][y]
        self.RobotMap[x][y]=self.RobotMap[x][y] + Val

        if self.RobotMap[x][y] > self.mapMaxOcc:
            self.RobotMap[x][y] = self.mapMaxOcc
        elif self.RobotMap[x][y] < self.mapMaxFree:
            self.RobotMap[x][y] = self.mapMaxFree
        return oldval, self.RobotMap[x][y]
    def setOcc(self,x,y):
            self.RobotMap[x][y] = self.mapMaxOcc
    def updateRobot(self,theta,x,y):
        robotThetaold=self.RobotTheta

        self.RobotTheta = float(theta)
        self.RobotPosX = int(round(self.StartPosX + float(int(x)/self.mapResolution), 0))
        self.RobotPosY = int(round(self.StartPosY - float(int(y)/self.mapResolution),0))

        if x != self.RobotPosOldX or y != self.RobotPosOldX:
            self.RobotPosOldX = x
            self.RobotPosOldY = y
            return True
        else:
            self.RobotPosOldX = x
            self.RobotPosOldY = y
            return False
    def getRobotPos(self):
        return self.RobotPosX, self.RobotPosY
    def display(self):
        s = [[str(e) for e in row] for row in self.RobotMap]
        lens = [len(max(col, key=len)) for col in zip(*s)]
        fmt = '\t'.join('{{:{}}}'.format(x) for x in lens)
        table = [fmt.format(*row) for row in s]
        print '\n'.join(table)
    def updateServoPos(self, newServoPos):
        self.ServoPos = newServoPos


templateData = {
    'MapWidth' : 800,
    'MapHeight': 600,
    'StartPosX' : 500,
    'StartPosY' : 300,
    'StartTheta' : 0,
    'Resolution' : 5,
    'mapThresholdFree' : 126,
    'mapThresholdOcc' : 130, #169
    'EmptyValue' : 128,
    'mapMaxOcc' : 137,
    'mapMaxFree' : 119,
    'ServoPos' : 0,
    'CurrentPosX' : 0,
    'CurrentPosY' : 0,
    'CurrentTheta' : 0,
    'SafeZone' : 10
}

templateData["MapHeight"] = templateData["MapHeight"] / templateData["Resolution"]
templateData["MapWidth"] = templateData["MapWidth"] / templateData["Resolution"]
templateData["StartPosX"] = templateData["StartPosX"] / templateData["Resolution"]
templateData["StartPosY"] = templateData["StartPosY"] / templateData["Resolution"]


def updateSonarCalcMapVal(val):
    mapThresholdFree = templateData["mapThresholdFree"]
    mapThresholdOcc = templateData["mapThresholdOcc"]

    #oldval
    if  val[0] <= mapThresholdFree:
        oldval = 0
    elif mapThresholdFree < val[0] < mapThresholdOcc:
        oldval = 1
    elif  val[0] >= mapThresholdOcc:
        oldval = 2

    # newval
    if  val[1] <= mapThresholdFree:
        newval = 0
    elif mapThresholdFree < val[1] < mapThresholdOcc:
        newval = 1
    elif  val[1] >= mapThresholdOcc:
        newval = 2

    if oldval != newval:
        return newval
    else:
        return 'n'


def dur( op=None, clock=[time.time()] ):
    if op != None:
        duration = time.time() - clock[0]
        print '%s finished. Duration %.6f seconds.' % (op, duration)
    clock[0] = time.time()

def updateIRWrite(RobotPos, coord, updateval):
    XtoUpdate=RobotPos[0] + coord[0]
    YtoUpdate=RobotPos[1] - coord[1]
    val = map.updateMap(XtoUpdate, YtoUpdate , updateval)
    newval=updateSonarCalcMapVal(val)

########### main Script #############

map=NewRobotMap(templateData["MapWidth"],templateData["MapHeight"], templateData["Resolution"], templateData["StartPosX"],templateData["StartPosY"], templateData["StartTheta"], templateData["ServoPos"],templateData["mapMaxOcc"],templateData["mapMaxFree"],templateData["mapThresholdOcc"],templateData["EmptyValue"])
map.clear()

dur()
for x in xrange(0,10001*40):
    updateIRWrite((100,100), (10,10), 1)
dur("loops")

我在NewRobotMap类/对象中尝试了一个numpy数组作为self.RobotMap。但这要慢得多。

2 个答案:

答案 0 :(得分:0)

几点提示

最小化重定向过深

您的代码在这里:

def updateMap(self ,x ,y , Val):
        oldval = self.RobotMap[x][y]
        self.RobotMap[x][y]=self.RobotMap[x][y] + Val

        if self.RobotMap[x][y] > self.mapMaxOcc:
            self.RobotMap[x][y] = self.mapMaxOcc
        elif self.RobotMap[x][y] < self.mapMaxFree:
            self.RobotMap[x][y] = self.mapMaxFree
        return oldval, self.RobotMap[x][y]

一直在重复self.RobotMap[x][y]需要4个级别的跳跃来获取值(自我&gt; RobotMap - &gt; [x] - &gt; [y])

这可以优化:

到位更新

旧:

self.RobotMap[x][y]=self.RobotMap[x][y] + Val

new(第二次为现有值保存潜水)

self.RobotMap[x][y] += Val

使用局部变量而不是深层嵌套结构

def updateMap(self ,x ,y , Val):
        oldval = self.RobotMap[x][y]
        newval = oldval + Val

        if newval > self.mapMaxOcc:
            newval = self.mapMaxOcc
        elif newval < self.mapMaxFree:
            newval = self.mapMaxFree
        return oldval, newval

请注意,旧的返回oldval, self.RobotMap[x][y]不仅会返回一个值,而且您已经修改了self.RobotMap[x][y](因为它是可变的),所以如果你依赖它,你可能会吃惊。

使用全局变量代替tempateData字典

将字典更改为全局变量会加速运行,因为它删除了一个级别的ov间接。我知道,它看起来很讨厌,但这可能会在优化时发生。

跳过返回self.RobotMap[x][y]

如果没有必要,或者您已经更改了该值,请考虑保存返回self.RobotMap[x][y]

快速清除

更改原件:

def clear(self):
    self.RobotMap = [[self.EmptyValue for i in xrange(self.sizeY)] for j in xrange(self.sizeX)]

为:

def clear(self):
    self.RobotMap = self.sizeY * [self.sizeY * [self.EmptyValue]]

我的测试表明,对于x = 3,y = 5,执行速度提高了两倍,更大的尺寸可能会更好。

修改后的代码 - 从0.790581到0.479875秒

from math import degrees, radians, sin, cos, fabs
import time

templ_MapWidth = 800
templ_MapHeight = 600
templ_StartPosX = 500
templ_StartPosY = 300
templ_StartTheta = 0
templ_Resolution = 5
templ_mapThresholdFree = 126
templ_mapThresholdOcc = 130
templ_EmptyValue = 128
templ_mapMaxOcc = 137
templ_mapMaxFree = 119
templ_ServoPos = 0
templ_CurrentPosX = 0
templ_CurrentPosY = 0
templ_CurrentTheta = 0
templ_SafeZone = 10

templ_MapHeight  = templ_MapHeight / templ_Resolution
templ_MapWidth   = templ_MapWidth /  templ_Resolution
templ_StartPosX  = templ_StartPosX / templ_Resolution
templ_StartPosY  = templ_StartPosY / templ_Resolution

class NewRobotMap(object):
    def __init__(self, sizeX, sizeY, Resolution, RobotPosX, RobotPosY, RobotTheta, ServoPos, mapMaxOcc, mapMaxFree, OccValue, EmptyValue):
        self.sizeX = sizeX
        self.sizeY = sizeY
        self.RobotPosX = int(RobotPosX)
        self.RobotPosY = int(RobotPosY)
        self.mapResolution = int(Resolution)
        self.StartPosX = int(RobotPosX)
        self.StartPosY = int(RobotPosY)
        self.RobotTheta = float(RobotTheta)
        self.EmptyValue = EmptyValue
        self.ServoPos = ServoPos
        self.mapMaxOcc = mapMaxOcc
        self.mapMaxFree = mapMaxFree
        self.mapOccValue = OccValue
        self.RobotPosOldX = ""
        self.RobotPosOldY = ""

    def clear(self):
        self.RobotMap = self.sizeX * [self.sizeY * [self.EmptyValue]]

    def updateMap(self, x, y, Val):
        oldval = self.RobotMap[x][y]
        newval = oldval + Val

        if newval < self.mapMaxFree:
            return oldval, self.mapMaxFree
        if newval > self.mapMaxOcc:
            return oldval, self.mapMaxOcc
        return oldval, newval

    def setOcc(self, x, y):
            self.RobotMap[x][y] = self.mapMaxOcc

    def updateRobot(self, theta, x, y):
        robotThetaold = self.RobotTheta

        self.RobotTheta = float(theta)
        self.RobotPosX = int(round(self.StartPosX + float(int(x)/self.mapResolution), 0))
        self.RobotPosY = int(round(self.StartPosY - float(int(y)/self.mapResolution), 0))

        if x != self.RobotPosOldX or y != self.RobotPosOldX:
            self.RobotPosOldX = x
            self.RobotPosOldY = y
            return True
        else:
            self.RobotPosOldX = x
            self.RobotPosOldY = y
            return False

    def getRobotPos(self):
        return self.RobotPosX, self.RobotPosY

    def display(self):
        s = [[str(e) for e in row] for row in self.RobotMap]
        lens = [len(max(col, key=len)) for col in zip(*s)]
        fmt = '\t'.join('{{:{}}}'.format(x) for x in lens)
        table = [fmt.format(*row) for row in s]
        print '\n'.join(table)

    def updateServoPos(self, newServoPos):
        self.ServoPos = newServoPos

def updateSonarCalcMapVal(org, new):
    mapThresholdFree = templ_mapThresholdFree
    mapThresholdOcc = templ_mapThresholdOcc

    #oldval
    if  org <= mapThresholdFree:
        oldval = 0
    elif mapThresholdFree < org < mapThresholdOcc:
        oldval = 1
    elif  org >= mapThresholdOcc:
        oldval = 2

    # newval
    if  new <= mapThresholdFree:
        newval = 0
    elif mapThresholdFree < new < mapThresholdOcc:
        newval = 1
    elif  new >= mapThresholdOcc:
        newval = 2

    if oldval != newval:
        return newval
    else:
        return 'n'


def dur(op=None, clock=[time.time()]):
    if op != None:
        duration = time.time() - clock[0]
        print '%s finished. Duration %.6f seconds.' % (op, duration)
    clock[0] = time.time()

def updateIRWrite(RobotPos, coord, updateval):
    XtoUpdate = RobotPos[0] + coord[0]
    YtoUpdate = RobotPos[1] - coord[1]

    newval = updateSonarCalcMapVal(*mymap.updateMap(XtoUpdate, YtoUpdate, updateval))

########### main Script #############

mymap = NewRobotMap(templ_MapWidth, templ_MapHeight, templ_Resolution, templ_StartPosX, templ_StartPosY, templ_StartTheta, templ_ServoPos, templ_mapMaxOcc, templ_mapMaxFree, templ_mapThresholdOcc, templ_EmptyValue)
mymap.clear()

dur()

for x in xrange(0, 10001*40):
    updateIRWrite((100, 100), (10, 10), 1)

dur("loops")

结论

代码肯定需要审查才能正确完成工作。例如。有一些方法,它们根本没有使用,也没有其他的调用,它们从不使用返回值。

但可以显示一些优化。一般来说,以下是很好的:

  1. 首先正确运行代码
  2. 明确什么是可接受的速度,如果没有必要,不要优化
  3. 测量,个人资料
  4. 在最繁忙的循环中开始优化,最有可能加快速度。在它们中,每行代码都很重要。

答案 1 :(得分:0)

你能安装PyPy并用它来代替CPython(默认)运行你的脚本吗?它应该作为CPython的替代品。

它基于(跟踪?)JIT,因其高运行时性能而闻名。