줄이 왜 이렇게 느린가요?


23

고등학교에서 처음으로 수업을 시작한 이래로 나는 문자열 연산이 신화적인 "평균 연산"보다 느리다는 것, 즉 더 많은 비용이 들었다고 들었습니다. 왜 그렇게 느리게합니까? (이 질문은 의도적으로 광범위하게 남았습니다.)


11
이러한 "평균 운영"이 신화 적이라는 것을 알고 있다면 적어도 그 중 일부가 무엇인지 말씀해 주시겠습니까? 이러한 모호한 질문을하고 있다는 점을 감안할 때 이러한 지정되지 않은 작업이 실제로 신화 적이라는 주장을 신뢰하기는 어렵습니다.
seh

1
@seh, 불행히도 실제로는 대답 할 수 없습니다. 몇 번이나 실제로 사람들에게 어떤 줄이 더 느린 지 물었습니다. 게다가 더 구체적인 정보가 있다면 이것은 프로그래머가 아닌 SO에 대한 질문 일 것입니다. 이미 약간 경계선입니다.
Pop

요점이 뭐야 ? 말한 줄이 실제로 느린 경우 사용을 중단합니까?
Tulains Córdova

잊어 버려. 누군가가 당신에게 그런 말도 안된다면, 반대 질문은 "정말입니까? 그들입니까? 그렇다면 우리는 int-array를 사용해야합니까?"입니다.
Ingo

답변:


47

"평균 작업"은 프리미티브에서 발생합니다. 그러나 문자열이 프리미티브로 처리되는 언어에서도 문자열은 여전히 ​​배열 아래에 있으며 전체 문자열과 관련된 모든 작업을 수행하려면 O (N) 시간이 걸립니다. 여기서 N은 문자열의 길이입니다.

예를 들어, 두 개의 숫자를 추가하려면 일반적으로 2-4 ASM 명령이 필요합니다. 두 문자열을 연결 ( "추가")하려면 새로운 메모리 할당과 전체 문자열을 포함하는 하나 또는 두 개의 문자열 복사본이 필요합니다.

특정 언어 요소로 인해 악화 될 수 있습니다. 예를 들어 C에서 문자열은 단순히 널로 끝나는 문자 배열에 대한 포인터입니다. 즉, 길이를 알 수 없으므로 빠른 이동 작업으로 문자열 복사 루프를 최적화 할 수있는 방법이 없습니다. 한 번에 한 문자 씩 복사해야 널 터미네이터의 각 바이트를 테스트 할 수 있습니다.


4
배열의 시작 부분에있는 문자열 길이에 대한 델파이의 인코딩은 문자열 연결을 매우 빠르게 만듭니다.
Frank Shearar

4
@gablin : 문자열 복사 자체를 훨씬 빠르게 만들어줍니다. 크기를 미리 알면 한 번에 한 바이트를 복사하고 각 바이트에서 널 종료자를 검사 할 필요가 없으므로 SIMD를 포함한 모든 레지스터의 전체 크기를 사용하여 데이터 이동, 작성 최대 16 배 더 빠릅니다.
메이슨 휠러

4
@mathepic : 그래, 당신은 libc의 또는 다른 외부 코드와 상호 작용하기 시작할 때까지로 당신을 동원하지만 같은 대한의 벌금, 그것은 기대하는 char*아니라 strbuf, 당신은 평방 1. 수있는 좋은 방법입니다 다시 너무 만있다하면 나쁜 디자인이 언어에 구워지면 할 수 있습니다.
메이슨 휠러

6
@mathepic : 물론 buf포인터가 있습니다. 나는 그것이 가능하지 않다는 것을 암시하지는 않았다. 오히려 필요하다. 표준 라이브러리 와 같은 기본 사항을 포함하여 최적화되었지만 비표준 문자열 유형에 대해 모르는 코드는 여전히 느리고 안전하지 않습니다 char*. 원하는 경우 해당 FUD를 호출 할 수는 있지만 사실이 아닙니다.
Mason Wheeler

7
Frank Shearer의 요점에 대한 Joel Spolsky 칼럼이 있습니다. Back to Basics
user16764

14

이것은 오래된 스레드이며 다른 답변은 훌륭하지만 무언가를 간과한다고 생각하므로 여기에 2 센트가 있습니다.

구문 설탕 코팅은 복잡성을 숨 깁니다

문자열의 문제점은 대부분의 언어에서 2 등 시민이며 실제로 대부분의 경우 실제로 언어 사양 자체의 일부가 아니라는 점입니다. 즉, 때때로 문법적으로 설탕이 코팅 된 라이브러리 구현 구문입니다. 고통을 덜어주기 위해

이것의 직접적인 결과는 언어가 복잡한 것의 많은 부분을 시야에서 멀리 숨기고, 저수준의 원자 실체처럼 생각하는 습관으로 자라기 때문에 부적절한 부작용에 대한 비용을 지불한다는 것입니다. 다른 기본 유형 (최고의 답변 및 기타에 의해 설명 됨).

구현 세부 사항

좋은 Ol '배열

이 기본 "복잡성"의 요소 중 하나는 대부분의 문자열 구현이 문자열을 나타내는 인접한 메모리 공간이있는 간단한 데이터 구조를 사용한다는 것입니다.

문자열 전체에 대한 액세스가 빠르기를 원하기 때문에이 점이 좋습니다. 그러나 이것은이 문자열을 조작 할 때 잠재적으로 끔찍한 비용을 의미합니다. 어떤 인덱스를 사용하고 있는지 알면 중간에 요소에 액세스하는 것이 빠를 수 있지만 조건에 따라 요소를 찾는 것은 그렇지 않습니다.

