【问题标题】:Performance issues in Python for small loops over simple array look-upsPython 中针对简单数组查找的小循环的性能问题
【发布时间】:2014-06-02 13:37:38
【问题描述】:

为什么简单的循环和/或简单的数组查找在 Python 中如此缓慢?

具体来说,在以下示例中,Python(使用 pypy)比 C++(使用 -O2)慢大约 9 倍。解释性能损失的技术原因是什么?是机器码中 Python 循环的实现吗?编译器使用的优化的差异?内存管理?还是别的什么?

Python 代码:

# File: timing.py
import sys
T = [ \
        [ 0, 6, 7, 7, 7, 8, 6, 7, 8,19,20, 7, 7, 7, 7, 7, 7,21,22,19,20,21,22,29, 7, 7, 7, 7, 7,29,35,36, 7, 7, 7,35,36, 7, 7,], \
        [ 9, 7,10, 7, 7, 7, 1, 7,23, 7, 7, 1,23, 7, 7, 7, 7, 7, 7, 9,10,30,31, 7, 9,10,30,31, 7,23, 7, 7,23, 7, 7,30,31,30,31,], \
        [ 7, 7, 2,11,12, 7, 7, 7, 7, 7, 7,11,12,24,25,26,27, 7, 7, 7, 7, 7, 7, 7,24,25,26,27,32, 7, 7, 7,32,37,38, 7, 7,37,38,], \
        [13, 7,14, 7, 7, 7, 3, 7,28, 7, 7, 3,28, 7, 7, 7, 7, 7, 7,13,14,33,34, 7,13,14,33,34, 7,28, 7, 7,28, 7, 7,33,34,33,34,], \
        [15, 7,16, 7, 7, 7, 7, 7, 4, 7, 7, 7, 4, 7, 7, 7, 7, 7, 7, 7, 7,15,16, 7, 7, 7,15,16, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,], \
        [17, 7,18, 7, 7, 7, 7, 7, 5, 7, 7, 7, 5, 7, 7, 7, 7, 7, 7, 7, 7,17,18, 7, 7, 7,17,18, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,], \
        [19, 7,20, 7, 7, 7, 6, 7,29, 7, 7, 6,29, 7, 7, 7, 7, 7, 7,19,20,35,36, 7,19,20,35,36, 7,29, 7, 7,29, 7, 7,35,36,35,36,], \
        [ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,], \
        [21, 7,22, 7, 7, 7, 7, 7, 8, 7, 7, 7, 8, 7, 7, 7, 7, 7, 7, 7, 7,21,22, 7, 7, 7,21,22, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,], \
        [ 9, 1, 7, 7, 7,23, 1, 7,23, 9,10, 7, 7, 7, 7, 7, 7,30,31, 9,10,30,31,23, 7, 7, 7, 7, 7,23,30,31, 7, 7, 7,30,31, 7, 7,], \
        [ 7, 7,10, 1,23, 7, 7, 7, 7, 7, 7, 1,23, 9,10,30,31, 7, 7, 7, 7, 7, 7, 7, 9,10,30,31,23, 7, 7, 7,23,30,31, 7, 7,30,31,], \
        [24, 7,25, 7, 7, 7,11, 7,32, 7, 7,11,32, 7, 7, 7, 7, 7, 7,24,25,37,38, 7,24,25,37,38, 7,32, 7, 7,32, 7, 7,37,38,37,38,], \
        [26, 7,27, 7, 7, 7, 7, 7,12, 7, 7, 7,12, 7, 7, 7, 7, 7, 7, 7, 7,26,27, 7, 7, 7,26,27, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,], \
        [13, 3, 7, 7, 7,28, 3, 7,28,13,14, 7, 7, 7, 7, 7, 7,33,34,13,14,33,34,28, 7, 7, 7, 7, 7,28,33,34, 7, 7, 7,33,34, 7, 7,], \
        [ 7, 7,14, 3,28, 7, 7, 7, 7, 7, 7, 3,28,13,14,33,34, 7, 7, 7, 7, 7, 7, 7,13,14,33,34,28, 7, 7, 7,28,33,34, 7, 7,33,34,], \
        [15, 7, 7, 7, 7, 4, 7, 7, 4, 7, 7, 7, 7, 7, 7, 7, 7,15,16, 7, 7,15,16, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,], \
        [ 7, 7,16, 7, 4, 7, 7, 7, 7, 7, 7, 7, 4, 7, 7,15,16, 7, 7, 7, 7, 7, 7, 7, 7, 7,15,16, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,], \
        [17, 7, 7, 7, 7, 5, 7, 7, 5, 7, 7, 7, 7, 7, 7, 7, 7,17,18, 7, 7,17,18, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,], \
        [ 7, 7,18, 7, 5, 7, 7, 7, 7, 7, 7, 7, 5, 7, 7,17,18, 7, 7, 7, 7, 7, 7, 7, 7, 7,17,18, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,], \
        [19, 6, 7, 7, 7,29, 6, 7,29,19,20, 7, 7, 7, 7, 7, 7,35,36,19,20,35,36,29, 7, 7, 7, 7, 7,29,35,36, 7, 7, 7,35,36, 7, 7,], \
        [ 7, 7,20, 6,29, 7, 7, 7, 7, 7, 7, 6,29,19,20,35,36, 7, 7, 7, 7, 7, 7, 7,19,20,35,36,29, 7, 7, 7,29,35,36, 7, 7,35,36,], \
        [21, 7, 7, 7, 7, 8, 7, 7, 8, 7, 7, 7, 7, 7, 7, 7, 7,21,22, 7, 7,21,22, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,], \
        [ 7, 7,22, 7, 8, 7, 7, 7, 7, 7, 7, 7, 8, 7, 7,21,22, 7, 7, 7, 7, 7, 7, 7, 7, 7,21,22, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,], \
        [30, 7,31, 7, 7, 7, 7, 7,23, 7, 7, 7,23, 7, 7, 7, 7, 7, 7, 7, 7,30,31, 7, 7, 7,30,31, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,], \
        [24,11, 7, 7, 7,32,11, 7,32,24,25, 7, 7, 7, 7, 7, 7,37,38,24,25,37,38,32, 7, 7, 7, 7, 7,32,37,38, 7, 7, 7,37,38, 7, 7,], \
        [ 7, 7,25,11,32, 7, 7, 7, 7, 7, 7,11,32,24,25,37,38, 7, 7, 7, 7, 7, 7, 7,24,25,37,38,32, 7, 7, 7,32,37,38, 7, 7,37,38,], \
        [26, 7, 7, 7, 7,12, 7, 7,12, 7, 7, 7, 7, 7, 7, 7, 7,26,27, 7, 7,26,27, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,], \
        [ 7, 7,27, 7,12, 7, 7, 7, 7, 7, 7, 7,12, 7, 7,26,27, 7, 7, 7, 7, 7, 7, 7, 7, 7,26,27, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,], \
        [33, 7,34, 7, 7, 7, 7, 7,28, 7, 7, 7,28, 7, 7, 7, 7, 7, 7, 7, 7,33,34, 7, 7, 7,33,34, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,], \
        [35, 7,36, 7, 7, 7, 7, 7,29, 7, 7, 7,29, 7, 7, 7, 7, 7, 7, 7, 7,35,36, 7, 7, 7,35,36, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,], \
        [30, 7, 7, 7, 7,23, 7, 7,23, 7, 7, 7, 7, 7, 7, 7, 7,30,31, 7, 7,30,31, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,], \
        [ 7, 7,31, 7,23, 7, 7, 7, 7, 7, 7, 7,23, 7, 7,30,31, 7, 7, 7, 7, 7, 7, 7, 7, 7,30,31, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,], \
        [37, 7,38, 7, 7, 7, 7, 7,32, 7, 7, 7,32, 7, 7, 7, 7, 7, 7, 7, 7,37,38, 7, 7, 7,37,38, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,], \
        [33, 7, 7, 7, 7,28, 7, 7,28, 7, 7, 7, 7, 7, 7, 7, 7,33,34, 7, 7,33,34, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,], \
        [ 7, 7,34, 7,28, 7, 7, 7, 7, 7, 7, 7,28, 7, 7,33,34, 7, 7, 7, 7, 7, 7, 7, 7, 7,33,34, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,], \
        [35, 7, 7, 7, 7,29, 7, 7,29, 7, 7, 7, 7, 7, 7, 7, 7,35,36, 7, 7,35,36, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,], \
        [ 7, 7,36, 7,29, 7, 7, 7, 7, 7, 7, 7,29, 7, 7,35,36, 7, 7, 7, 7, 7, 7, 7, 7, 7,35,36, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,], \
        [37, 7, 7, 7, 7,32, 7, 7,32, 7, 7, 7, 7, 7, 7, 7, 7,37,38, 7, 7,37,38, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,], \
        [ 7, 7,38, 7,32, 7, 7, 7, 7, 7, 7, 7,32, 7, 7,37,38, 7, 7, 7, 7, 7, 7, 7, 7, 7,37,38, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,], \
            ]

