3

最近,我在线程中的 Jon Clements 的帮助下发现以下代码的执行时间非常不同。

你知道为什么会这样吗?

评论: self.stream_data 是一个向量元组,有许多零和 int16 值,create_ZS_data 方法正在执行所谓的 ZeroSuppression。

环境
输入: 许多(3.5k)小文件(每个~120kb)
操作系统: Linux64
Python ver 2.6.8

基于生成器的解决方案:

def create_ZS_data(self):
    self.ZS_data = ( [column, row, self.stream_data[column + row * self.rows ]]
                     for row, column in itertools.product(xrange(self.rows), xrange(self.columns))
                     if self.stream_data[column + row * self.rows ] )

探查器信息:

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
     3257    1.117    0.000   71.598    0.022 decode_from_merlin.py:302(create_ZS_file)
   463419   67.705    0.000   67.705    0.000 decode_from_merlin.py:86(<genexpr>)

乔恩的解决方案:

create_ZS_data(self):
    self.ZS_data = list()
    for rowno, cols in enumerate(self.stream_data[i:i+self.columns] for i in xrange(0, len(self.stream_data), self.columns)):
        for colno, col in enumerate(cols):
            # col == value, (rowno, colno) = index
            if col:
                self.ZS_data.append([colno, rowno, col])


探查器信息:

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
     3257   18.616    0.006   19.919    0.006 decode_from_merlin.py:83(create_ZS_data)
4

2 回答 2

4

我看了之前的讨论;您似乎很困扰,因为您的聪明理解在循环中不如在源代码字符中有效。我当时没有指出的是,这将是我阅读的首选实现:

def sparse_table_elements(cells, columns, rows):
    ncells = len(cells)
    non_zeros = list()
    for nrow in range(0, ncells, columns):
         row = cells[nrow:nrow+columns]
         for ncol, cell in enumerate(row):
             if cell:
                 non_zeros.append([ncol, nrow, cell])
    return non_zeros

我没有测试过它,但我可以理解它。有几件事让我觉得潜在的效率低下。重新计算两个恒定单调“无聊”指数的笛卡尔积必须很昂贵:

itertools.product(xrange(self.rows), xrange(self.columns))

然后,您可以使用结果[(0, 0), (0, 1), ...]从源中进行单元素索引:

stream_data[column + row * self.rows]

这也比像“Jon's”实现那样处理更大的切片更昂贵。

生成器并不是保证效率的秘诀。在这种特殊情况下,135kb 的数据已经读入核心,构建不良的生成器似乎确实会让您付出代价。如果您想要简洁的矩阵运算,请使用APL;如果您想要可读的代码,请不要在 Python 中追求最小化。

于 2012-07-23T13:15:59.283 回答
2

您可以将 Jon 的解决方案简单地重写为生成器:

def create_ZS_data(self):
    self.ZS_data = ([colno, rowno, col]
                    for rowno, cols in enumerate(self.stream_data[i:i+self.columns]
                                                 for i in xrange(0, len(self.stream_data), self.columns))
                    for colno, col in enumerate(cols)
                    if col)

我强烈希望这与 Jon 的基于循环的解决方案具有相同的行为,证明性能差异可以归结为算法实现的中等规模细节。

于 2012-07-23T13:37:36.787 回答