언어가 문자열 길이를 캐시하지 않고 문자 수를 계산하기 위해 문자열을 실행 해야하는 경우 문자열 크기를 반환하더라도 비용이 많이들 수 있습니다.

비슷한 이유로 문자열에 요소를 추가 하면이 작업을 수행하기 위해 메모리를 다시 할당해야 할 가능성이 높으므로 비용이 많이 듭니다.

따라서 언어마다 다른 문제가 있습니다. 예를 들어, Java는 유효한 이유 (캐싱 길이, 스레드 안전성)로 인해 문자열을 변경할 수없는 자유를 가져 왔고 변경 가능한 대응 물 (StringBuffer 및 StringBuilder)은 더 큰 크기의 청크를 사용하여 크기를 할당 할 필요가 없습니다. 항상 최선의 시나리오를 희망합니다. 일반적으로 잘 작동하지만 단점은 때로는 메모리 영향을 지불하는 것입니다.

유니 코드 지원

또한, 이것은 언어의 구문 설탕 코팅이 이것을 즐기기 위해 숨기고 있기 때문에 종종 유니 코드 지원이라는 용어를 생각하지 않습니다 (특히 실제로 필요하지 않는 한) 벽에 부딪쳤다). 그리고 미래를 생각하는 일부 언어는 간단한 8 비트 문자 프리미티브의 기본 배열로 문자열을 구현하지 않습니다. 그것들은 UTF-8 또는 UTF-16으로 구워 졌거나 당신을 위해 무엇을 지원 했습니까? 결과적으로 엄청나게 많은 메모리 소비가 종종 필요하지 않으며 메모리를 할당하고 문자열을 처리하는 데 더 많은 처리 시간이 필요합니다. 코드 포인트 조작과 함께 모든 논리를 구현합니다.


이 모든 결과는 의사 코드에서 동등한 작업을 수행 할 때 다음과 같습니다.

hello = "hello,"
world = " world!"
str = hello + world

언어 개발자가 최선의 노력을 기울 였음에도 불구하고 다음과 같은 간단한 동작을 제외하고는 그렇지 않을 수도 있습니다.

a = 1;
b = 2;
shouldBeThree = a + b

후속 조치로 다음을 읽을 수 있습니다.


현재 토론에 추가되었습니다.
Abel

신화적인 진술이 RSA 암호화와 같은 것에 적용될 수 있기 때문에 이것이 최선의 대답이라는 것을 깨달았습니다. 이 당혹스러운 장소에 문자열을 넣는 유일한 이유는 대부분의 언어에서 문자열에 대해 더하기 연산자를 제공했기 때문에 초보자가 작업 비용을 인식하지 못하기 때문입니다.
Codism

@ 아벨 : 감사합니다.보다 일반적인 세부 사항을위한 공간이었습니다.
haylem

@Codism : 감사합니다. 마음에 들었습니다. 실제로 이것은 복잡성이 숨겨져있는 많은 경우에 적용될 수 있다고 생각합니다 (그리고 우리는 어떤 종류의 병목이나 벽돌 벽에 부딪쳐서 마침내 필요할 때까지 더 이상 하위 수준의 세부 사항에주의를 기울이지 않습니다) ).
haylem

1

"평균 연산"이라는 문구는 이론적 랜덤 액세스 저장 프로그램 머신 의 단일 조작을위한 축약 형일 것 입니다 . 이것은 다양한 알고리즘의 실행 시간을 분석하는 데 일반적으로 사용되는 이론적 인 시스템입니다.

일반 작업은 일반적으로로드, 더하기, 빼기, 저장, 분기로 이루어집니다. 아마도 읽고 인쇄하고 멈출 수도 있습니다.

그러나 대부분의 문자열 연산에는 이러한 몇 가지 기본 연산이 필요합니다. 예를 들어, 문자열을 복제하려면 일반적으로 복사 작업이 필요하므로 문자열 길이에 비례하는 많은 작업 (즉, "선형")이 필요합니다. 다른 문자열에서 하위 문자열을 찾는 것도 선형 복잡성입니다.


1

작업, 문자열 표현 방법 및 존재하는 최적화에 따라 달라집니다. 문자열의 길이가 4 또는 8 바이트이고 정렬되는 경우 반드시 느릴 필요는 없습니다. 많은 연산이 프리미티브만큼 빠릅니다. 또는 모든 문자열에 32 비트 또는 64 비트 해시가있는 경우 많은 작업이 빠릅니다 (해시 비용을 선불로 지불하더라도).

또한 "느리게"의 의미에 따라 다릅니다. 대부분의 프로그램은 필요한만큼 문자열을 빠르게 처리합니다. 문자열 비교는 두 정수를 비교하는 것만 큼 빠르지는 않지만 프로파일 링만으로 "느린"것이 프로그램에 어떤 의미인지 알 수 있습니다.


0

질문으로 질문에 대답하겠습니다. 단어 한 줄을 말하는 것이 한 단어를 말하는 것보다 더 오래 걸리는 이유는 무엇입니까?


2
반드시 그런 것은 아닙니다.
user16764

3
Supercalifragilisticexpialidocious
Spoike

s / word / syllable / g
Caleb

질문으로 귀하의 질문에 대한 답변을 드리겠습니다 : 답변의 의미가 무엇인지 말하지 않겠습니까? 결국 일부 런타임 시스템에 적용되는 것으로 어떻게 해석 될 수 있는지는 분명하지 않습니다.
PJTraill
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.