M = range(39)
idempotents = [0,2,6,7,8,9,11,12,14,16,17,19,21,25,27]
omega = [0,7,2,7,7,7,6,7,8,9,7,11,12,7,14,7,16,17,7,19,7,21,7,7,7,25,7,27,7,7,7,7,7,7,7,7,7,7,7]

def check():
    for e in idempotents:
        for x in M:
            ex = T[e][x]
            for s in M:
                es = T[e][s]
                for f in idempotents:
                    exf = T[ex][f]
                    esf = T[es][f]
                    for y in M:
                        exfy = omega[T[exf][y]]
                        for t in M:
                            tesf = omega[T[t][esf]]
                            if T[T[exfy][exf]][tesf] != T[T[exfy][esf]][tesf]:
                                return 0
    return 1
sys.exit(check())

C++ 代码(需要 C++11,因为新的数组初始化和迭代语法):

// File: timing.cc
// Compile via 'g++ -std=c++11 -O2 timing.cc'
// Run via 'time ./a.out'
#include <vector>
#include <cstddef>

int main(int, char **) {
  const size_t N = 39;
  typedef unsigned element_t;
  const std::vector<std::vector<element_t>> T{{
     {{ 0, 6, 7, 7, 7, 8, 6, 7, 8,19,20, 7, 7, 7, 7, 7, 7,21,22,19,20,21,22,29, 7, 7, 7, 7, 7,29,35,36, 7, 7, 7,35,36, 7, 7,}}, 
     {{ 9, 7,10, 7, 7, 7, 1, 7,23, 7, 7, 1,23, 7, 7, 7, 7, 7, 7, 9,10,30,31, 7, 9,10,30,31, 7,23, 7, 7,23, 7, 7,30,31,30,31,}}, 
     {{ 7, 7, 2,11,12, 7, 7, 7, 7, 7, 7,11,12,24,25,26,27, 7, 7, 7, 7, 7, 7, 7,24,25,26,27,32, 7, 7, 7,32,37,38, 7, 7,37,38,}}, 
     {{13, 7,14, 7, 7, 7, 3, 7,28, 7, 7, 3,28, 7, 7, 7, 7, 7, 7,13,14,33,34, 7,13,14,33,34, 7,28, 7, 7,28, 7, 7,33,34,33,34,}}, 
     {{15, 7,16, 7, 7, 7, 7, 7, 4, 7, 7, 7, 4, 7, 7, 7, 7, 7, 7, 7, 7,15,16, 7, 7, 7,15,16, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,}}, 
     {{17, 7,18, 7, 7, 7, 7, 7, 5, 7, 7, 7, 5, 7, 7, 7, 7, 7, 7, 7, 7,17,18, 7, 7, 7,17,18, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,}}, 
     {{19, 7,20, 7, 7, 7, 6, 7,29, 7, 7, 6,29, 7, 7, 7, 7, 7, 7,19,20,35,36, 7,19,20,35,36, 7,29, 7, 7,29, 7, 7,35,36,35,36,}}, 
     {{ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,}}, 
     {{21, 7,22, 7, 7, 7, 7, 7, 8, 7, 7, 7, 8, 7, 7, 7, 7, 7, 7, 7, 7,21,22, 7, 7, 7,21,22, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,}}, 
     {{ 9, 1, 7, 7, 7,23, 1, 7,23, 9,10, 7, 7, 7, 7, 7, 7,30,31, 9,10,30,31,23, 7, 7, 7, 7, 7,23,30,31, 7, 7, 7,30,31, 7, 7,}}, 
     {{ 7, 7,10, 1,23, 7, 7, 7, 7, 7, 7, 1,23, 9,10,30,31, 7, 7, 7, 7, 7, 7, 7, 9,10,30,31,23, 7, 7, 7,23,30,31, 7, 7,30,31,}}, 
     {{24, 7,25, 7, 7, 7,11, 7,32, 7, 7,11,32, 7, 7, 7, 7, 7, 7,24,25,37,38, 7,24,25,37,38, 7,32, 7, 7,32, 7, 7,37,38,37,38,}}, 
     {{26, 7,27, 7, 7, 7, 7, 7,12, 7, 7, 7,12, 7, 7, 7, 7, 7, 7, 7, 7,26,27, 7, 7, 7,26,27, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,}}, 
     {{13, 3, 7, 7, 7,28, 3, 7,28,13,14, 7, 7, 7, 7, 7, 7,33,34,13,14,33,34,28, 7, 7, 7, 7, 7,28,33,34, 7, 7, 7,33,34, 7, 7,}}, 
     {{ 7, 7,14, 3,28, 7, 7, 7, 7, 7, 7, 3,28,13,14,33,34, 7, 7, 7, 7, 7, 7, 7,13,14,33,34,28, 7, 7, 7,28,33,34, 7, 7,33,34,}}, 
     {{15, 7, 7, 7, 7, 4, 7, 7, 4, 7, 7, 7, 7, 7, 7, 7, 7,15,16, 7, 7,15,16, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,}}, 
     {{ 7, 7,16, 7, 4, 7, 7, 7, 7, 7, 7, 7, 4, 7, 7,15,16, 7, 7, 7, 7, 7, 7, 7, 7, 7,15,16, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,}}, 
     {{17, 7, 7, 7, 7, 5, 7, 7, 5, 7, 7, 7, 7, 7, 7, 7, 7,17,18, 7, 7,17,18, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,}}, 
     {{ 7, 7,18, 7, 5, 7, 7, 7, 7, 7, 7, 7, 5, 7, 7,17,18, 7, 7, 7, 7, 7, 7, 7, 7, 7,17,18, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,}}, 
     {{19, 6, 7, 7, 7,29, 6, 7,29,19,20, 7, 7, 7, 7, 7, 7,35,36,19,20,35,36,29, 7, 7, 7, 7, 7,29,35,36, 7, 7, 7,35,36, 7, 7,}}, 
     {{ 7, 7,20, 6,29, 7, 7, 7, 7, 7, 7, 6,29,19,20,35,36, 7, 7, 7, 7, 7, 7, 7,19,20,35,36,29, 7, 7, 7,29,35,36, 7, 7,35,36,}}, 
     {{21, 7, 7, 7, 7, 8, 7, 7, 8, 7, 7, 7, 7, 7, 7, 7, 7,21,22, 7, 7,21,22, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,}}, 
     {{ 7, 7,22, 7, 8, 7, 7, 7, 7, 7, 7, 7, 8, 7, 7,21,22, 7, 7, 7, 7, 7, 7, 7, 7, 7,21,22, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,}}, 
     {{30, 7,31, 7, 7, 7, 7, 7,23, 7, 7, 7,23, 7, 7, 7, 7, 7, 7, 7, 7,30,31, 7, 7, 7,30,31, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,}}, 
     {{24,11, 7, 7, 7,32,11, 7,32,24,25, 7, 7, 7, 7, 7, 7,37,38,24,25,37,38,32, 7, 7, 7, 7, 7,32,37,38, 7, 7, 7,37,38, 7, 7,}}, 
     {{ 7, 7,25,11,32, 7, 7, 7, 7, 7, 7,11,32,24,25,37,38, 7, 7, 7, 7, 7, 7, 7,24,25,37,38,32, 7, 7, 7,32,37,38, 7, 7,37,38,}}, 
     {{26, 7, 7, 7, 7,12, 7, 7,12, 7, 7, 7, 7, 7, 7, 7, 7,26,27, 7, 7,26,27, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,}}, 
     {{ 7, 7,27, 7,12, 7, 7, 7, 7, 7, 7, 7,12, 7, 7,26,27, 7, 7, 7, 7, 7, 7, 7, 7, 7,26,27, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,}}, 
     {{33, 7,34, 7, 7, 7, 7, 7,28, 7, 7, 7,28, 7, 7, 7, 7, 7, 7, 7, 7,33,34, 7, 7, 7,33,34, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,}}, 
     {{35, 7,36, 7, 7, 7, 7, 7,29, 7, 7, 7,29, 7, 7, 7, 7, 7, 7, 7, 7,35,36, 7, 7, 7,35,36, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,}}, 
     {{30, 7, 7, 7, 7,23, 7, 7,23, 7, 7, 7, 7, 7, 7, 7, 7,30,31, 7, 7,30,31, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,}}, 
     {{ 7, 7,31, 7,23, 7, 7, 7, 7, 7, 7, 7,23, 7, 7,30,31, 7, 7, 7, 7, 7, 7, 7, 7, 7,30,31, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,}}, 
     {{37, 7,38, 7, 7, 7, 7, 7,32, 7, 7, 7,32, 7, 7, 7, 7, 7, 7, 7, 7,37,38, 7, 7, 7,37,38, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,}}, 
     {{33, 7, 7, 7, 7,28, 7, 7,28, 7, 7, 7, 7, 7, 7, 7, 7,33,34, 7, 7,33,34, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,}}, 
     {{ 7, 7,34, 7,28, 7, 7, 7, 7, 7, 7, 7,28, 7, 7,33,34, 7, 7, 7, 7, 7, 7, 7, 7, 7,33,34, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,}}, 
     {{35, 7, 7, 7, 7,29, 7, 7,29, 7, 7, 7, 7, 7, 7, 7, 7,35,36, 7, 7,35,36, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,}}, 
     {{ 7, 7,36, 7,29, 7, 7, 7, 7, 7, 7, 7,29, 7, 7,35,36, 7, 7, 7, 7, 7, 7, 7, 7, 7,35,36, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,}}, 
     {{37, 7, 7, 7, 7,32, 7, 7,32, 7, 7, 7, 7, 7, 7, 7, 7,37,38, 7, 7,37,38, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,}}, 
     {{ 7, 7,38, 7,32, 7, 7, 7, 7, 7, 7, 7,32, 7, 7,37,38, 7, 7, 7, 7, 7, 7, 7, 7, 7,37,38, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,}}, 
  }};

  const std::vector<element_t> idempotents{{0,2,6,7,8,9,11,12,14,16,17,19,21,25,27}};
  const std::vector<element_t> omega{{0,7,2,7,7,7,6,7,8,9,7,11,12,7,14,7,16,17,7,19,7,21,7,7,7,25,7,27,7,7,7,7,7,7,7,7,7,7,7}};

  element_t ex, es, exf, esf, exfy, tesf;
  for(auto e: idempotents) {
    for(size_t x = 0; x < N; ++x) {
      ex = T[e][x];
      for(size_t s = 0; s < N; ++s) {
        es = T[e][s];
        for(auto f: idempotents) {
          exf = T[ex][f];
          esf = T[es][f];
          for(size_t y = 0; y < N; ++y) {
            exfy = omega[T[exf][y]];
            for(size_t t = 0; t < N; ++t) {
              tesf = omega[T[t][esf]];
              if(T[T[exfy][exf]][tesf] != T[T[exfy][esf]][tesf])
                return 0;
            }}}}}}
  return 1;
}

