หลาม vs bc ในการประเมิน 6 ^ 6 ^ 6


29

ฉันประเมินการแสดงออก6^6^6โดยใช้pythonและbcแยก

print 6**6**6เนื้อหาของไฟล์หลามคือ เมื่อฉันรันtime python test.pyฉันจะได้ผลลัพธ์เป็น

real        0m0.067s
user        0m0.050s
sys         0m0.011s

จากนั้นฉันรันคำสั่งtime echo 6^6^6 | bcที่ให้ผลลัพธ์ต่อไปนี้ให้ฉัน

real        0m0.205s
user        0m0.197s
sys         0m0.005s

จากผลลัพธ์เหล่านี้เห็นได้ชัดว่าเวลา sys ที่ใช้โดย python และ bc คือ 11ms และ 5ms ตามลำดับ คำสั่ง BC เฮงหลามในระดับเวลา SYSแต่เมื่อมันมาถึงผู้ใช้และเวลาจริงหลามเป็นเกือบ 4 ครั้งเร็วกว่าปีก่อนคริสตกาล สิ่งที่อาจจะไปที่นั่น ฉันไม่ได้ให้ความสำคัญกับกระบวนการเช่นนี้ ฉันพยายามที่จะเข้าใจสถานการณ์นี้


ดังนั้นคุณหมายถึงองค์ประกอบ sys เพียงให้เวลาที่ใช้ในการโหลดและรันไทม์จะได้รับในส่วนผู้ใช้ของการส่งออก?
ganessh

ฉันไม่แน่ใจจริงๆนั่นคือเหตุผลที่ฉันโพสต์ความคิดเห็น มันเป็นเพียงการเดา
terdon

7
echo | bcเกี่ยวข้องกับการเปิดตัว subshell เนื่องจากไปป์ - นั่นคือเวลาที่ผู้ใช้เพิ่มเติมของคุณอาจมาจาก นี้เพื่อให้การทดสอบอย่างเป็นธรรมสคริปต์หลามควรอ่านจาก stdin time echo 6**6**6 | whatever.pyเพื่อให้คุณสามารถ
goldilocks

1
ฉันอยากจะใส่บรรทัดคำสั่งลงในสคริปต์และใช้เวลาในการดำเนินการ echo 6^6^6 | time bcหรือการใช้งาน
daniel kullmann

1
บันทึก Side: ในหลาม6**6**6แสดงออกคำนวณจริงที่รวบรวมเวลา อย่างไรก็ตามเนื่องจากคุณกำลังเรียกใช้ไฟล์โดยตรงแทนที่จะนำเข้าจากโมดูลสิ่งนี้ไม่สำคัญ หากต้องการดูความแตกต่างที่ใส่10**12345678เข้าไปในa.pyไฟล์และพยายามนำเข้าจากล่ามแบบโต้ตอบ จากนั้นปิดล่ามรีสตาร์ทและนำเข้าaอีกครั้ง ครั้งแรกมันควรจะใช้เวลาจำนวนหนึ่งที่เห็นได้ชัด (เพราะไพ ธ อนกำลังรวบรวมโมดูล) ในขณะที่ครั้งที่สองมันโหลด.pycซึ่งควรจะเป็นทันที
Bakuriu

คำตอบ:


25

Python นำเข้าไฟล์จำนวนมากเมื่อเริ่มต้น:

% python -c 'import sys; print len(sys.modules)'
39

แต่ละสิ่งเหล่านี้ต้องการความพยายามในการเปิดไฟล์ Python มากยิ่งขึ้นเนื่องจากมีหลายวิธีในการกำหนดโมดูล:

% python -vv -c 'pass'
# installing zipimport hook
import zipimport # builtin
# installed zipimport hook
# trying site.so
# trying sitemodule.so
# trying site.py
# trying site.pyc
# trying /System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site.so
# trying /System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/sitemodule.so
# trying /System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site.py
# /System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site.pyc matches /System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site.py
import site # precompiled from /System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site.pyc
# trying os.so
# trying osmodule.so
# trying os.py
# trying os.pyc
# trying /System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/os.so
# trying /System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/osmodule.so
# trying /System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/os.py
# /System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/os.pyc matches /System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/os.py
import os # precompiled from /System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/os.pyc
    ...

