มีความแตกต่างของประสิทธิภาพระหว่างสิ่งอันดับและรายการเมื่อพูดถึงการสร้างอินสแตนซ์และการดึงองค์ประกอบ?
มีความแตกต่างของประสิทธิภาพระหว่างสิ่งอันดับและรายการเมื่อพูดถึงการสร้างอินสแตนซ์และการดึงองค์ประกอบ?
คำตอบ:
dis
โมดูล disassembles รหัสไบต์สำหรับฟังก์ชั่นและเป็นประโยชน์ที่จะเห็นความแตกต่างระหว่างสิ่งอันดับและรายการ
ในกรณีนี้คุณจะเห็นว่าการเข้าถึงองค์ประกอบสร้างรหัสที่เหมือนกัน แต่การกำหนดสิ่งอันดับจะเร็วกว่าการกำหนดรายการ
>>> 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
กับที่ทำบางสิ่งที่น่ากลัวช้าถอดแยกชิ้นส่วนแล้ว__getitem__
x = ListLike((1, 2, 3, 4, 5)); y = x[2]
bytecode จะเป็นเหมือนตัวอย่างของ tuple ด้านบนมากกว่าตัวอย่างของรายการ แต่คุณเชื่อหรือไม่ว่านั่นหมายความว่าประสิทธิภาพจะคล้ายกัน
โดยทั่วไปคุณอาจคาดหวังว่าสิ่งอันดับจะเร็วขึ้นเล็กน้อย อย่างไรก็ตามคุณควรทดสอบกรณีเฉพาะของคุณอย่างแน่นอน (หากความแตกต่างอาจส่งผลกระทบต่อประสิทธิภาพการทำงานของโปรแกรมของคุณ - โปรดจำไว้ว่า "การเพิ่มประสิทธิภาพก่อนวัยอันควรเป็นรากฐานของความชั่วร้ายทั้งหมด")
Python ทำให้ง่ายมาก: 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 แต่การเข้าถึงไอเท็มนั้นค่อนข้างเร็วกว่าสำหรับรายการ! ดังนั้นหากคุณกำลังสร้างสิ่งอันดับสองและเข้าถึงพวกเขาหลาย ๆ ครั้งมันอาจจะเร็วกว่าที่จะใช้รายการแทน
แน่นอนถ้าคุณต้องการเปลี่ยนรายการรายการจะเร็วขึ้นแน่นอนเนื่องจากคุณต้องสร้าง tuple ใหม่ทั้งหมดเพื่อเปลี่ยนรายการหนึ่งรายการ (เนื่องจาก tuples ไม่มีการเปลี่ยนแปลง)
python -m timeit "x=tuple(xrange(999999))"
python -m timeit "x=list(xrange(999999))"
ตามที่คาดไว้อาจใช้เวลานานขึ้นเล็กน้อยในการทำให้สิ่งอันดับเป็นรูปธรรมกว่ารายการ
-s "SETUP_CODE"
จะดำเนินการก่อนที่จะหมดเวลารหัสที่เกิดขึ้นจริง
สิ่งอันดับมีแนวโน้มที่จะทำงานได้ดีกว่ารายการในเกือบทุกหมวดหมู่:
2) Tuples สามารถนำกลับมาใช้ใหม่ได้แทนที่จะคัดลอก
3) สิ่งอันดับมีขนาดกะทัดรัดและไม่จัดสรรเกิน
4) Tuples อ้างอิงองค์ประกอบของพวกเขาโดยตรง
ค่าคงที่ Tuples ของค่าคงที่สามารถคำนวณได้ล่วงหน้าโดยเครื่องมือเพิ่มประสิทธิภาพช่องมองของ Python หรือ AST-optimizer ในทางกลับกันรายการที่ได้รับการสร้างขึ้นจากศูนย์:
>>> 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 ได้รับการแก้ไขจึงสามารถจัดเก็บได้แน่นกว่ารายการที่ต้องจัดสรรมากเกินไปเพื่อให้การดำเนินการผนวก ()มีประสิทธิภาพ
สิ่งนี้ให้ประโยชน์พื้นที่ที่ดี tuples:
>>> 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
นี่คือวิธีการ(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;
โปรดทราบว่าวัตถุ tuple รวมตัวชี้ข้อมูลสองตัวโดยตรงในขณะที่วัตถุรายการมีชั้นของการเปลี่ยนทิศทางทางอ้อมไปยังอาร์เรย์ภายนอกที่ถือตัวชี้ข้อมูลสองตัว
Internally, tuples are stored a little more efficiently than lists, and also tuples can be accessed slightly faster.
คุณจะอธิบายผลลัพธ์จากคำตอบของ dF ได้อย่างไร?
tuple(some_tuple)
จะส่งคืนsome_tuple
ตัวเองหากsome_tuple
สามารถแฮชได้เมื่อเนื้อหานั้นไม่เปลี่ยนรูปและแฮช มิฉะนั้นtuple(some_tuple)
ส่งคืน tuple ใหม่ ตัวอย่างเช่นเมื่อsome_tuple
มีรายการที่ไม่แน่นอน
Tuples ที่ไม่เปลี่ยนรูปเป็นหน่วยความจำที่มีประสิทธิภาพมากกว่า รายการเพื่อประสิทธิภาพหน่วยความจำ overallocate เพื่ออนุญาตให้ผนวกโดยไม่มีค่าคงrealloc
ที่ ดังนั้นหากคุณต้องการวนซ้ำตามลำดับค่าคงที่ในรหัสของคุณ (เช่นfor direction in 'up', 'right', 'down', 'left':
) คุณควรใช้สิ่งอันดับเนื่องจาก tuples ดังกล่าวจะถูกคำนวณล่วงหน้าในเวลารวบรวม
ความเร็วในการเข้าถึงควรเท่ากัน (ทั้งคู่ถูกเก็บไว้เป็นอาร์เรย์ต่อเนื่องกันในหน่วยความจำ)
แต่alist.append(item)
เป็นที่ต้องการมากatuple+= (item,)
เมื่อคุณจัดการกับข้อมูลที่ไม่แน่นอน จำไว้ว่า tuples มีวัตถุประสงค์เพื่อใช้เป็นเรคคอร์ดโดยไม่มีชื่อฟิลด์
คุณควรพิจารณาarray
โมดูลในไลบรารีมาตรฐานหากรายการทั้งหมดในรายการหรือ tuple ของคุณเป็นประเภท 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 มีประสิทธิภาพในการอ่านมากขึ้นก็เพราะว่ามันไม่เปลี่ยนรูป
เหตุผลคือสิ่งอันดับสามารถเก็บไว้ในแคชหน่วยความจำซึ่งแตกต่างจากรายการ โปรแกรมจะอ่านจากตำแหน่งหน่วยความจำเสมอเนื่องจากสามารถเปลี่ยนแปลงได้ (สามารถเปลี่ยนแปลงได้ตลอดเวลา)