it-swarm-ko.tech

튜플은 파이썬의 목록보다 더 효율적입니까?

요소의 인스턴스화 및 검색과 관련하여 튜플과 목록 사이에 성능 차이가 있습니까?

191
Readonly

dis 모듈은 함수의 바이트 코드를 디스 어셈블하며 튜플과 목록의 차이를 보는 데 유용합니다.

이 경우 요소에 액세스하면 동일한 코드가 생성되지만 Tuple을 할당하는 것이 목록을 할당하는 것보다 훨씬 빠르다는 것을 알 수 있습니다.

>>> def a():
...     x=[1,2,3,4,5]
...     y=x[2]
...
>>> def b():
...     x=(1,2,3,4,5)
...     y=x[2]
...
>>> import dis
>>> dis.dis(a)
  2           0 LOAD_CONST               1 (1)
              3 LOAD_CONST               2 (2)
              6 LOAD_CONST               3 (3)
              9 LOAD_CONST               4 (4)
             12 LOAD_CONST               5 (5)
             15 BUILD_LIST               5
             18 STORE_FAST               0 (x)

  3          21 LOAD_FAST                0 (x)
             24 LOAD_CONST               2 (2)
             27 BINARY_SUBSCR
             28 STORE_FAST               1 (y)
             31 LOAD_CONST               0 (None)
             34 RETURN_VALUE
>>> dis.dis(b)
  2           0 LOAD_CONST               6 ((1, 2, 3, 4, 5))
              3 STORE_FAST               0 (x)

  3           6 LOAD_FAST                0 (x)
              9 LOAD_CONST               2 (2)
             12 BINARY_SUBSCR
             13 STORE_FAST               1 (y)
             16 LOAD_CONST               0 (None)
             19 RETURN_VALUE
152
Mark Harrison

일반적으로 튜플이 약간 더 빠를 것으로 예상 할 수 있습니다. 그러나 특정 사례를 확실히 테스트해야합니다 (차이가 프로그램의 성능에 영향을 줄 수있는 경우 "미리 최적화는 모든 악의 근원"임을 명심하십시오).

파이썬은 이것을 아주 쉽게 만듭니다 : timeit 는 당신의 친구입니다.

$ python -m timeit "x=(1,2,3,4,5,6,7,8)"
10000000 loops, best of 3: 0.0388 usec per loop

$ python -m timeit "x=[1,2,3,4,5,6,7,8]"
1000000 loops, best of 3: 0.363 usec per loop

과...

$ python -m timeit -s "x=(1,2,3,4,5,6,7,8)" "y=x[3]"
10000000 loops, best of 3: 0.0938 usec per loop

$ python -m timeit -s "x=[1,2,3,4,5,6,7,8]" "y=x[3]"
10000000 loops, best of 3: 0.0649 usec per loop

따라서이 경우 인스턴스화는 거의 Tuple에 비해 훨씬 빠르지 만 실제로는 항목에 대한 액세스가 목록에서 다소 빠릅니다. 따라서 몇 개의 튜플을 만들고 여러 번 액세스하는 경우 실제로 목록을 사용하는 것이 더 빠를 수 있습니다.

물론 항목을 change 하려는 경우 하나의 항목을 변경하려면 완전히 새로운 Tuple을 만들어야하기 때문에 목록이 훨씬 빠릅니다. (튜플은 불변이므로).

194
dF.

개요

Tuples는 거의 모든 범주에서 list 보다 성능이 좋은 경향이 있습니다.

1) 튜플은 일정한 접힘 일 수 있습니다.

2) 튜플은 복사하는 대신 재사용 할 수 있습니다.

3) 튜플은 콤팩트하며 과도하게 할당되지 않습니다.

4) 튜플은 해당 요소를 직접 참조합니다.

튜플은 일정하게 접을 수 있습니다

튜플 상수는 Python의 틈새 최적화 장치 또는 AST- 최적화 장치로 사전 계산할 수 있습니다. 반면에 목록은 처음부터 작성됩니다.

    >>> from dis import dis

    >>> dis(compile("(10, 'abc')", '', 'eval'))
      1           0 LOAD_CONST               2 ((10, 'abc'))
                  3 RETURN_VALUE   

    >>> dis(compile("[10, 'abc']", '', 'eval'))
      1           0 LOAD_CONST               0 (10)
                  3 LOAD_CONST               1 ('abc')
                  6 BUILD_LIST               2
                  9 RETURN_VALUE 

튜플은 복사 할 필요가 없습니다

Tuple(some_Tuple)을 실행하면 즉시 자체가 반환됩니다. 튜플은 변경할 수 없으므로 복사 할 필요가 없습니다.

>>> a = (10, 20, 30)
>>> b = Tuple(a)
>>> a is b
True

반대로 list(some_list)은 모든 데이터를 새 목록으로 복사해야합니다.

>>> a = [10, 20, 30]
>>> b = list(a)
>>> a is b
False

튜플은 과도하게 할당되지 않습니다

Tuple의 크기는 고정되어 있기 때문에 append () 연산을 효율적으로 수행하기 위해 초과 할당해야하는 목록보다 더 컴팩트하게 저장할 수 있습니다.

이것은 튜플에게 멋진 공간 이점을 제공합니다.

>>> import sys
>>> sys.getsizeof(Tuple(iter(range(10))))
128
>>> sys.getsizeof(list(iter(range(10))))
200

