문제

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

도움이 되었습니까?

해결책

그만큼 dis 모듈은 함수의 바이트 코드를 분해하며 튜플과 목록의 차이점을 확인하는 데 유용합니다.

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

>>> 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

다른 팁

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

Python에서는 이를 매우 쉽게 수행할 수 있습니다. 타임잇 당신의 친구입니다.

$ 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

따라서 이 경우 인스턴스화는 튜플의 경우 거의 10배 더 빠르지만 항목 액세스는 실제로 목록의 경우 다소 더 빠릅니다.따라서 몇 개의 튜플을 생성하고 여러 번 액세스하는 경우 대신 목록을 사용하는 것이 실제로 더 빠를 수 있습니다.

물론 원한다면 변화 항목 중 하나를 변경하려면 전체 새 튜플을 만들어야 하므로 목록이 확실히 더 빨라집니다(튜플은 변경할 수 없기 때문입니다).

요약

튜플은 리스트보다 성능이 더 좋은 경향이 있습니다. 거의 모든 카테고리에서:

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

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

튜플의 크기는 고정되어 있으므로 초과 할당이 필요한 목록보다 더 컴팩트하게 저장할 수 있습니다. 추가() 효율적인 운영.

이는 튜플에 좋은 공간 이점을 제공합니다.

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

다음은 님의 댓글입니다. 객체/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.
 */

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

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

이는 튜플에 인덱스 조회 및 압축 풀기에 대한 약간의 속도 이점을 제공합니다.

$ 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

여기 튜플은 어떤가요? (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;

여기 목록이 어떻게 되나요? [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;

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

불변인 튜플은 메모리 효율성이 더 높습니다.목록은 효율성을 위해 상수 없이 추가할 수 있도록 메모리를 초과 할당합니다. realloc에스.따라서 코드에서 일정한 값 시퀀스를 반복하려는 경우(예: for direction in 'up', 'right', 'down', 'left':), 튜플은 컴파일 타임에 미리 계산되므로 튜플이 선호됩니다.

액세스 속도는 동일해야 합니다(둘 다 메모리에 연속 배열로 저장됨).

하지만, alist.append(item) 훨씬 선호된다 atuple+= (item,) 변경 가능한 데이터를 다룰 때.튜플은 필드 이름이 없는 레코드로 처리된다는 점을 기억하세요.

당신은 또한 고려해야합니다 array 목록이나 튜플의 모든 항목이 동일한 C 유형인 경우 표준 라이브러리의 모듈입니다.메모리를 적게 사용하고 속도도 더 빨라질 수 있습니다.

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

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

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% 목록에 비해 작업을 수행하는 데 추가 시간이 걸립니다.

Tuple이 읽기에 매우 효율적인 주된 이유는 그것이 불변이기 때문입니다.

불변 객체가 읽기 쉬운 이유는 무엇입니까?

그 이유는 목록과 달리 튜플은 메모리 캐시에 저장될 수 있기 때문입니다.프로그램은 변경 가능하므로 항상 목록 메모리 위치에서 읽습니다(언제든지 변경 가능).

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top