python vs bc khi đánh giá 6 ^ 6 ^ 6


29

Tôi đang đánh giá biểu thức 6^6^6bằng cách sử dụng pythonbcriêng biệt.

Nội dung của tập tin python là print 6**6**6. Khi tôi thực thi time python test.py, tôi nhận được đầu ra là

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

Và sau đó, tôi chạy lệnh time echo 6^6^6 | bccho tôi đầu ra sau

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

Từ những kết quả này, rõ ràng thời gian sys của python và bc lần lượt là 11ms và 5ms. Lệnh bc vượt trội hơn python ở mức thời gian sys nhưng khi nói đến người dùng và python thời gian thực nhanh hơn gần 4 lần so với bc . Những gì có thể đã đi đến đó. Tôi đã không ưu tiên cho các quy trình như vậy. Tôi đang cố gắng để hiểu tình huống này.


Vì vậy, bạn có nghĩa là thành phần sys chỉ cho thời gian tải và thời gian chạy sẽ được đưa ra trong thành phần người dùng của đầu ra?
ganessh

Tôi thực sự không chắc chắn, đó là lý do tại sao tôi đăng một bình luận. Nó chỉ là một phỏng đoán.
terdon

7
echo | bcliên quan đến việc khởi chạy một subshell vì đường ống - đó là nơi mà một số thời gian người dùng thêm của bạn có thể đến từ. Để thực hiện điều này một thử nghiệm công bằng, tập lệnh python nên đọc từ stdin để bạn có thể time echo 6**6**6 | whatever.py.
goldilocks

1
Tôi muốn đặt dòng lệnh be thành một tập lệnh và thời gian thực hiện điều đó. Hoặc sử dụng echo 6^6^6 | time bc.
daniel kullmann

1
Lưu ý bên: trong python 6**6**6biểu thức thực sự được tính toán tại thời gian biên dịch . Tuy nhiên vì bạn đang khởi chạy tệp trực tiếp thay vì nhập tệp từ mô-đun, nên điều này không thành vấn đề. Để xem sự khác biệt được đưa 10**12345678vào một a.pytệp và cố gắng nhập nó từ trình thông dịch tương tác. Sau đó đóng trình thông dịch, khởi động lại và nhập alại. Lần đầu tiên cần một khoảng thời gian đáng chú ý (vì python đang biên dịch mô-đun), trong khi lần thứ hai nó tải .pyc, sẽ là tức thời,
Bakuriu

Câu trả lời:


25

Python nhập một số lượng lớn tệp khi khởi động:

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

Mỗi trong số này đòi hỏi số lần thử thậm chí nhiều hơn khi mở tệp Python, bởi vì có nhiều cách để xác định mô-đun:

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

Mỗi lần "thử", ngoại trừ những lần được tích hợp sẵn, yêu cầu một cuộc gọi hệ thống / cấp độ os và mỗi lần "nhập" dường như kích hoạt khoảng 8 thông báo "thử". (Có nhiều cách để giảm điều này bằng cách sử dụng zipimport và mỗi đường dẫn trong PYTHONPATH của bạn có thể yêu cầu một cuộc gọi khác.)

Điều này có nghĩa là có gần 200 lệnh gọi hệ thống trước khi Python khởi động trên máy của tôi và "thời gian" gán nó cho "sys" chứ không phải "người dùng", vì chương trình người dùng đang chờ hệ thống thực hiện.

Khi so sánh, và như terdon đã nói, "bc" không có chi phí khởi đầu cao như vậy. Nhìn vào đầu ra dtruss (tôi có máy Mac; "strace" cho HĐH dựa trên Linux), tôi thấy rằng bc không thực hiện bất kỳ cuộc gọi hệ thống mở () hoặc stat () nào, ngoại trừ việc tải một vài chia sẻ các thư viện là khởi đầu, tất nhiên Python cũng vậy. Ngoài ra, Python có nhiều tệp để đọc hơn, trước khi nó sẵn sàng xử lý mọi thứ.

Chờ đĩa là chậm.

Bạn có thể hiểu được chi phí khởi động của Python bằng cách thực hiện:

time python -c pass

Đó là 0,032 giây trên máy của tôi, trong khi 'in 6 ** 6 ** 6' là 0,072 giây, do đó chi phí khởi động là 1/2 của tổng thời gian và phép tính + chuyển đổi thành số thập phân là nửa còn lại. Trong khi:

time echo 1 | bc