การ "พยายาม" แต่ละครั้งยกเว้นการสร้างขึ้นในตัวต้องใช้การเรียกระดับระบบ / ระบบและแต่ละรายการ "นำเข้า" ดูเหมือนจะเรียกใช้ข้อความประมาณ 8 รายการ "ลอง" (มีวิธีการลดขนาดนี้โดยใช้ zipimport และแต่ละเส้นทางใน PYTHONPATH ของคุณอาจต้องมีการโทรอีกครั้ง)

ซึ่งหมายความว่ามีการเรียกใช้ระบบสถิติเกือบ 200 ครั้งก่อนที่ Python จะเริ่มทำงานบนเครื่องของฉันและ "เวลา" กำหนดให้กับ "sys" มากกว่า "ผู้ใช้" เนื่องจากโปรแกรมผู้ใช้กำลังรอให้ระบบทำสิ่งต่างๆ

โดยการเปรียบเทียบและเช่นเดียวกับ terdon กล่าวว่า "bc" ไม่มีค่าใช้จ่ายเริ่มต้นที่สูง ดูที่เอาต์พุต dtruss (ฉันมี Mac; "strace" สำหรับระบบปฏิบัติการที่ใช้ Linux) ฉันเห็นว่า bc ไม่ได้เปิดระบบ () หรือ stat () การเรียกใช้ระบบของตัวเองยกเว้นการโหลดที่ใช้ร่วมกันไม่กี่แห่ง ห้องสมุดคือจุดเริ่มต้นซึ่งแน่นอนว่า Python ก็ทำได้เช่นกัน นอกจากนี้ Python มีไฟล์ให้อ่านเพิ่มเติมก่อนที่มันจะพร้อมที่จะประมวลผลอะไรก็ได้

รอดิสก์ช้า

คุณสามารถเข้าใจถึงต้นทุนการเริ่มต้นของ Python ได้โดยทำดังนี้

time python -c pass

มันเป็น 0.032s บนเครื่องของฉันในขณะที่ 'print 6 ** 6 ** 6' คือ 0.072s ดังนั้นราคาเริ่มต้นคือ 1/2 ของเวลาโดยรวมและการคำนวณ + การแปลงเป็นทศนิยมเป็นครึ่งหนึ่ง ในขณะที่:

time echo 1 | bc

ใช้เวลา 0.005s และ "6 ^ 6 ^ 6" ใช้เวลา 0.184 วินาทีดังนั้นการยกกำลังของ bc นั้นช้ากว่า 4x4 เท่าของ Python ถึงแม้ว่ามันจะเร็วขึ้น 7x ในการเริ่มต้น


4
คุณฝังสารตะกั่วไว้ที่นั่น คุณอาจต้องการย้ายบิตสุดท้ายไปด้านบน
Riking

เพิ่งหมดความสนใจในเครื่องของฉัน: time python -c 'pass' 0m0.025s, time python -c 'พิมพ์ 6 6 6' 0m0.087s แต่ python time -c 'x = 6 6 6' 0m0.028s ดังนั้นส่วนใหญ่ของ เวลากำลังแสดงผลเป็นจำนวนมาก
Steve Barnes

ใช่การแปลงฐาน 10 ใช้เวลากำลังสองในจำนวนหลัก ในกรณีที่รุนแรงให้ลองพิมพ์หนึ่งใน Mersenne ที่ใหญ่กว่า มันเร็วมากในการคำนวณ แต่ใช้เวลานานในการพิมพ์ในฐาน 10
Andrew Dalke

11

