Neden bazı yüzer < karşılaştırmalar diğerlerine göre dört kat daha yavaş tamsayı vardır?
Tamsayılar için yüzer karşılaştırırken, bazı değerler çift değerlendirilecek çok daha uzun bir benzer büyüklükteki diğer değerleri daha al.
Örneğin:
>>> import timeit
>>> timeit.timeit("562949953420000.7 < 562949953421000") # run 1 million times
0.5387085462592742
Ama eğer şamandıra veya tamsayı daha küçük ya da daha büyük bir miktar olarak yapılıyorsa, karşılaştırma çok daha hızlı bir şekilde çalışır:
>>> timeit.timeit("562949953420000.7 < 562949953422000") # integer increased by 1000
0.1481498428446173
>>> timeit.timeit("562949953423001.8 < 562949953421000") # float increased by 3001.1
0.1459577925548956
Karşılaştırma operatörü (örneğin yerine ==
>
kullanarak) deÄŸiÅŸen belirgin bir ÅŸekilde kat etkilemez.
Bu değilsadecedaha büyük ya da daha küçük değerler alıyorum çünkü büyüklüğü ile ilgili bazı talihsiz şekilde olduğunu sanıyorum çok daha hızlı karşılaştırmalar neden olabilir bit hattı.
Açıkça, bu değerler karşılaştırıldığında hızlı daha yeterli çoğu durumlarda kullanmak için. Sadece Python diğerleri ile daha değerleri bazı çiftleri daha fazla mücadele gibi görünüyor neden olarak merak ediyorum.
CEVAP
Yüzen nesneler için Python kaynak kod içinde bir yorum olduğunu kabul eder:
Bu, tersine yüzer, Python tamsayılar keyfi büyük olabilir, çünkü bir tamsayı için bir kayan nokta karşılaştırma, doğrudur ve her zaman kesin değildir. Bir yüzer tamsayı döküm çalışırken hassas kaybetmek ve karşılaştırma yapmak yanlış olabilir. Bir tamsayı için kaydır atmak için çalışıyor herhangi bir kesirli kısmı kaybolacak, çünkü her iki işe yaramayacak.
Bu sorunu geçici olarak almak için, Python kontrolleri bir dizi, eğer denetimlerden biri başarılı olursa sonucu dönen gerçekleştirir. Tamsayı çok büyük "" bir kayan nokta, tamsayı uzunluğu şamandıra üs karşılaştırır. de olsa iki değer belirtileri karşılaştırır Eğer tüm bu kontrolleri başarısız olursa, iki yeni Python nesneleri sonuç elde etmek için karşılaştırmak oluşturmak için gereklidir.
Bir şamandıra karşılaştırırken bir tamsayı için v
uzun w
, en kötü durum o/:
v
w
aynı işaretli (pozitif ya da negatif her ikisi de)- tamsayı
w
size_t
türü (genellikle 32 veya 64 bit) düzenlenen, olabilir yeterli birkaç bit vardır - tamsayı
w
en az 49 bit vardır - şamandıra üs
v
w
bit sayısı aynıdır.
Ve bu soru değerleri için elimizde tam olarak ne olduğunu:
>>> import math
>>> math.frexp(562949953420000.7) # gives the float's (significand, exponent) pair
(0.9999999999976706, 49)
>>> (562949953421000).bit_length()
49
49 şamandıra üs ve tamsayı bit sayısını iki olduğunu görüyoruz. Hem sayılar pozitif ve dört kriterlerinin üzerinde bir araya geldi.
Seçimi değerleri için daha büyük (veya daha küçük) değiştirebilir sayıda bit tamsayı veya üs değeri, ve Python olduğunu tespit etme sonucu karşılaştırma olmadan sahne pahalı son kontrol.
Bu dil CPython uygulaması için geçerlidir.
Daha detaylı karşılaştırma
float_richcompare
bu fonksiyon iki deÄŸer v
w
arasında karşılaştırma işler.
Aşağıda işlevi gerçekleştiren kontroller adım adım açıklama. Python kaynağı açıklamalarda işlevi ne yaptığını anlamaya çalışırken aslında çok yardımsever, gerektiğinde onları bıraktım. Ayrıca cevap dibinde bir listede bu kontroller özetlenmiştir ettim.
Ana fikir için harita Python nesneleri v
w
iki uygun C çiftler i
j
, o zaman kolayca karşılaştırıldığında vermek doğru sonuç. Python 2 ve Python 3 de bunu yapmak için aynı düşünceleri (eski sadece ayrı ayrı int
long
halleden) kullanın.
Yapmanız gereken ilk şey, v
kesinlikle Python bir şamandıra ve bir C i
çift için harita kontrol edin. Fonksiyonu w
da şamandıra ve haritalar olup olmadığını inceliyor sonraki bir C çift 31**. Bu tüm diğer kontroller atlanması olarak işlev için en iyi durum senaryosu. İşlevi de v
inf
nan
olup olmadığını görmek için denetler:
static PyObject*
float_richcompare(PyObject *v, PyObject *w, int op)
{
double i, j;
int r = 0;
assert(PyFloat_Check(v));
i = PyFloat_AS_DOUBLE(v);
if (PyFloat_Check(w))
j = PyFloat_AS_DOUBLE(w);
else if (!Py_IS_FINITE(i)) {
if (PyLong_Check(w))
j = 0.0;
else
goto Unimplemented;
}
Åžimdi ise w
Bu kontroller başarısız olursa, bir Python şamandıra olmadığını biliyoruz. Eğer Python bir tam sayı ise fonksiyon kontrolleri artık. Bu durumda, en kolay test etmek özü işareti v
ve iÅŸareti w
(*dön 39* sıfır, -1
negatif, 1
eğer pozitif). Eğer işaretler farklı ise, bu tüm bilgileri karşılaştırma sonucunu döndürmek için gerekli:
else if (PyLong_Check(w)) {
int vsign = i == 0.0 ? 0 : i < 0.0 ? -1 : 1;
int wsign = _PyLong_Sign(w);
size_t nbits;
int exponent;
if (vsign != wsign) {
/* Magnitudes are irrelevant -- the signs alone
* determine the outcome.
*/
i = (double)vsign;
j = (double)wsign;
goto Compare;
}
}
Eğer bu onay başarısız olursa, o zaman v
w
aynı işaret var.
Bir sonraki onay tamsayı w
bit sayısını sayar. Eğer çok fazla parçası var, eğer o zaman muhtemelen bir şamandıra olarak kabul edilemez ve bu yüzden yüzer büyüklüğü daha büyük olmalıdır v
:
nbits = _PyLong_NumBits(w);
if (nbits == (size_t)-1 && PyErr_Occurred()) {
/* This long is so large that size_t isn't big enough
* to hold the # of bits. Replace with little doubles
* that give the same outcome -- w is so large that
* its magnitude must exceed the magnitude of any
* finite float.
*/
PyErr_Clear();
i = (double)vsign;
assert(wsign != 0);
j = wsign * 2.0;
goto Compare;
}
Eğer tamsayı w
48 veya daha az bit varsa, diğer taraftan, güvenli bir C ihbar çift j
ve göre:
if (nbits <= 48) {
j = PyLong_AsDouble(w);
/* It's impossible that <= 48 bits overflowed. */
assert(j != -1.0 || ! PyErr_Occurred());
goto Compare;
}
Bu noktadan itibaren, w
49 veya daha fazla bit olduğunu biliyoruz. Pozitif bir tamsayı w
tedavi için uygun olacaktır, işaret ve karşılaştırma gerekli operatör değiştirme:
if (nbits <= 48) {
/* "Multiply both sides" by -1; this also swaps the
* comparator.
*/
i = -i;
op = _Py_SwappedOp[op];
}
Şimdi işlevi şamandıra üs bakar. Bir mala * 2 significand olarak yazılı bir delil yok sayarak) olabilir hatırlayınüsve significand 0.5 ve 1 arasında bir sayı gösterir:
(void) frexp(i, &exponent);
if (exponent < 0 || (size_t)exponent < nbits) {
i = 1.0;
j = 2.0;
goto Compare;
}
Bu iki şeyi kontrol eder. Eğer üs değeri 0'dan küçük ise yüzer (ve herhangi bir tamsayı daha büyüklüğü içinde çok küçük) 1'den küçüktür. Ya da, eğer üs w
bit sayısı az ise o zaman v < |w|
beri * 2 significand varüs2'den daha aznbits.
Bu iki kontrolleri yapmadıkları, işlevi üs w
bit sayısından daha büyük olup olmadığına bakıyor. Bu * 2 significand gösteriyorüs2'den büyüknbitsve böylece v > |w|
:
if ((size_t)exponent > nbits) {
i = 2.0;
j = 1.0;
goto Compare;
}
Eğer bu denetimi başarılı mı yoksa şamandıra üs v
tamsayı w
bit sayısı ile aynı olduğunu biliyoruz.
İki değer mukayese edilebilir tek yolu artık v
w
iki yeni Python tamsayıları inşa edecek. Fikri v
çift tamsayı kısmı, kesirli kısmını atmak ve bir ekleyin. w
da iki katına çıkarılır ve bu iki yeni Python nesneleri doğru dönüş değeri vermek için karşılaştırılabilir. Küçük değerler, 4.65 < 4
örneği ile karşılaştırma (2*4) 1 == 9 < 8 == (2*4)
(false dönen) tarafından belirlenecektir.
{
double fracpart;
double intpart;
PyObject *result = NULL;
PyObject *one = NULL;
PyObject *vv = NULL;
PyObject *ww = w;
// snip
fracpart = modf(i, &intpart); // split i (the double that v mapped to)
vv = PyLong_FromDouble(intpart);
// snip
if (fracpart != 0.0) {
/* Shift left, and or a 1 bit into vv
* to represent the lost fraction.
*/
PyObject *temp;
one = PyLong_FromLong(1);
temp = PyNumber_Lshift(ww, one); // left-shift doubles an integer
ww = temp;
temp = PyNumber_Lshift(vv, one);
vv = temp;
temp = PyNumber_Or(vv, one); // a doubled integer is even, so this adds 1
vv = temp;
}
// snip
}
}
Kısalık için ek hata denetimi bırakmadım ve Çöp izleme Python bu yeni nesneler oluşturur. Söylemeye gerek yok, bu ek yük ekler ve değerleri soruda vurgulanan diğerlerine göre karşılaştırmak için önemli ölçüde daha yavaş olduğunu açıklıyor.
Burada karşılaştırma işlevi tarafından gerçekleştirilen denetimler bir özet.
v
bir şamandıra olalım ve C çift olarak koyun. Eğer w
da bir kayan nokta ise:
w
nan
inf
olup olmadığını kontrol edin. Eğer öyleyse, bu özel durumda ayrı ayrıw
türüne bağlı olarak işlemek.Değilse, doğrudan C çiftler gibi ifadeleri tarafından
v
w
karşılaştırın.
EÄŸer w
bir tamsayı ise:
v
w
belirtileri ayıklayın. Eğer farklıysa o zamanv
w
büyük değeri olan ve farklı olduğunu biliyoruz.(Belirtileri aynı.)
w
çok fazla bir kaydır (daha fazlasize_t
) bit olup olmadığını kontrol edin. , 84 **v
daha büyük bir büyüklük vardır.w
48 veya daha az bit olup olmadığını kontrol edin. Eğer öyleyse, güvenli bir şekildev
ile karşılaştırıldığında hassasiyet kaybetmeden bir C çift ve döküm olabilir.(
w
48 daha fazla bit vardır. Şimdi op uygun olarak karşılaştırmak pozitif bir tamsayı değiştirmiş olması gibiw
davranacağız.)Şamandıra üs
v
düşünün. Eğer üs negatif ise, o zamanv
az daha1
ve herhangi bir pozitif tamsayı daha bu nedenle daha az. Eğer üsw
bit sayısı az ise başka, o zamanw
daha az olmalı.Eğer
v
üsw
bit sayısı ne kadar büyükse ov
w
daha büyük.(Üs
w
bit sayısı aynıdır.)Son kontrol edin. Tam sayı ve kesirli kısımları içine
v
bölünmüş. Tamsayı kısmı çift ve 1 kesirli kısmını telafi etmek için ekleyin. Şimdi çift tamsayıw
. Bu iki yeni tamsayılar sonuç almak yerine karşılaştırın.
Neden bir döngü iki döngü daha yavaş g...
Nasıl ve/veya neden Git daha iyi SVN g...
Neden sıralanmış bir dizi sıralanmamış...
Neden 512x512 matrix 513x513 bir matri...
Neden küçük bir liste daha küçük bir d...