ทำไม a.insert (0,0) ช้ากว่า [0: 0] = [0] มาก?


61

การใช้insertฟังก์ชั่นของรายการนั้นช้ากว่าการใช้เอฟเฟ็กต์แบบเดียวกันมาก:

> python -m timeit -n 100000 -s "a=[]" "a.insert(0,0)"
100000 loops, best of 5: 19.2 usec per loop

> python -m timeit -n 100000 -s "a=[]" "a[0:0]=[0]"
100000 loops, best of 5: 6.78 usec per loop

(โปรดทราบว่าa=[]เป็นเพียงการตั้งค่าดังนั้นaเริ่มว่างเปล่าจากนั้นขยายเป็น 100,000 องค์ประกอบ)

ตอนแรกฉันคิดว่าอาจเป็นการค้นหาแอตทริบิวต์หรือการเรียกใช้ค่าใช้จ่ายหรือมากกว่านั้น แต่การแทรกเข้าใกล้จุดสิ้นสุดแสดงว่าไม่สำคัญ:

> python -m timeit -n 100000 -s "a=[]" "a.insert(-1,0)"
100000 loops, best of 5: 79.1 nsec per loop

ทำไมฟังก์ชั่น "insert single element" ที่เรียบง่ายโดยเฉพาะน่าจะช้ากว่ามาก?

ฉันยังสามารถทำซ้ำได้ที่ repl.it :

from timeit import repeat

for _ in range(3):
  for stmt in 'a.insert(0,0)', 'a[0:0]=[0]', 'a.insert(-1,0)':
    t = min(repeat(stmt, 'a=[]', number=10**5))
    print('%.6f' % t, stmt)
  print()

# Example output:
#
# 4.803514 a.insert(0,0)
# 1.807832 a[0:0]=[0]
# 0.012533 a.insert(-1,0)
#
# 4.967313 a.insert(0,0)
# 1.821665 a[0:0]=[0]
# 0.012738 a.insert(-1,0)
#
# 5.694100 a.insert(0,0)
# 1.899940 a[0:0]=[0]
# 0.012664 a.insert(-1,0)

ฉันใช้ Python 3.8.1 32- บิตบน Windows 10 64- บิต
repl.it ใช้ Python 3.8.1 64- บิตบน Linux 64- บิต


น่าสนใจที่จะทราบว่าa=[]; a[0:0]=[0]ทำเช่นเดียวกันกับa=[]; a[100:200]=[0]
smac89

มีเหตุผลใดที่คุณกำลังทดสอบสิ่งนี้ด้วยรายการที่ว่างเปล่า?
MisterMiyagi

@MisterMiyagi ดีฉันต้องเริ่มต้นด้วยบางสิ่งบางอย่าง โปรดทราบว่ามันว่างเปล่าก่อนการแทรกครั้งแรกและเติบโตถึง 100,000 องค์ประกอบในระหว่างการวัดประสิทธิภาพ
กองมากเกิน

@ smac89 a=[1,2,3];a[100:200]=[4]กำลังต่อ4ท้ายรายการที่aน่าสนใจ
Ch3steR

1
@ smac89 ในขณะที่มันเป็นจริงมันไม่ได้เกี่ยวข้องกับคำถามและฉันกลัวว่ามันอาจทำให้บางคนเข้าใจผิดคิดว่าฉันเป็นมาตรฐานa=[]; a[0:0]=[0]หรือนั่นa[0:0]=[0]ก็เหมือนกับa[100:200]=[0]...
Heap Overflow

คำตอบ:


57

ฉันคิดว่ามันอาจจะเพียงแค่ว่าพวกเขาลืมที่จะใช้ในmemmove list.insertหากคุณดูที่รหัสที่ list.insertใช้ในการเปลี่ยนองค์ประกอบคุณจะเห็นว่ามันเป็นเพียงลูปด้วยตนเอง:

for (i = n; --i >= where; )
    items[i+1] = items[i];

ขณะที่list.__setitem__บนพา ธ การกำหนดชิ้นใช้memmove :

