Manakah yang lebih cepat dalam Python: x **. 5 atau math.sqrt (x)?


188

Saya sudah lama bertanya-tanya tentang hal ini. Seperti judulnya, mana yang lebih cepat, fungsi sebenarnya atau hanya menaikkan setengah daya?

MEMPERBARUI

Ini bukan masalah optimasi prematur. Ini hanyalah pertanyaan tentang bagaimana sebenarnya kode yang mendasarinya bekerja. Apa teori tentang bagaimana kode Python bekerja?

Saya mengirim Guido van Rossum email sebab saya benar-benar ingin tahu perbedaan dalam metode ini.

Email saya:

Setidaknya ada 3 cara untuk melakukan root kuadrat dalam Python: math.sqrt, operator '**' dan pow (x, .5). Saya hanya ingin tahu perbedaan dalam implementasi masing-masing. Dalam hal efisiensi, mana yang lebih baik?

Jawabannya:

pow dan ** adalah setara; math.sqrt tidak berfungsi untuk bilangan kompleks, dan tautan ke fungsi C sqrt (). Yang mana yang lebih cepat, saya tidak tahu ...


81
Itu luar biasa, Guido merespons email.
Evan Fosmark

3
Evan, saya terkejut saya mendapat jawaban
Tidak

11
Saya kira ini bukan pertanyaan yang buruk. Sebagai contoh, x * x penuh 10 kali lebih cepat daripada x ** 2. Keterbacaan adalah perubahan dalam situasi ini, jadi mengapa tidak melakukan cara cepat?
TM.

12
Casey, saya bersama Anda dalam hal "optimasi prematur". :) Pertanyaan Anda tidak terlihat seperti optimasi prematur bagi saya: tidak ada risiko salah satu varian merusak kode Anda. Ini lebih merupakan masalah mengetahui lebih baik apa yang Anda lakukan (dalam hal waktu eksekusi) ketika Anda memilih pow () daripada math.sqrt ().
Eric O Lebigot

8
Ini bukan optimasi prematur, melainkan menghindari pesimisasi dini (ref. No. 28, standar pengkodean C ++, A.Alexandrescu). Jika math.sqrtrutin lebih dioptimalkan (sebagaimana adanya) dan mengekspresikan maksud lebih jelas, itu harus selalu lebih disukai x**.5. Ini bukan optimasi dini untuk mengetahui apa yang Anda tulis, dan memilih alternatif yang lebih cepat dan memberikan kejelasan kode yang lebih banyak. Jika demikian, Anda perlu berdebat dengan baik mengapa Anda memilih alternatif lain.
swalog

Jawaban:


90

math.sqrt(x)secara signifikan lebih cepat daripada x**0.5.

import math
N = 1000000
%%timeit
for i in range(N):
    z=i**.5

10 loop, terbaik 3: 156 ms per loop

%%timeit
for i in range(N):
    z=math.sqrt(i)

10 loop, terbaik 3: 91,1 ms per loop

Menggunakan Python 3.6.9 ( notebook ).


Saya sekarang sudah menjalankannya 3 kali di codepad.org dan ketiga kali a () jauh lebih cepat daripada b ().
Jeremy Ruten

10
Modul timeit standar adalah teman Anda. Itu menghindari perangkap umum ketika datang ke mengukur waktu eksekusi!
Eric O Lebigot

1
Berikut adalah hasil skrip Anda: zoltan @ host: ~ $ python2.5 p.py Mengambil 0.183226 detik Butuh 0.155829 detik zoltan @ host: ~ $ python2.4 p.py Mengambil 0.181142 detik Mengambil 0.153742 detik zoltan @ host: ~ $ python2.6 p.py Mengambil 0,157436 detik Mengambil 0,093905 detik Sistem target: Ubuntu Linux CPU: Intel (R) Core (TM) 2 Duo CPU T9600 @ 2,80GHz Seperti yang Anda lihat, saya mendapat hasil yang berbeda. Menurut ini jawaban Anda tidak umum.
zoli2k

2
Codepad adalah layanan yang hebat, tetapi mengerikan untuk pengaturan waktu, maksud saya siapa yang tahu seberapa sibuk server pada saat tertentu. Setiap proses berpotensi memberikan hasil yang sangat berbeda
adamJLev

1
Saya telah menambahkan perbandingan kinerja x ** .5 vs sqrt (x) untuk py32, py31, py30, py27, py26, pypy, jython, py25, py24 interpreter di Linux. gist.github.com/783011
jfs

19
  • aturan optimasi pertama: jangan lakukan itu
  • aturan kedua: jangan lakukan itu dulu