mất 0,005 giây và "6 ^ 6 ^ 6" mất 0,184 giây nên số mũ của bc chậm hơn 4 lần so với Python mặc dù bắt đầu nhanh hơn 7 lần.


4
Bạn có thể chôn chì ở đó. Bạn có thể muốn di chuyển bit kết thúc lên đầu.
Đạp xe

Chỉ cần ra khỏi sự quan tâm trên máy tính của tôi: Hiện python -c 'pass' 0m0.025s, thời gian python -c 'in 6 6 6' 0m0.087s nhưng lần python -c 'x = 6 6 6' 0m0.028s Vì vậy, hầu hết các thời gian là đầu ra số lượng lớn.
Steve Barnes

Có, chuyển đổi sang cơ sở 10 mất thời gian bậc hai theo số chữ số. Như một trường hợp cực đoan, hãy thử in một trong những số nguyên tố Mersenne lớn hơn. Việc tính toán rất nhanh, nhưng mất nhiều thời gian để in ở cơ sở 10.
Andrew Dalke

11

Tôi tìm thấy một câu trả lời hay về SO giải thích các lĩnh vực khác nhau:

  • Real là thời gian đồng hồ treo tường - thời gian từ khi bắt đầu đến khi kết thúc cuộc gọi. Đây là tất cả thời gian đã trôi qua bao gồm cả các lát thời gian được sử dụng bởi các quy trình khác và thời gian quy trình bị chặn (ví dụ nếu nó đang chờ I / O hoàn thành).

  • Người dùng là lượng thời gian CPU dành cho mã chế độ người dùng (bên ngoài kernel) trong quy trình. Đây chỉ là thời gian CPU thực tế được sử dụng để thực hiện quá trình. Các quy trình và thời gian khác mà quy trình chi tiêu bị chặn không được tính vào con số này.

  • Sys là lượng thời gian CPU dành cho kernel trong tiến trình. Điều này có nghĩa là thực thi thời gian CPU dành cho các cuộc gọi hệ thống trong kernel, trái ngược với mã thư viện, vẫn đang chạy trong không gian người dùng. Giống như 'người dùng', đây chỉ là thời gian CPU được sử dụng bởi quy trình. Xem bên dưới để biết mô tả ngắn gọn về chế độ kernel (còn được gọi là chế độ 'giám sát viên) và cơ chế gọi hệ thống.

Vì vậy, trong ví dụ cụ thể của bạn, phiên bản python nhanh hơn về thời gian thực tế cần thiết để hoàn thành. Tuy nhiên, cách tiếp cận python dành nhiều thời gian hơn trong không gian kernel, thực hiện các cuộc gọi đến các hàm kernel. Các bclệnh cơ bản dành không có thời gian trong vũ trụ hạt nhân và tất cả những gì của thời gian cho việc sử dụng không gian, có lẽ chạy nội bcmã.

Điều này không có gì khác biệt với bạn, thông tin duy nhất bạn thực sự quan tâm là realthời gian thực tế trôi qua giữa lúc khởi chạy lệnh và nhận đầu ra.

Bạn cũng nên lưu ý rằng những khác biệt nhỏ này không ổn định, chúng cũng sẽ phụ thuộc vào tải của hệ thống của bạn và sẽ thay đổi mỗi khi bạn chạy lệnh:

$ 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

Tôi sẽ giải thích nó từ một góc độ khác.

Để công bằng, bccó lợi thế vì nó không phải đọc bất cứ thứ gì từ đĩa và chỉ cần blob / nhị phân của nó trong khi python phải nhập một loạt các mô-đun + đọc một tệp. Vì vậy, bài kiểm tra của bạn có thể được thiên vị bc. Để thực sự kiểm tra nó, bạn nên sử dụng bc -q filenơi filechứa:

6^6^6
quit

Thay đổi chỉ sửa đổi thời gian sử dụng echo:

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

Để sử dụng tệp:

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

(bạn sẽ phải sử dụng phương pháp của terdon để nhận thấy sự khác biệt lớn hơn, nhưng ít nhất chúng tôi biết chúng là như vậy)

Bây giờ, từ phối cảnh python, python cần đọc từ đĩa, biên dịch thực thi mỗi lần tệp, cộng với việc tải các mô-đun như điểm Andrew , khiến thời gian thực hiện chậm hơn. Nếu bạn biên dịch mã byte của tập lệnh python, bạn sẽ nhận thấy rằng phải mất ít hơn 50% tổng thời gian để thực thi mã:

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

biên dịch:

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

Như bạn có thể thấy, có một số yếu tố có thể ảnh hưởng đến việc thực hiện thời gian giữa các công cụ khác nhau.


3

Tôi đã có lợi ích khi đọc các câu trả lời khác. Đối với những người mới bắt đầu, những người như tôi nên biết lý do tại sao chúng ta xử lý một số nguyên lớn như vậy ở đây là cả hai Pythonbcthực hiện mở rộng lũy ​​thừa liên kết đúng , có nghĩa là đây không phải là 6^36chúng ta đang đánh giá 6^46656mà là lớn hơn đáng kể. 1

Sử dụng các biến thể trên các lệnh sau, chúng ta có thể trích xuất trung bình cho một yếu tố cụ thể của đầu ra của cả timetừ và lệnh dành riêng:

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

Có thể đi một tuyến đường khác và loại bỏ các tập tin hoàn toàn khỏi so sánh. Ngoài ra, chúng ta có thể so sánh thời gian bc với một cái gì đó giống như dclệnh, như lịch sử trước đây là một "bộ xử lý kết thúc trước" để sau này. Các lệnh sau đã được tính thời gian:

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

Lưu ý dclệnh là liên kết trái cho lũy thừa. 2

Chúng tôi có một số kết quả với time(bash) trong 1000 lần lặp (tính bằng giây):

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

bcdccung cấp hiệu suất tương đương trong bối cảnh này.

Ít chính xác hơn 3 kết quả từ lệnh /usr/bin/timeGNU time(độ chính xác của thang đo không hợp lệ ở đây nhưng kết quả tương tự nhau):

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

Một lợi thế của /usr/bin/timenó là nó cung cấp -vtùy chọn mang lại nhiều thông tin hơn và cuối cùng có thể hữu ích.

Cũng có thể đánh giá nội bộ này để nói chuyện với timeitmô-đun 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

Điều đó nhanh hơn một chút so với những gì chúng ta thấy trước đây. Hãy thử bản thân trình thông dịch:

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

Đó là cách nhanh nhất tôi từng thấy.


Nếu chúng ta đánh giá mức lũy thừa nhỏ hơn như thế 6^6, thì lệnh thời gian mang lại kết quả đáng ngạc nhiên - sử dụng các forlệnh lặp tương tự mà chúng ta đã sử dụng hiện có:

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

Vì vậy, với một số nguyên nhỏ hơn bclà đột nhiên nhanh hơn nhiều ?? Từ khởi động lại hệ thống đến lần chạy thứ hai không có sự khác biệt. Tuy nhiên, đồng thời, nếu chúng ta sử dụng timeitcho Python, chúng ta sẽ nhận được:

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

Đây là micro giây , không phải mili giây, do đó, điều này không khớp với kết quả chậm hơn nhiều khi sử dụng forvòng lặp. Có thể các công cụ khác được yêu cầu để kiểm tra điều này hơn nữa và như những người khác đã giải thích có nhiều điều hơn là bắt mắt ở đây. Có vẻ như Python đã nhanh hơn trong kịch bản của câu hỏi nhưng không rõ liệu kết luận có thể được rút ra ngoài điều đó ...


1. Không cần phải nói nó vượt quá phạm vi của một cái gì đó như mở rộng số học của ech nghĩa là echo $((6**6**6))- bashcũng có thể là liên kết đúng cho điều đó tức là 6^6^6 = 6^(6^6).

2. So sánh với điều này : 6 6 ^ 6 ^ p.

3. Có thể lệnh thời gian GNU cung cấp thêm thông tin khi chạy trên BSD UNIX (tài liệu thông tin thời gian GNU): Hầu hết thông tin được hiển thị theo 'thời gian' được lấy từ lệnh gọi hệ thống 'Wait3'. Các con số chỉ tốt bằng những con số được trả về bởi 'Wait3'. Nhiều hệ thống không đo lường tất cả các tài nguyên mà 'thời gian' có thể báo cáo; những tài nguyên được báo cáo là không. Các hệ thống đo lường hầu hết hoặc tất cả các tài nguyên đều dựa trên 4.2 hoặc 4.3BSD. Các bản phát hành BSD sau này sử dụng mã quản lý bộ nhớ khác nhau để đo lường ít tài nguyên hơn. - Trên các hệ thống không có cuộc gọi 'Wait3' trả về thông tin trạng thái, cuộc gọi hệ thống 'lần' được sử dụng thay thế. Nó cung cấp ít thông tin hơn nhiều so với 'Wait3', do đó, trên các hệ thống 'thời gian' đó báo cáo hầu hết các tài nguyên là 0.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.