문자열의 시작이 in보다 느린 이유는 무엇입니까?
놀랍게도 다음 startswith
보다 느립니다 in
.
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
우리 모두 알다시피 in
작업은 전체 문자열을 검색 startswith
하고 처음 몇 글자 만 확인하면되므로 startswith
더 효율적이어야합니다.
s
충분히 클 때 startswith
더 빠릅니다.
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
에는 약간의 오버 헤드가있어 문자열이 작을 때 속도가 느려지는 것 같습니다 .
그리고 startswith
전화 의 오버 헤드가 무엇인지 알아 내려고 노력한 것보다 .
첫째, f
이 답변 에서 언급했듯이 점 연산의 비용을 줄이기 위해 변수를 사용했습니다. 여기서 우리 startswith
는 여전히 더 느린 것을 알 수 있습니다 .
In [16]: f=s.startswith
In [17]: %timeit f("XYZ")
1000000 loops, best of 3: 270 ns per loop
또한 빈 함수 호출 비용을 테스트했습니다.
In [18]: def func(a): pass
In [19]: %timeit func("XYZ")
10000000 loops, best of 3: 106 ns per loop
도트 연산 및 함수 호출 비용에 관계없이 시간 startswith
은 약 (270-106) = 164ns이지만 in
작업에는 81.7ns 만 걸립니다. 에 대한 오버 헤드가 여전히있는 것 같습니다. 그게 뭔데요 startswith
?
poke 및 lvc에서 제안한대로 startswith
및 사이에 테스트 결과를 추가합니다 __contains__
.
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
주석에서 이미 언급했듯이 사용 s.__contains__("XYZ")
하면 s.startswith("XYZ")
동일한 경로를 취해야 하기 때문에 보다 유사한 결과를 얻을 수 있습니다 . 문자열 객체에 대한 멤버 조회, 함수 호출이 이어집니다. 이것은 일반적으로 다소 비쌉니다 (물론 걱정할만큼 충분하지 않습니다). 반면에를 수행 "XYZ" in s
하면 파서가 연산자를 해석하고에 대한 멤버 액세스를 단축 할 수 있습니다 __contains__
(또는 __contains__
그 자체가 구현에 액세스하는 한 가지 방법 이기 때문에 그 뒤에 있는 구현).
바이트 코드를 살펴보면 이에 대한 아이디어를 얻을 수 있습니다.
>>> 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
따라서 s.__contains__("XYZ")
와 비교 s.startswith("XYZ")
하면 더 유사한 결과가 생성되지만 예제 string s
의 startswith
경우 여전히 느려집니다.
To get to that, you could check the implementation of both. Interesting to see for the contains implementation is that it is statically typed, and just assumes that the argument is a unicode object itself. So this is quite efficient.
The startswith
implementation however is a “dynamic” Python method which requires the implementation to actually parse the arguments. startswith
also supports a tuple as an argument, which makes the whole start-up of the method a bit slower: (shortened by me, with my comments):
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);
}
This is likely a big reason why startswith
is slower for strings for which a contains
is fast because of its simplicity.
This is likely because str.startswith()
does more than str.__contains__()
, and also because I believe str.__contains__
operates fully in C, whereas str.startswith()
has to interact with Python types. Its signature is str.startswith(prefix[, start[, end]])
, where prefix can be a tuple of strings to try.
ReferenceURL : https://stackoverflow.com/questions/31917372/why-is-strings-startswith-slower-than-in
'development' 카테고리의 다른 글
x86, arm, GCC 및 icc에서 작동하는 Linux에서 원자 연산을 수행하는 방법은 무엇입니까? (0) | 2021.01.08 |
---|---|
Google App Engine에서 스테이징 환경을 설정하는 방법 (0) | 2021.01.08 |
Objective-C의 속성 및 인스턴스 변수 (0) | 2021.01.08 |
MVVM을 사용하여 WPF에서 새 창을 만드는 가장 좋은 방법 (0) | 2021.01.08 |
Windows에서 Eclipse를 어떤 폴더에 설치해야합니까? (0) | 2021.01.08 |