Berikut ini beberapa timing (Python 2.5.2, Windows):

$ python -mtimeit -s"from math import sqrt; x = 123" "x**.5"
1000000 loops, best of 3: 0.445 usec per loop

$ python -mtimeit -s"from math import sqrt; x = 123" "sqrt(x)"
1000000 loops, best of 3: 0.574 usec per loop

$ python -mtimeit -s"import math; x = 123" "math.sqrt(x)"
1000000 loops, best of 3: 0.727 usec per loop

Tes ini menunjukkan bahwa x**.5sedikit lebih cepat daripada sqrt(x).

Untuk Python 3.0 hasilnya sebaliknya:

$ \Python30\python -mtimeit -s"from math import sqrt; x = 123" "x**.5"
1000000 loops, best of 3: 0.803 usec per loop

$ \Python30\python -mtimeit -s"from math import sqrt; x = 123" "sqrt(x)"
1000000 loops, best of 3: 0.695 usec per loop

$ \Python30\python -mtimeit -s"import math; x = 123" "math.sqrt(x)"
1000000 loops, best of 3: 0.761 usec per loop

math.sqrt(x)selalu lebih cepat daripada x**.5di komputer lain (Ubuntu, Python 2.6 dan 3.1):

$ python -mtimeit -s"from math import sqrt; x = 123" "x**.5"
10000000 loops, best of 3: 0.173 usec per loop
$ python -mtimeit -s"from math import sqrt; x = 123" "sqrt(x)"
10000000 loops, best of 3: 0.115 usec per loop
$ python -mtimeit -s"import math; x = 123" "math.sqrt(x)"
10000000 loops, best of 3: 0.158 usec per loop
$ python3.1 -mtimeit -s"from math import sqrt; x = 123" "x**.5"
10000000 loops, best of 3: 0.194 usec per loop
$ python3.1 -mtimeit -s"from math import sqrt; x = 123" "sqrt(x)"
10000000 loops, best of 3: 0.123 usec per loop
$ python3.1 -mtimeit -s"import math; x = 123" "math.sqrt(x)"
10000000 loops, best of 3: 0.157 usec per loop

10

Berapa banyak akar kuadrat yang benar-benar Anda lakukan? Apakah Anda mencoba menulis beberapa mesin grafis 3D dengan Python? Jika tidak, lalu mengapa menggunakan kode yang cryptic over code yang mudah dibaca? Perbedaan waktu adalah lebih sedikit daripada yang dapat dilihat siapa pun di hampir semua aplikasi yang dapat saya lihat. Saya benar-benar tidak bermaksud untuk meletakkan pertanyaan Anda, tetapi tampaknya Anda terlalu jauh dengan optimasi prematur.


16
saya tidak merasa sedang melakukan optimasi prematur. Ini lebih merupakan pertanyaan sederhana untuk memutuskan dari 2 metode berbeda, yang, rata-rata, akan lebih cepat.
Tidak

2
Kibbee: itu pasti pertanyaan yang valid, tapi saya berbagi kekecewaan Anda pada sejumlah pertanyaan di Stack Overflow yang menyiratkan bahwa penanya melakukan semua jenis optimasi prematur. Ini jelas merupakan persentase besar dari pertanyaan yang diajukan untuk setiap bahasa.
Eli Courtwright

2
Apakah math.sqrt (x) lebih mudah dibaca daripada x ** 0,5? Saya pikir mereka berdua cukup jelas root kuadrat ... setidaknya jika Anda terbiasa dengan python. Jangan panggil operator standar python seperti ** "cryptic" hanya karena Anda tidak terbiasa dengan python.
TM.

5
Saya tidak berpikir operator ** itu samar. Saya pikir bahwa menaikkan sesuatu ke eksponen 0,5 sebagai metode untuk mendapatkan akar kuadrat menjadi sedikit samar bagi mereka yang tidak mengikuti matematika mereka.
Kibbee

13
Bagaimana jika dia membuat mesin 3D dengan Python?
Chris Burt-Brown

9

Dalam benchmark mikro ini, math.sqrtakan lebih lambat, karena sedikit waktu yang dibutuhkan untuk mencari sqrtdalam namespace matematika. Anda dapat sedikit meningkatkannya dengan

 from math import sqrt

Meski begitu, menjalankan beberapa variasi melalui timeit, menunjukkan sedikit keuntungan kinerja (4-5%) untuk x**.5

Menariknya, lakukan

 import math
 sqrt = math.sqrt