ฉันพบคำตอบที่ดีในการอธิบายฟิลด์ต่างๆ:

  • แบบเรียลไทม์เป็นนาฬิกาแขวนเวลา - เวลาตั้งแต่ต้นจนจบการโทร นี่เป็นเวลาที่ผ่านไปทั้งหมดรวมถึงตัวแบ่งเวลาที่ใช้โดยกระบวนการอื่นและเวลาที่กระบวนการใช้บล็อก (ตัวอย่างเช่นถ้ารอ I / O ให้เสร็จ)

  • ผู้ใช้คือจำนวนเวลา CPU ที่ใช้ในรหัสโหมดผู้ใช้ (นอกเคอร์เนล) ภายในกระบวนการ นี่เป็นเวลา CPU จริงที่ใช้ในการดำเนินการตามกระบวนการเท่านั้น กระบวนการและเวลาอื่น ๆ ที่กระบวนการใช้ไปจะไม่ถูกนับรวมในรูปนี้

  • Sys คือจำนวนเวลา CPU ที่ใช้ในเคอร์เนลภายในกระบวนการ นี่หมายถึงการประมวลผลเวลา CPU ที่ใช้ในการเรียกใช้ระบบภายในเคอร์เนลซึ่งตรงข้ามกับรหัสห้องสมุดซึ่งยังคงทำงานในพื้นที่ผู้ใช้ เช่นเดียวกับ 'ผู้ใช้' นี่เป็นเพียงเวลา CPU ที่ใช้โดยกระบวนการ ดูคำอธิบายสั้น ๆ ของโหมดเคอร์เนล (เรียกอีกอย่างว่าโหมด 'ผู้ควบคุม') และกลไกการเรียกระบบ

ดังนั้นในตัวอย่างเฉพาะของคุณเวอร์ชันของงูหลามนั้นเร็วกว่าในแง่ของเวลาจริงที่ใช้ในการทำให้สมบูรณ์ อย่างไรก็ตามวิธีการของไพ ธ อนใช้เวลากับเคอร์เนลพื้นที่มากขึ้นทำให้สามารถเรียกใช้ฟังก์ชันเคอร์เนลได้ bcคำสั่งใช้เวลาเป็นหลักเวลาไม่นานในพื้นที่เคอร์เนลและทุกครั้งที่มันคือการใช้จ่ายในการใช้พื้นที่สันนิษฐานทำงานภายในbcรหัส

สิ่งนี้ไม่สร้างความแตกต่างให้คุณข้อมูลเดียวที่คุณสนใจจริงๆrealคือเวลาที่ผ่านไประหว่างการเรียกใช้คำสั่งและการรับเอาต์พุต

คุณควรทราบด้วยว่าความแตกต่างเล็ก ๆ เหล่านี้ไม่เสถียรพวกเขาจะขึ้นอยู่กับโหลดของระบบของคุณและจะเปลี่ยนแปลงทุกครั้งที่คุณรันคำสั่ง:

$ for i in {1..10}; do ( time python test.py > /dev/null ) 2>&1; done | grep user
user    0m0.056s
user    0m0.052s
user    0m0.052s
user    0m0.052s
user    0m0.060s
user    0m0.052s
user    0m0.052s
user    0m0.056s
user    0m0.048s
user    0m0.056s

$ for i in {1..10}; do ( time echo 6^6^6 | bc > /dev/null ) 2>&1; done | grep user
user    0m0.188s
user    0m0.188s
user    0m0.176s
user    0m0.176s
user    0m0.172s
user    0m0.176s
user    0m0.180s
user    0m0.172s
user    0m0.172s
user    0m0.172s

10

ฉันจะอธิบายมันจากมุมมองอื่น

เพื่อความเป็นธรรมbcมีข้อได้เปรียบเนื่องจากไม่ต้องอ่านอะไรจากดิสก์และต้องการเพียงแค่ blob / binaries ในขณะที่ python จำเป็นต้องนำเข้าชุดของโมดูล + อ่านไฟล์ bcดังนั้นการทดสอบของคุณอาจจะลำเอียงไปทาง ในการทดสอบจริงคุณควรใช้bc -q fileที่fileมี:

6^6^6
quit

การเปลี่ยนเพียงแค่ปรับเปลี่ยนเวลาในการใช้งานecho:

bc  0.33s user 0.00s system 80% cpu 0.414 total

วิธีใช้ไฟล์:

bc -q some  0.33s user 0.00s system 86% cpu 0.385 total

(คุณจะต้องใช้วิธีการของ terdon เพื่อสังเกตเห็นความแตกต่างที่มากขึ้น แต่อย่างน้อยเราก็รู้ว่าพวกเขาเป็น)