(不要问代码做什么的细节。粗略地说,实现了代数形式语言理论上下文中的决策过程;代码验证乘法表给出的幺半群上的身份@ 987654326@。特别是,代码不是一个人为的例子,而是一个现实世界的应用程序。当然,人们可以在形式语言理论的上下文中争论“应用程序”。)

计时结果

使用上面的代码,我机器上的用户 CPU 运行时间如下:

  • time pypy timing.py 输出 0m9.329s
  • time python timing.py 输出 2m18.389s
  • g++ -std=c++11 -O2 timing.cc &amp;&amp; time ./a.out 输出 0m1.064s

编辑

  • 为了更公平的比较,我做了一些优化,g++ 似乎会自动合并。我重新排序了循环并将变量分配尽可能向外移动(如 cmets 所建议的那样)。这为 pypypython 带来了大约 2.5 的加速因子。
  • 为了公平起见,我使用了动态大小的std::vector,而不是固定大小的std::array
  • 我删除了关于为什么 numpy 更慢(cmets 表明我没有正确使用它)以及为什么在脚本的主要部分执行循环更慢(众所周知 Python 使用局部变量更快)的旁白与全局变量相比;cmets 对此提供了参考)。
  • 我知道 C++ 和 Python 具有不同的作用域这一事实。我也知道 C++ 是编译的,而 Python 通常是解释的(这就是我使用 pypy 的原因)。我想知道为什么pypy 在这段特定代码上的速度要慢得多的最终技术原因。 (在这里使用 numpy 和 numba one 可能能够获得接近本机的性能,但这不符合我的问题的精神,因为它实际上将所有计算转移回 C 代码。)我相应地澄清了我的问题。