mempercepatnya bahkan lebih, hingga perbedaan kecepatan 1%, dengan signifikansi statistik yang sangat sedikit.


Saya akan mengulangi Kibbee, dan mengatakan bahwa ini mungkin optimasi prematur.


7

Dalam python 2.6 (float).__pow__() fungsi menggunakan pow()fungsi C dan math.sqrt()fungsi menggunakan Csqrt() fungsi

Dalam glibc compiler implementasinya pow(x,y)cukup kompleks dan dioptimalkan dengan baik untuk berbagai kasus luar biasa. Misalnya, memanggil C pow(x,0.5)cukup memanggil sqrt()fungsi.

Perbedaan kecepatan penggunaan .**atau math.sqrtdisebabkan oleh pembungkus yang digunakan di sekitar fungsi C dan kecepatan sangat tergantung pada flag optimasi / kompiler C yang digunakan pada sistem.

Edit:

Ini adalah hasil algoritma Claudiu di komputer saya. Saya mendapat hasil berbeda:

zoltan@host:~$ python2.4 p.py 
Took 0.173994 seconds
Took 0.158991 seconds
zoltan@host:~$ python2.5 p.py 
Took 0.182321 seconds
Took 0.155394 seconds
zoltan@host:~$ python2.6 p.py 
Took 0.166766 seconds
Took 0.097018 seconds

4

Untuk apa nilainya (lihat jawaban Jim). Di mesin saya, menjalankan python 2.5:

PS C:\> python -m timeit -n 100000 10000**.5
100000 loops, best of 3: 0.0543 usec per loop
PS C:\> python -m timeit -n 100000 -s "import math" math.sqrt(10000)
100000 loops, best of 3: 0.162 usec per loop
PS C:\> python -m timeit -n 100000 -s "from math import sqrt" sqrt(10000)
100000 loops, best of 3: 0.0541 usec per loop

4

menggunakan kode Claudiu, di komputer saya bahkan dengan "dari impor matematika sqrt" x **. 5 lebih cepat tetapi menggunakan psyco.full () sqrt (x) menjadi jauh lebih cepat, setidaknya 200%


3

Kemungkinan besar math.sqrt (x), karena dioptimalkan untuk rooting persegi.

Tingkatan yang dicapai akan memberi Anda jawaban yang Anda cari.


3

Seseorang berkomentar tentang "akar kuadrat cepat Newton-Raphson" dari Quake 3 ... Saya menerapkannya dengan ctypes, tetapi sangat lambat dibandingkan dengan versi asli. Saya akan mencoba beberapa optimasi dan implementasi alternatif.

from ctypes import c_float, c_long, byref, POINTER, cast

def sqrt(num):
 xhalf = 0.5*num
 x = c_float(num)
 i = cast(byref(x), POINTER(c_long)).contents.value
 i = c_long(0x5f375a86 - (i>>1))
 x = cast(byref(i), POINTER(c_float)).contents.value

 x = x*(1.5-xhalf*x*x)
 x = x*(1.5-xhalf*x*x)
 return x * num

Berikut metode lain menggunakan struct, keluar sekitar 3.6x lebih cepat dari versi ctypes, tetapi masih 1/10 kecepatan C.

from struct import pack, unpack

def sqrt_struct(num):
 xhalf = 0.5*num
 i = unpack('L', pack('f', 28.0))[0]
 i = 0x5f375a86 - (i>>1)
 x = unpack('f', pack('L', i))[0]

 x = x*(1.5-xhalf*x*x)
 x = x*(1.5-xhalf*x*x)
 return x * num

1

Hasil Claudiu berbeda dari saya. Saya menggunakan Python 2.6 di Ubuntu pada mesin P4 2.4Ghz lama ... Inilah hasil saya:

>>> timeit1()
Took 0.564911 seconds
>>> timeit2()
Took 0.403087 seconds
>>> timeit1()
Took 0.604713 seconds
>>> timeit2()
Took 0.387749 seconds
>>> timeit1()
Took 0.587829 seconds
>>> timeit2()
Took 0.379381 seconds