ตอนนี้จากมุมมองของงูหลามงูเหลือมจำเป็นต้องอ่านจากดิสก์รวบรวมและดำเนินการทุกครั้งที่ไฟล์รวมถึงการโหลดโมดูลเป็นจุดแอนดรูซึ่งทำให้เวลาดำเนินการช้าลง หากคุณรวบรวมรหัสไบต์ของสคริปต์ไพ ธ อนคุณจะสังเกตเห็นว่าใช้เวลารวมน้อยลง 50% ในการเรียกใช้งานโค้ด:

python some.py > /dev/null  0.25s user 0.01s system 63% cpu 0.413 total

เรียบเรียง:

./some.pyc  0.22s user 0.00s system 77% cpu 0.282 total

อย่างที่คุณเห็นมีปัจจัยหลายอย่างที่ส่งผลต่อการทำงานของเวลาระหว่างเครื่องมือต่าง ๆ


3

ฉันได้รับประโยชน์จากการอ่านคำตอบอื่น ๆ สำหรับผู้เริ่มต้นคนอย่างฉันควรรู้ว่าทำไมเราถึงต้องจัดการกับจำนวนเต็มขนาดใหญ่เช่นนี้นั่นก็คือทั้งคู่Pythonและbcทำการขยายการยกกำลังเชื่อมโยงที่ถูกต้องซึ่งหมายความว่านี่ไม่ใช่6^36เรากำลังประเมิน แต่ค่อนข้าง6^46656ใหญ่กว่ามาก 1

การใช้ชุดรูปแบบในคำสั่งต่อไปนี้เราสามารถแยกค่าเฉลี่ยสำหรับองค์ประกอบเฉพาะของเอาต์พุตของทั้งtimeคำที่สงวนและคำสั่ง

for i in {1..1000}; do (time echo 6^6^6 | bc > /dev/null) 2>&1; done | grep 'rea' | sed -e s/.*m// | awk '{sum += $1} END {print sum / NR}'

for i in {1..1000}; do (/usr/bin/time -v sh -c 'echo 6^6^6 | bc > /dev/null') 2>&1; done | grep 'Use' | sed -e s/.*:// | awk '{sum += $1} END {print sum / NR}'

เป็นไปได้ที่จะไปยังเส้นทางอื่นและลบไฟล์ทั้งหมดออกจากการเปรียบเทียบ นอกจากนี้เรายังสามารถเปรียบเทียบระยะเวลา BC กับบางสิ่งบางอย่างเช่นdcคำสั่งเป็นอดีตอดีตคือ "การประมวลผลส่วนหน้า" เพื่อหลัง คำสั่งต่อไปนี้ถูกตั้งเวลา:

echo 6^6^6 | bc
echo 6 6 6 ^ ^ p | dc
echo print 6**6**6 | python2.7

หมายเหตุdcคำสั่งนั้นเชื่อมโยงกันเพื่อการยกกำลัง 2

เรามีผลลัพธ์บางส่วนด้วยtime(ทุบตี) เป็นเวลา 1,000 รอบ (เป็นวินาที):

0.229678 real bc
0.228348 user bc
0.000569 sys bc
0.23306  real dc
0.231786 user dc
0.000395 sys dc
0.07 real python
0.065907 user python
0.003141 sys python

bcและdcเสนอประสิทธิภาพที่เทียบเท่าในบริบทนี้

ความแม่นยำน้อยกว่า3ผลลัพธ์จากคำสั่ง/usr/bin/timeGNU time(ความแม่นยำของสเกลไม่ถูกต้องที่นี่ แต่ผลลัพธ์คล้ายกัน):

0.2224 user bc
0 sys bc
0.23 Elapsed bc
0.22998 user dc
0 sys dc
0.23 Elapsed dc
0.06008 user python
0 sys python
0.07 Elapsed python

ข้อดีของ/usr/bin/timeมันคือมันมี-vตัวเลือกที่ให้ข้อมูลมากกว่าซึ่งอาจเป็นประโยชน์ในที่สุด

นอกจากนี้ยังเป็นไปได้ที่จะประเมินผลภายในเพื่อพูดคุยกับtimeitโมดูล Python:

python2.7 -m timeit -n 1000 -r 1 'print 6**6**6' | grep 'loops'
1000 loops, best of 1: 55.4 msec per loop

มันเร็วกว่าที่เราเห็นนิดหน่อย ลองล่ามเอง:

>>> import timeit
>>> import sys
>>> import os
>>> T = timeit.Timer("print 6**6**6")
>>> n = int(1000)
>>> f = open(os.devnull, 'w')
>>> sys.stdout = f
>>> t = t.timeit(n)
>>> sys.stdout = sys.__stdout__
>>> print t/n
0.0553743481636

นั่นคือเร็วที่สุดที่ฉันเคยเห็น


หากเราประเมินค่า exponentiation ที่น้อยกว่าเช่น6^6นั้นคำสั่ง time จะให้ผลลัพธ์ที่น่าประหลาดใจโดยใช้forคำสั่ง loop เดียวกันกับที่เราใช้ตอนนี้

0.001001 bc real
0.000304 user
0.000554 sys
0.014    python real i.e. 10x more than bc??
0.010432 user
0.002606 sys

ดังนั้นด้วยจำนวนเต็มขนาดเล็กbcจะเร็วกว่าทั้งหมดทันที? จากการรีบูตระบบไปจนถึงการรันครั้งที่สองก็ไม่ต่างอะไร แต่ในเวลาเดียวกันถ้าเราใช้timeitสำหรับ Python เราจะได้รับ:

python2.7 -m timeit -n 100000 -r 1 'print 6**6' | grep loops  
100000 loops, best of 1: 0.468 usec per loop

นี่คือไมโครวินาทีไม่ใช่มิลลิวินาทีดังนั้นจึงไม่ตรงกับผลลัพธ์ที่ช้ากว่ามากเมื่อใช้forลูป อาจต้องใช้เครื่องมืออื่นเพื่อทดสอบสิ่งนี้เพิ่มเติมและในขณะที่คนอื่น ๆ อธิบายว่ามันมีมากกว่าที่เห็น ดูเหมือนว่า Python จะเร็วกว่าในสถานการณ์ของคำถาม แต่ไม่ชัดเจนว่าจะสามารถสรุปได้อย่างไร ...


1. ความจำเป็นที่จะบอกว่ามันเกินขอบเขตของสิ่งที่ต้องการสะท้อนการขยายตัวทางคณิตศาสตร์เช่นecho $((6**6**6))- bashยังเกิดขึ้นเป็นขวาเชื่อมโยงสำหรับ 6^6^6 = 6^(6^6)IE

2. 6 6 ^ 6 ^ pเปรียบเทียบกับนี้:

3. อาจเป็นไปได้ที่คำสั่งเวลาของ GNU จะให้ข้อมูลเพิ่มเติมเมื่อทำงานกับ BSD UNIX (เอกสารข้อมูลเวลาของ GNU): ข้อมูลส่วนใหญ่ที่แสดงโดย 'เวลา' นั้นได้มาจากการเรียกระบบ 'wait3' ตัวเลขนั้นดีเท่าตัวเลขที่ส่งคืนโดย 'wait3' ระบบจำนวนมากไม่ได้วัดทรัพยากรทั้งหมดที่ 'เวลา' สามารถรายงานได้ ทรัพยากรเหล่านั้นถูกรายงานว่าเป็นศูนย์ ระบบที่ใช้วัดทรัพยากรส่วนใหญ่หรือทั้งหมดนั้นใช้ระบบ 4.2 หรือ 4.3BSD BSD รุ่นใหม่กว่าใช้รหัสการจัดการหน่วยความจำที่แตกต่างกันซึ่งวัดทรัพยากรน้อยลง - สำหรับระบบที่ไม่มีการเรียก 'wait3' ที่ส่งคืนข้อมูลสถานะการเรียกใช้ระบบ 'times' จะถูกใช้แทน มันให้ข้อมูลน้อยกว่า 'wait3' ดังนั้นเวลา 'ระบบ' เหล่านั้นจึงรายงานว่าทรัพยากรส่วนใหญ่เป็นศูนย์

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.