Öğelerin örneklenmesi ve alınması söz konusu olduğunda tuples ve listeler arasında herhangi bir performans farkı var mı?
Öğelerin örneklenmesi ve alınması söz konusu olduğunda tuples ve listeler arasında herhangi bir performans farkı var mı?
Yanıtlar:
dis
Modül, bir işlev için bayt kodu demonte ve küpe ve listeler arasındaki farkı görmek için yararlıdır.
Bu durumda, bir öğeye erişmenin aynı kodu oluşturduğunu ancak bir demet atamanın bir liste atamaktan çok daha hızlı olduğunu görebilirsiniz.
>>> 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
ListLike
bir ile __getitem__
korkunç yavaş, sonra sökmeye şey yapar x = ListLike((1, 2, 3, 4, 5)); y = x[2]
. Bayt kodu, liste örneğinden çok yukarıdaki grup örneğine benzer, ancak performansın benzer olacağına gerçekten inanıyor musunuz?
Genel olarak, tuple'lerin biraz daha hızlı olmasını bekleyebilirsiniz. Bununla birlikte, özel durumunuzu kesinlikle test etmelisiniz (eğer fark programınızın performansını etkileyebilirse - "erken optimizasyon tüm kötülüklerin köküdür").
Python bunu çok kolaylaştırıyor: timeit senin arkadaşın.
$ 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
ve...
$ 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
Bu durumda, örnekleme neredeyse demet için daha hızlı bir büyüklük sırasıdır, ancak öğe erişimi aslında liste için biraz daha hızlıdır! Bu yüzden birkaç başlık oluşturuyorsanız ve bunlara birçok kez erişiyorsanız, bunun yerine listeleri kullanmak daha hızlı olabilir.
Tabii ki bir öğeyi değiştirmek istiyorsanız, bir öğeyi değiştirmek için tamamen yeni bir grup oluşturmanız gerektiğinden liste kesinlikle daha hızlı olacaktır (tupler değişmez olduğu için).
python -m timeit "x=tuple(xrange(999999))"
vs python -m timeit "x=list(xrange(999999))"
. Tahmin edilebileceği gibi, bir listeyi gerçekleştirmek bir listeden biraz daha uzun sürer.
-s "SETUP_CODE"
Gerçek Zamanlı kodun önce çalıştırılır.
Tuples neredeyse her kategorideki listelerden daha iyi performans gösterir :
1) Tuples sabit katlanabilir .
2) Tuples kopyalanmak yerine yeniden kullanılabilir.
3) Tuples kompakt ve aşırı tahsis etmeyin.
4) Tuples doğrudan elemanlarına referans verir.
Sabitler, Python'un gözetleme deliği optimize edici veya AST-optimize edici ile önceden hesaplanabilir. Öte yandan listeler sıfırdan oluşturuluyor:
>>> 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
Koşmak tuple(some_tuple)
hemen geri döner. Tupller değişmez olduğu için kopyalanmaları gerekmez:
>>> a = (10, 20, 30)
>>> b = tuple(a)
>>> a is b
True
Buna karşılık, list(some_list)
tüm verilerin yeni bir listeye kopyalanmasını gerektirir:
>>> a = [10, 20, 30]
>>> b = list(a)
>>> a is b
False
Bir demetin boyutu sabit olduğundan, append () işlemlerini verimli hale getirmek için aşırı tahsis edilmesi gereken listelerden daha kompakt olarak saklanabilir .
Bu, tuples'e güzel bir alan avantajı sağlar:
>>> import sys
>>> sys.getsizeof(tuple(iter(range(10))))
128
>>> sys.getsizeof(list(iter(range(10))))
200
İşte Objects / listobject.c'nin listelerin ne yaptığını açıklayan yorumu :
/* 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.
*/
Nesnelere yapılan başvurular doğrudan bir tuple nesnesine dahil edilir. Bunun aksine, listeler harici bir işaretçi dizisine fazladan bir dolaylama katmanına sahiptir.
Bu, tuples'e endeksli aramalar ve paketten çıkarma için küçük bir hız avantajı sağlar:
$ 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
İşte tanımlama grubu nasıl (10, 20)
saklanır:
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;
İşte liste nasıl [10, 20]
saklanır:
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;
Liste nesnesinin, iki veri işaretçisini tutan harici bir diziye ek bir dolaylı katmanı varken, tuple nesnesinin iki veri işaretçisini doğrudan içerdiğini unutmayın.
Internally, tuples are stored a little more efficiently than lists, and also tuples can be accessed slightly faster.
O halde dF.'nin cevabından elde edilen sonuçları nasıl açıklayabilirsiniz?
tuple(some_tuple)
Sadece döner some_tuple
eğer kendini some_tuple
hashable-zaman içeriği verilmiştir yinelemeli değişmez ve hashable olduğunu. Aksi takdirde, tuple(some_tuple)
yeni bir demet döndürür. Örneğin, some_tuple
değiştirilebilir öğeler içerdiğinde.
Değişmez olan tüller daha fazla bellek verimlidir; Verimlilik için, sabit realloc
s olmadan ekleme yapılmasına izin vermek üzere belleği fazladan konumlandırır . Bu nedenle, kodunuzdaki sabit bir değer dizisini tekrarlamak istiyorsanız (örn. for direction in 'up', 'right', 'down', 'left':
), Tuples tercih edilir, çünkü bu tür tuples derleme zamanında önceden hesaplanır.
Erişim hızları aynı olmalıdır (her ikisi de bellekte bitişik diziler olarak saklanır).
Ancak, değiştirilebilir verilerle uğraşırken alist.append(item)
çok tercih edilir atuple+= (item,)
. Unutmayın, tuples alan adları olmayan kayıtlar olarak düşünülmelidir.
array
Listenizdeki veya gruptaki tüm öğeler aynı C türündeyse, standart kütüphanedeki modülü de göz önünde bulundurmalısınız . Daha az bellek alır ve daha hızlı olabilir.
İşte yeni bir kıyaslama daha, sadece uğruna ..
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)
Bunları ortalayalım:
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
Neredeyse sonuçsuz diyebilirsiniz.
Ama tabii, küpe aldı 101.239%
saat veya 1.239%
listelere göre işi yapmak için ekstra zaman.
Tuples, değişmez oldukları için listelerden biraz daha verimli ve bu nedenle listelerden daha hızlı olmalıdır.
Tuple'in okumada çok verimli olmasının temel nedeni değişmez olmasıdır.
Nedeni tuples, listelerin aksine bellek önbelleğinde saklanabilir. Program her zaman değiştirilebilir olduğu için listelerin bellek konumundan okur (herhangi bir zamanda değişebilir).