Objects/listobject.c 의 주석은 다음과 같습니다.

/* This over-allocates proportional to the list size, making room
 * for additional growth.  The over-allocation is mild, but is
 * enough to give linear-time amortized behavior over a long
 * sequence of appends() in the presence of a poorly-performing
 * system realloc().
 * The growth pattern is:  0, 4, 8, 16, 25, 35, 46, 58, 72, 88, ...
 * Note: new_allocated won't overflow because the largest possible value
 *       is PY_SSIZE_T_MAX * (9 / 8) + 6 which always fits in a size_t.
 */

튜플은 요소를 직접 참조합니다

객체에 대한 참조는 Tuple 객체에 직접 통합됩니다. 대조적으로, 목록에는 외부 포인터 배열에 대한 추가 간접 계층이 있습니다.

이렇게하면 튜플에 색인 조회 및 포장 풀기에 작은 속도 이점이 있습니다.

$ python3.6 -m timeit -s 'a = (10, 20, 30)' 'a[1]'
10000000 loops, best of 3: 0.0304 usec per loop
$ python3.6 -m timeit -s 'a = [10, 20, 30]' 'a[1]'
10000000 loops, best of 3: 0.0309 usec per loop

$ python3.6 -m timeit -s 'a = (10, 20, 30)' 'x, y, z = a'
10000000 loops, best of 3: 0.0249 usec per loop
$ python3.6 -m timeit -s 'a = [10, 20, 30]' 'x, y, z = a'
10000000 loops, best of 3: 0.0251 usec per loop

Here Tuple (10, 20)가 저장되는 방식입니다 :

    typedef struct {
        Py_ssize_t ob_refcnt;
        struct _typeobject *ob_type;
        Py_ssize_t ob_size;
        PyObject *ob_item[2];     /* store a pointer to 10 and a pointer to 20 */
    } PyTupleObject;

Here 목록 [10, 20]가 저장되는 방식입니다 :

    PyObject arr[2];              /* store a pointer to 10 and a pointer to 20 */

    typedef struct {
        Py_ssize_t ob_refcnt;
        struct _typeobject *ob_type;
        Py_ssize_t ob_size;
        PyObject **ob_item = arr; /* store a pointer to the two-pointer array */
        Py_ssize_t allocated;
    } PyListObject;

Tuple 객체는 두 개의 데이터 포인터를 직접 통합하는 반면 목록 객체에는 두 개의 데이터 포인터를 보유하는 외부 배열에 대한 추가 간접 계층이 있습니다.

166

변경 불가능한 튜플은 메모리 효율성이 더 높습니다. 효율성을 위해 상수 reallocs없이 추가를 허용하기 위해 메모리를 전체적으로 나열합니다. 따라서 코드에서 일정한 값 시퀀스 (예 : for direction in 'up', 'right', 'down', 'left':)를 반복하려면 튜플이 선호됩니다. 튜플은 컴파일 시간에 미리 계산되기 때문입니다.

액세스 속도는 같아야합니다 (모두 메모리에 연속 배열로 저장 됨).

그러나 가변 데이터를 처리 할 때는 alist.append(item)atuple+= (item,)보다 훨씬 선호됩니다. 튜플은 필드 이름이없는 레코드로 취급되어야합니다.

31
tzot

목록 또는 Tuple의 모든 항목이 동일한 C 유형 인 경우 표준 라이브러리의 array 모듈도 고려해야합니다. 메모리가 적게 들고 더 빠를 수 있습니다.

9
Ralph Corderoy

튜플은 불변이기 때문에 목록보다 약간 더 효율적이며 그 때문에 빠릅니다.

4
ctcherry

여기에 다른 작은 벤치 마크가 있습니다.

In [11]: %timeit list(range(100))
749 ns ± 2.41 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

In [12]: %timeit Tuple(range(100))
781 ns ± 3.34 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

In [1]: %timeit list(range(1_000))
13.5 µs ± 466 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

In [2]: %timeit Tuple(range(1_000))
12.4 µs ± 182 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

In [7]: %timeit list(range(10_000))
182 µs ± 810 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

In [8]: %timeit Tuple(range(10_000))
188 µs ± 2.38 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

In [3]: %timeit list(range(1_00_000))
2.76 ms ± 30.5 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In [4]: %timeit Tuple(range(1_00_000))
2.74 ms ± 31.8 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In [10]: %timeit list(range(10_00_000))
28.1 ms ± 266 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

In [9]: %timeit Tuple(range(10_00_000))
28.5 ms ± 447 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

이것을 평균화하자.

In [3]: l = np.array([749 * 10 ** -9, 13.5 * 10 ** -6, 182 * 10 ** -6, 2.76 * 10 ** -3, 28.1 * 10 ** -3])

In [2]: t = np.array([781 * 10 ** -9, 12.4 * 10 ** -6, 188 * 10 ** -6, 2.74 * 10 ** -3, 28.5 * 10 ** -3])

In [11]: np.average(l)
Out[11]: 0.0062112498000000006

In [12]: np.average(t)
Out[12]: 0.0062882362

In [17]: np.average(t) / np.average(l)  * 100
Out[17]: 101.23946713590554

거의 결정적이지 않다고 부를 수 있습니다.

그러나 튜플은 101.239% 시간 또는 1.239% 목록에 비해 추가 작업 시간이 있습니다.

3
Dev Aggarwal