sqrt secara konsisten lebih cepat bagi saya ... Bahkan Codepad.org SEKARANG tampaknya setuju bahwa sqrt, dalam konteks lokal, lebih cepat ( http://codepad.org/6trzcM3j ). Codepad tampaknya sedang menjalankan Python 2.5 saat ini. Mungkin mereka menggunakan 2.4 atau lebih tua ketika Claudiu pertama kali menjawab?

Bahkan, bahkan menggunakan math.sqrt (i) sebagai pengganti arg (i), saya masih mendapatkan waktu yang lebih baik untuk sqrt. Dalam hal ini timeit2 () mengambil antara 0,53 dan 0,55 detik pada mesin saya, yang masih lebih baik daripada angka 0,56-0,60 dari timeit1.

Saya akan mengatakan, pada Python modern, gunakan math.sqrt dan jelas membawanya ke konteks lokal, baik dengan somevar = math.sqrt atau dengan dari impor matematika sqrt.


1

Hal Pythonic untuk dioptimalkan adalah keterbacaan. Untuk ini saya pikir penggunaan eksplisit dari sqrtfungsi adalah yang terbaik. Karena itu, mari kita selidiki kinerja.

Saya memperbarui kode Claudiu untuk Python 3 dan juga membuatnya tidak mungkin untuk mengoptimalkan perhitungan (sesuatu yang dapat dilakukan oleh kompiler Python yang baik di masa depan):

from sys import version
from time import time
from math import sqrt, pi, e

print(version)

N = 1_000_000

def timeit1():
  z = N * e
  s = time()
  for n in range(N):
    z += (n * pi) ** .5 - z ** .5
  print (f"Took {(time() - s):.4f} seconds to calculate {z}")

def timeit2():
  z = N * e
  s = time()
  for n in range(N):
    z += sqrt(n * pi) - sqrt(z)
  print (f"Took {(time() - s):.4f} seconds to calculate {z}")

def timeit3(arg=sqrt):
  z = N * e
  s = time()
  for n in range(N):
    z += arg(n * pi) - arg(z)
  print (f"Took {(time() - s):.4f} seconds to calculate {z}")

timeit1()
timeit2()
timeit3()

Hasil bervariasi, tetapi output sampel adalah:

3.6.6 (default, Jul 19 2018, 14:25:17) 
[GCC 8.1.1 20180712 (Red Hat 8.1.1-5)]
Took 0.3747 seconds to calculate 3130485.5713865166
Took 0.2899 seconds to calculate 3130485.5713865166
Took 0.2635 seconds to calculate 3130485.5713865166

Cobalah sendiri.


0

Masalah SQRMINSUM yang telah saya pecahkan baru-baru ini membutuhkan komputasi akar kuadrat berulang kali pada dataset besar. 2 kiriman tertua dalam sejarah saya , sebelum saya membuat pengoptimalan lainnya, berbeda hanya dengan mengganti ** 0,5 dengan sqrt (), sehingga mengurangi runtime dari 3,74s menjadi 0,51s di PyPy. Ini hampir dua kali lipat peningkatan 400% yang sudah besar yang diukur Claudiu.


0

Tentu saja, jika seseorang berhadapan dengan literal dan membutuhkan nilai konstan, runtime Python dapat pra-menghitung nilai pada waktu kompilasi, jika ditulis dengan operator - tidak perlu membuat profil setiap versi dalam kasus ini:

In [77]: dis.dis(a)                                                                                                                       
  2           0 LOAD_CONST               1 (1.4142135623730951)
              2 RETURN_VALUE

In [78]: def a(): 
    ...:     return 2 ** 0.5 
    ...:                                                                                                                                  

In [79]: import dis                                                                                                                       

In [80]: dis.dis(a)                                                                                                                       
  2           0 LOAD_CONST               1 (1.4142135623730951)
              2 RETURN_VALUE

-3

Apa yang akan lebih cepat adalah jika Anda masuk ke math.py dan menyalin fungsi "sqrt" ke program Anda. Program Anda membutuhkan waktu untuk menemukan math.py, lalu membukanya, menemukan fungsi yang Anda cari, dan kemudian mengembalikannya ke program Anda. Jika fungsi itu lebih cepat bahkan dengan langkah-langkah "pencarian", maka fungsi itu sendiri harus sangat cepat. Mungkin akan memotong waktu Anda menjadi dua. Singkatnya:

  1. Pergi ke math.py
  2. Temukan fungsi "sqrt"
  3. Salin itu
  4. Tempel fungsi ke dalam program Anda sebagai pencari sqrt.
  5. Waktu itu.

1
Itu tidak akan berhasil; lihat stackoverflow.com/q/18857355/3004881 . Perhatikan juga kutipan dalam pertanyaan asli yang mengatakan itu adalah tautan ke fungsi C. Juga, bagaimana cara menyalin kode sumber fungsi berbeda from math import sqrt?
Dan Getz

Tidak akan, saya mengatakan bahwa hanya untuk memperjelas apa perbedaan dalam memanggil dua fungsi.
PyGuy
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.