Neden string'daha yavaş daha startswith s?
Şaşırtıcı olan, startswith
in
daha yavaş olduğunu düşünüyorum:
In [10]: s="ABCD"*10
In [11]: %timeit s.startswith("XYZ")
1000000 loops, best of 3: 307 ns per loop
In [12]: %timeit "XYZ" in s
10000000 loops, best of 3: 81.7 ns per loop
Hepimizin bildiği gibi, in
işlemi tüm dizeyi aramak için ihtiyaçları ve startswith
sadece ilk birkaç karakteri kontrol gerekir startswith
daha etkili olur.
s
yeterince büyük olduğunda, startswith
daha hızlı
In [13]: s="ABCD"*200
In [14]: %timeit s.startswith("XYZ")
1000000 loops, best of 3: 306 ns per loop
In [15]: %timeit "XYZ" in s
1000000 loops, best of 3: 666 ns per loop
startswith
arama dize küçük olduğunda daha yavaş olur biraz masrafı var gibi görünüyor.
Ve startswith
çağrı yükü ne olduğunu anlamaya çalıştım.
İlk olarak, bu answer burada startswith
hala yavaş olduğunu görüyoruz belirtildiği gibi f
değişken nokta operasyon maliyeti azaltmak için kullanılır:
In [16]: f=s.startswith
In [17]: %timeit f("XYZ")
1000000 loops, best of 3: 270 ns per loop
Ayrıca, boş bir fonksiyon çağrısı maliyeti test:
In [18]: def func(a): pass
In [19]: %timeit func("XYZ")
10000000 loops, best of 3: 106 ns per loop
Nokta operasyonu ve fonksiyonun maliyeti ne olursa olsun, startswith
zaman=164ns, ama in
Bu çalışma, sadece 81.7 ns alır (270-106). Hala ne startswith
için bazı giderleri var gibi görünüyor?
Test poke ve lvc önerdiği gibi startswith
__contains__
arasında neden ekleyin:
In [28]: %timeit s.startswith("XYZ")
1000000 loops, best of 3: 314 ns per loop
In [29]: %timeit s.__contains__("XYZ")
1000000 loops, best of 3: 192 ns per loop
CEVAP
Zaten belirtildiği açıklamalarda, eğer kullandığınız s.__contains__("XYZ")
olsun sonuç daha benzer s.startswith("XYZ")
çünkü ihtiyaçları aynı rota: Üye arama dize nesne, ardından bir işlev çağrısı. Bu genellikle biraz pahalı (tabi endişelenmen gereken bu yeterli değil). Diğer taraftan, ne zaman yap "XYZ" in s
çözümleyici yorumlar operatörü ve kısa kesilmiş üye __contains__
(ya da daha doğrusu uygulama arkasında, çünkü __contains__
kendisi sadece bir şekilde erişim uygulaması).
Bayt kodu bakarak bu konuda bir fikir edinebilirsiniz:
>>> dis.dis('"XYZ" in s')
1 0 LOAD_CONST 0 ('XYZ')
3 LOAD_NAME 0 (s)
6 COMPARE_OP 6 (in)
9 RETURN_VALUE
>>> dis.dis('s.__contains__("XYZ")')
1 0 LOAD_NAME 0 (s)
3 LOAD_ATTR 1 (__contains__)
6 LOAD_CONST 0 ('XYZ')
9 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
12 RETURN_VALUE
Yani s.startswith("XYZ")
34 *karşılaştırma daha benzer bir sonuç, ancak s
, startswith
hala daha yavaş olacaktır örnek dizesi için üretecek.
Onun için, hem de uygulanması kontrol edebilir. Statik olarak yazılan ve bağımsız bir unicode olduğunu varsayar contains implementation görmek ilginç nesne kendisini. Bu oldukça verimlidir.
startswith
implementation ancak bu bir “dinamik” uygulama aslında argümanlar ayrıştırma gerektiren Python yöntemi. startswith
tüm devreye alma yöntemi biraz daha yavaş yapar, bağımsız değişken olarak bir demet destekler: (bana göre kısaltılmış, yorumlarım ile):
static PyObject * unicode_startswith(PyObject *self, PyObject *args)
{
// argument parsing
PyObject *subobj;
PyObject *substring;
Py_ssize_t start = 0;
Py_ssize_t end = PY_SSIZE_T_MAX;
int result;
if (!stringlib_parse_args_finds("startswith", args, &subobj, &start, &end))
return NULL;
// tuple handling
if (PyTuple_Check(subobj)) {}
// unicode conversion
substring = PyUnicode_FromObject(subobj);
if (substring == NULL) {}
// actual implementation
result = tailmatch(self, substring, start, end, -1);
Py_DECREF(substring);
if (result == -1)
return NULL;
return PyBool_FromLong(result);
}
Bu büyük olasılıkla startswith
contains
bir hızlı, basit olduğu dizeleri yavaş neden büyük bir nedenidir.
Neden bir döngü iki döngü daha yavaş g...
Neden bazı yüzer < karşılaştırmalar...
Neden sıralanmış bir dizi sıralanmamış...
Neden 512x512 matrix 513x513 bir matri...
Neden küçük bir liste daha küçük bir d...