【问题讨论】:

  • 你的 numpy 实现是什么?在 numpy 数组上执行普通的 python for 循环会很慢。但是使用 numpy,您可以避免编写 python for 循环,这将大大加快您的代码速度。
  • 额外的 7 秒几乎可以肯定是因为 python 访问局部变量比访问全局变量更快。见this
  • 一个古老的技巧是将全局变量缓存在局部变量中以加快查找速度。您还在最内层循环中进行了大量计算,这些计算可以向外提升(exfesfexfy),并且您的 C++ 编译器很有可能为您完成了这些计算。当然,无论如何你都不会接近 C++ 的性能,但它应该会产生显着的差异。
  • 我使用 numba autojit 将 4 分 53 秒缩短到 12 秒。
  • 按照@molbdnilo 的建议,将一些变量移到外部循环中,将其进一步减少到 7 秒。

标签: python c++ arrays performance


【解决方案1】:

numpy 和 python 循环的简短回答

如果您正确使用 numpy,您将再次将所有内容推回 C 级。

长答案

如何做到这一点?

我将在这里稍微说明一下我的评论的意思,以及如何避免使用 numpy.假设您已使用 np.asarray(list) 将所有列表放入 np.arrays 中,让我们看一下原始代码。

for e in idempotents:
    for x in M:
        ex = T[e][x]