memmove(&item[ihigh+d], &item[ihigh],
    (k - ihigh)*sizeof(PyObject *));

memmove โดยทั่วไปจะมีการปรับให้เหมาะสมจำนวนมากเช่นการใช้ประโยชน์จากคำแนะนำ SSE / AVX


5
ขอบคุณ สร้างปัญหาโดยอ้างถึงสิ่งนี้
กองมากเกิน

7
หากล่ามถูกสร้างขึ้น-O3โดยเปิดใช้งานการทำให้เป็นเวกเตอร์อัตโนมัติห่วงนั้นอาจรวบรวมได้อย่างมีประสิทธิภาพ แต่ถ้าคอมไพเลอร์รับรู้ลูปเป็น memmove และคอมไพล์ลงในการเรียกจริงmemmoveมันสามารถใช้ประโยชน์จากส่วนขยายชุดคำสั่งที่เปิดใช้งานในเวลารวบรวม (ดีถ้าคุณกำลังสร้างของคุณเองด้วย-march=nativeไม่มากนักสำหรับไบนารี distro ที่สร้างด้วยพื้นฐาน) และ GCC จะไม่เปิดใช้งานลูปเป็นค่าเริ่มต้นเว้นแต่ว่าคุณจะใช้ PGO ( -fprofile-generate/ run / ...-use)
Peter Cordes

@PeterCordes ฉันเข้าใจคุณถูกต้องไหมว่าถ้าคอมไพเลอร์รวบรวมมันเป็นการmemmoveโทรจริงมันจะสามารถใช้ประโยชน์จากส่วนขยายทั้งหมดที่มีอยู่ในเวลาดำเนินการได้หรือไม่?
กองมากเกิน

1
@HeapOverflow: ใช่ ตัวอย่างเช่นบน GNU / Linux glibc โอเวอร์โหลดความละเอียดของสัญลักษณ์ linker แบบไดนามิกด้วยฟังก์ชั่นที่เลือกรุ่น memmove asm ที่เขียนด้วยลายมือที่ดีที่สุดสำหรับเครื่องนี้ตามผลการตรวจจับ CPU ที่บันทึกไว้ (เช่นบน x86 ฟังก์ชั่น init ของ glibc ใช้cpuid) เหมือนกันสำหรับฟังก์ชั่น mem / str อื่น ๆ ดังนั้น distros สามารถคอมไพล์ด้วย-O2เพื่อสร้างไบนารีที่รันได้ทุกที่ แต่อย่างน้อยก็มี memcpy / memmove ใช้การโหลดวน AVX ที่ยังไม่ผ่านการควบคุม / การจัดเก็บ 32 ไบต์ต่อคำสั่ง (หรือแม้แต่ AVX512 ในซีพียูไม่กี่ตัวที่เป็นความคิดที่ดีฉันคิดว่า Xeon Phi)
Peter Cordes

1
@HeapOverflow: ไม่ได้มีหลายmemmoveเวอร์ชันอยู่ใน libc.so ซึ่งเป็น shared library สำหรับแต่ละฟังก์ชั่นการจัดส่งจะเกิดขึ้นหนึ่งครั้งในระหว่างการแก้ปัญหาสัญลักษณ์ (การรวมก่อนหน้าหรือการโทรครั้งแรกที่มีการผูกแบบดั้งเดิม) อย่างที่ฉันบอกไปมันเป็นแค่การโอเวอร์โหลด / ขอเกี่ยวว่าการเชื่อมโยงแบบไดนามิกนั้นเกิดขึ้นได้อย่างไร (โดยเฉพาะผ่านกลไก ifunc ของ GCC: code.woboq.org/userspace/glibc/sysdeps/x86_64/multiarch/ ...... ) ที่เกี่ยวข้อง: สำหรับความทรงจำตัวเลือกปกติของซีพียูสมัยใหม่คือ__memset_avx2_unaligned_erms คำถามและคำตอบ
Peter Cordes
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.