这直接转化为:

T[idempotents]

这是为什么呢?

Numpy 数组可以使用索引数组进行索引。例如。 T[0] 返回矩阵 T 的所有列(实际上是所有后续维度)。所以T[0]==T[0,:] 用于二维数组。由于您将所有幂等作为索引循环,而不是循环T[e][x] 列中的所有元素,T[idempotents] 与这两个循环相同。

有关它的详细信息,请参阅here

前、后

接下来是

for e in idempotents:
    for x in M:
        ex = T[e][x]
        for s in M:
            es = T[e][s]

由于再次重做整个循环没有意义,这转化为

es=ex

因为我们使用的是python,所以甚至没有复制矩阵es,只是引用了。

exf, esf

我现在跳过了 sn-ps 中的一些 for 循环。

for f in idempotents:
    exf = T[ex][f]
    esf = T[es][f]

现在您正在使用幂等向量再次访问最外层的索引。所以我们可以用同样的方式用 numpy 做到这一点:

T[T[idempotents]]
print T[T[idempotents]].shape
>> (15, 39, 39)

现在,我们有一个维度为 (15, 39, 39) 的数组,因为对于 2d 数组 T[idempotents] 的每个元素,您都返回 T 的元素。这基本上是第三个循环。

exf = T[T[idempotents]]
esf = exf

从这里开始变得更加复杂,我将跳过其余部分。它将遵循以下几行:

Ti = T[idempotents]  # T[e][x] == T[e][s] by loop definition
TTi = T[Ti]  # T[T[e][x]]
TTi.shape = -1 , 39  # bring first index back into shape
exf = TTi[:, idempotents]  # T[T[e][x]][f]
esf = exf  # T[T[e][s]][f] == T[T[e][x]][f] by loop definition
Texf = T[exf].ravel()
exfy = omega[Texf]
TTexf = T.T[exf].ravel()  # tesf = omega[T[t][esf]] # since I cannot index fast along t I use the transpose of T
tesf = omega[TTexf]

等等……

【讨论】:

  • 感谢您的详细解答。看到 numpy 应用很有趣,为此投票。但正如我在编辑中指出的那样,这不是我的问题的精神。此外,我不同意通过设置ex = es 可以删除s 上的循环。这会改变代码的语义(事实上,无论T 是什么return 1 都是微不足道的)。
  • 无论何时执行循环,向量 es 或 ex 的分量都是相同的。语义被应用程序的顺序保留为嵌套索引。C++ 编译器可能也会做类似的事情,甚至更多。不过这离题了。
  • 明白了,你的意思不是在我的原始代码中,那么?!我可能很困惑,因为您没有在代码中定义 ex。但现在我很好奇。你说T[T[idempotents]] 是三维的,所以大小约为n 的立方,其中n 是M 的大小。这是如何实施的?如果这个数组实际上是在内存中扩展的,这在我的情况下是不可接受的(记住,这只是一个玩具示例)。将值分配给矩阵中的单元格时会发生什么?
  • 在内存中扩展。尽管可以使用唯一元素和索引矩阵来减少元素数量。从长远来看,您最好编写一个自动生成 c++ 实现并让编译器优化处理循环的程序。
【解决方案2】:

因为 Python 在运行时解释其输入,而 C++ 编译器能够执行主要优化,尤其是在具有固定大小数组的 for 循环上。

一些编译器甚至可能计算嵌套 for 循环的整个输出。

【讨论】:

  • 感谢您的回答。我知道 Python 通常会被解释,这就是为什么我在 pypy 中包含时间。我只是想知道为什么这种简单循环的优化对于 C++ 来说要好得多。顺便说一句,我编辑了我的问题以包括动态 std::vector 数组类型的时间。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-05-08
  • 1970-01-01
  • 2018-12-12
  • 2014-10-18
  • 1970-01-01
  • 1970-01-01
  • 2020-01-31
相关资源
最近更新 更多