가장 빠른 하위 문자열 검색 알고리즘은 무엇입니까?


165

좋아, 나는 바보처럼 들리지 않는다. 나는 문제 / 요구 사항을보다 명확하게 언급 할 것이다.

  • 바늘 (패턴)과 건초 더미 (검색 할 텍스트)는 모두 C 스타일의 널 종료 문자열입니다. 길이 정보가 제공되지 않습니다. 필요한 경우 계산해야합니다.
  • 함수는 첫 번째 일치 항목에 대한 포인터를 반환해야합니다 NULL.
  • 실패 사례는 허용되지 않습니다. 즉, 일정하지 않은 (또는 큰 상수) 스토리지 요구 사항이있는 알고리즘은 할당 실패에 대한 폴백 사례가 있어야하고 폴백 관리의 성능이 최악의 성능에 기여한다는 것을 의미합니다.
  • 코드가없는 알고리즘 (또는 그러한 링크에 대한 링크)에 대한 좋은 설명도 괜찮지 만 구현은 C로되어 있습니다.

... 그리고 "가장 빠름"의 의미는 다음과 같습니다.

  • 결정 론적 O(n)위치 n= 건초 더미 길이. (하지만 O(nm)결정 론적 O(n)결과 를 제공하기 위해보다 강력한 알고리즘과 결합 된 경우 일반적으로 롤링 해시와 같은 알고리즘의 아이디어를 사용할 수 있습니다 ).
  • if (!needle[1])순진한 무차별 대입 알고리즘보다, 특히 가장 일반적인 경우 일 수있는 매우 짧은 바늘에서 (실제로 몇 개의 시계 등은 괜찮습니다.) 나쁘게 수행하지 마십시오 . (무조건적인 무거운 전처리 오버 헤드는 불량한 바늘을 희생시키면서 병리학 적 바늘의 선형 계수를 개선하려고 시도함에 따라 나쁘다.)
  • 임의의 바늘과 건초 더미가 주어지면 다른 널리 구현되는 알고리즘과 비교할 수 있거나 더 나은 성능 (검색 시간이 50 % 이상 길지 않음)입니다.
  • 이러한 조건을 제외하고는 "가장 빠른"개방형 정의를 그대로 둡니다. 좋은 대답은 왜 당신이 "가장 빠른"제안하는 접근법을 고려하는지 설명해야합니다.

현재 구현은 glibc의 Two-Way 구현보다 약 10 % 느리고 8 배 빠릅니다 (입력에 따라 다름).

업데이트 : 현재 최적의 알고리즘은 다음과 같습니다.

  • 길이가 1 인 바늘에는을 사용하십시오 strchr.
  • 길이가 2-4 인 바늘의 경우 머신 워드를 사용하여 다음과 같이 한 번에 2-4 바이트를 비교하십시오. . 건초 더미의 모든 바이트는 정확히 한 번 읽히고 0 (문자열 끝)과 하나의 16 비트 또는 32 비트 비교에 대한 검사가 수행됩니다.
  • 길이가 4보다 큰 바늘의 경우 창의 마지막 바이트에만 적용되는 잘못된 이동 테이블 (Boyer-Moore와 같은)이있는 양방향 알고리즘을 사용하십시오. 많은 중간 길이의 바늘에 대한 순 손실 인 1kb 테이블을 초기화하는 오버 헤드를 피하기 위해 시프트 테이블의 항목이 초기화되는 비트 배열 (32 바이트)을 표시합니다. 설정되지 않은 비트는 바늘에 나타나지 않는 바이트 값에 해당하며 전체 바늘 길이 이동이 가능합니다.

내 마음에 남아있는 큰 질문은 다음과 같습니다.

  • 잘못된 시프트 테이블을 더 잘 활용할 수있는 방법이 있습니까? Boyer-Moore는 뒤로 (오른쪽에서 왼쪽으로) 스캔하여 최대한 활용할 수 있지만 양방향에서는 왼쪽에서 오른쪽으로 스캔해야합니다.
  • 일반적인 경우에 대해 찾은 두 가지 실행 가능한 후보 알고리즘 (메모리 부족 또는 2 차 성능 조건 없음)은 정렬 된 알파벳에서 양방향문자열 일치입니다 . 그러나 다른 알고리즘이 최적 인 경우를 쉽게 감지 할 수있는 경우가 있습니까? 공간 알고리즘에서 확실히 O(m)( m바늘 길이는) 많은 부분 이 사용될 수 있습니다 m<100. 선형 시간 만 필요로하는 바늘에 대한 쉬운 테스트가있는 경우 최악의 2 차 알고리즘을 사용할 수도 있습니다.

보너스 포인트 :

  • needle과 haystack이 잘 구성된 UTF-8이라고 가정하여 성능을 향상시킬 수 있습니까? (바이트 길이가 다양한 문자를 사용하면 올바른 형식으로 바늘과 건초 더미 사이에 일부 문자열 정렬 요구 사항이 적용되며 불일치하는 헤드 바이트가 발생하면 자동으로 2-4 바이트 이동이 가능합니다. 그러나 이러한 제약 조건으로 인해 최대 접미사 계산, 좋은 접미사 이동 등은 이미 다양한 알고리즘을 제공합니까?)

참고 : 실제로 알고리즘의 성능이 좋지는 않지만 대부분의 알고리즘을 잘 알고 있습니다. 사람들이 나에게 알고리즘에 대한 의견 / 답변으로 계속 언급하지 않도록 좋은 참고 자료가 있습니다. http://www-igm.univ-mlv.fr/~lecroq/string/index.html


에 나와있는 문자열 검색 알고리즘의 꽤가 있습니다 문자열에 알고리즘 . 이 목록에서 고려한 알고리즘을 설명 할 수 있습니다.
Greg Hewgill

61
결국 그 연결은 금입니다!
Carlos

4
당신이 여전히 대답을 받아들이지 않았다는 것을 믿을 수 없습니다.
user541686

1
@ Mehrdad : 질문에 실제로 질문에 대한 답변이 없지만 당신의 것 같습니다. 당신이 대답했을 때 나는 앞으로 나아가서 strstr무언가를 더 발전시켜 나갔습니다 . 그래서 실제로 당신이 연결된 논문을 제대로 읽지 못했지만 매우 유망한 것으로 들립니다. 답장을 보내지 않아서 죄송합니다.
R .. GitHub 중지 지원 얼음

답변:


37

가능한 바늘과 건초 더미로 구성된 테스트 라이브러리를 구축하십시오. 무차별 대입을 포함한 여러 검색 알고리즘에서 테스트를 프로파일 링합니다. 데이터에 가장 적합한 것을 선택하십시오.

Boyer-Moore 는 접미사 테이블이 좋은 잘못된 문자 테이블을 사용합니다.

Boyer-Moore-Horspool 은 잘못된 문자표를 사용합니다.

Knuth-Morris-Pratt 는 부분 일치 테이블을 사용합니다.

Rabin-Karp 는 실행중인 해시를 사용합니다.

그것들은 모두 다른 정도의 비교를 위해 오버 헤드를 교환하므로 실제 성능은 바늘과 건초 더미의 평균 길이에 달려 있습니다. 초기 오버 헤드가 많을수록 입력이 길수록 좋습니다. 바늘이 매우 짧으면 무차별적인 힘이 이길 수 있습니다.

편집하다:

기본 쌍, 영어 구 또는 단일 단어를 찾는 데 다른 알고리즘이 가장 좋습니다. 모든 입력에 대해 하나의 최상의 알고리즘이 있다면 공개되었을 것입니다.

다음의 작은 테이블에 대해 생각해보십시오. 각 물음표마다 다른 최상의 검색 알고리즘이있을 수 있습니다.

                 short needle     long needle
short haystack         ?               ?
long haystack          ?               ?

이것은 각 축에서 더 짧거나 더 긴 입력 범위를 갖는 그래프 여야합니다. 이러한 그래프에 각 알고리즘을 플로팅하면 각각 다른 서명을 갖게됩니다. 일부 알고리즘은 패턴에서 많은 반복이 발생하여 유전자 검색과 같은 용도에 영향을 줄 수 있습니다. 전체 성능에 영향을 미치는 다른 요소로는 동일한 패턴을 두 번 이상 검색하고 다른 패턴을 동시에 검색하는 것입니다.

샘플 세트가 필요한 경우 Google 또는 Wikipedia와 같은 사이트를 긁어 내고 모든 결과 페이지에서 html을 제거한다고 생각합니다. 검색 사이트의 경우 단어를 입력 한 다음 제안 된 검색 구 중 하나를 사용하십시오. 해당되는 경우 몇 가지 다른 언어를 선택하십시오. 웹 페이지를 사용하면 모든 텍스트가 중간에서 짧아 지므로 더 긴 텍스트를 얻을 수 있도록 충분한 페이지를 병합하십시오. 공개 도서, 법률 기록 및 기타 큰 본문을 찾을 수도 있습니다. 또는 사전에서 단어를 선택하여 임의의 컨텐츠를 생성하십시오. 그러나 프로파일 링의 요점은 검색 할 컨텐츠 유형에 대해 테스트하는 것이므로 가능한 경우 실제 샘플을 사용하십시오.

나는 짧고 긴 모호함을 남겼습니다. 바늘의 경우 8 자 이하, 중간자 64 자 이하, 1k 자 이하로 짧게 생각합니다. 건초 더미의 경우 2 ^ 10 미만, 중간 2 ^ 20 미만, 최대 2 ^ 30 자 정도로 생각합니다.


1
테스트 라이브러리에 대한 좋은 제안이 있습니까? 내가 SO에 요청한 이전 질문은 그와 관련이 있었고 실제 답변을 얻지 못했습니다. (나만의 것을 제외하고 ...) 그것은 광범위해야한다. 않는 strstr에 대한 응용 프로그램의 내 생각은 영어 텍스트를 검색하는 경우에도 다른 사람의가 ... 염기쌍 서열에서 유전자를 검색 할 수 있습니다
R을 ... GitHub의 STOP은 ICE 돕기

3
짧거나 긴 것보다 조금 더 복잡합니다. 바늘의 경우 대부분의 알고리즘의 성능과 관련된 큰 질문은 다음과 같습니다. 길이? 주기성이 있습니까? 바늘에는 모든 고유 한 문자가 포함되어 있습니까 (반복 없음)? 아니면 같은 캐릭터? 건초 더미에 바늘에 절대로 나타나지 않는 많은 문자가 있습니까? 최악의 성능을 이용하여 시스템을 손상시키려는 공격자가 제공 한 바늘을 처리해야 할 가능성이 있습니까? 등
R .. GitHub의 STOP 돕기 ICE

31

2011 년에 출판 된 Dany Breslauer, Roberto Grossi, Filippo Mignosi 의 "Simple Real-Time Constant-Space String Matching" 알고리즘 일 것입니다.

최신 정보:

2014 년 저자는 이러한 개선을 출판 : 최적의 문자열 일치를 포장 향해 .


1
와우 감사합니다. 나는 신문을 읽고 있습니다. 그것이 내가 가진 것보다 낫다면, 나는 당신의 대답을 분명히 받아 들일 것입니다.
R .. GitHub 중지 지원 얼음

1
@R .. : 물론입니다! :) 말하자면, 알고리즘을 구현할 수 있다면 모두가 그것을 이용할 수 있도록 StackOverflow에 게시하는 것을 고려하십시오! 나는 어디서나 구현을 찾지 못했고 연구 논문에서 찾은 알고리즘을 구현하는 데 능숙하지 않습니다.
user541686

2
이미 사용중인 "양방향"알고리즘의 변형이므로이 코드를 사용하도록 코드를 조정하면 실제로 쉽습니다. 그래도 확실하게 논문을 더 자세히 읽어야 할 것이며, 변경 사항이 일반적인 경우를 크게 단축시키는 "나쁜 문자표"를 사용하는 것과 호환되는지 평가해야합니다.
R .. GitHub 중지 지원 얼음

11
그리고 당신은 여전히 ​​@Mehrdad의 대답을 받아들이지 않았습니다! :-)
수명 균형

3
@DavidWallace : 무엇? 논문 제목과 저자가 있습니다. 연결이 끊어 지더라도 논문을 찾을 수 있습니다. 알고리즘에 대한 의사 코드를 작성하려면 어떻게해야합니까? 내가 알고리즘을 이해한다고 생각하는 것은 무엇입니까?
user541686

23

http://www-igm.univ-mlv.fr/~lecroq/string/index.html는 당신이이 가장 잘 알려진 및 연구 문자열 매칭 알고리즘의 일부의 훌륭한 소스 및 요약 포인트 연결합니다.

대부분의 검색 문제에 대한 솔루션에는 사전 처리 오버 헤드, 시간 및 공간 요구 사항과 관련하여 트레이드 오프가 포함됩니다. 모든 경우에 단일 알고리즘이 최적이거나 실용적이지는 않습니다.

문자열 검색을 위해 특정 알고리즘을 설계하는 것이 목표라면, 나머지 말을 무시하십시오. 일반화 된 문자열 검색 서비스 루틴을 개발하려면 다음을 시도하십시오.

이미 언급 한 알고리즘의 강점과 약점을 검토하는 데 시간을 보내십시오. 관심있는 문자열 검색의 범위와 범위를 포괄하는 일련의 알고리즘을 찾는 것을 목표로 검토를 수행하십시오. 그런 다음 주어진 입력에 가장 적합한 알고리즘을 대상으로하는 분류기 함수를 기반으로 프론트 엔드 검색 선택기를 빌드하십시오. 이런 식으로 가장 효율적인 알고리즘을 사용하여 작업을 수행 할 수 있습니다. 이는 특정 검색에 알고리즘이 매우 우수하지만 성능이 저하 될 때 특히 효과적입니다. 예를 들어, 짐승의 힘은 바늘 길이가 증가함에 따라 길이 1의 바늘 있지만 빨리 저하, 그러자 대한 아마 최고입니다 sustik - 무어 algoritim더 작은 바늘보다 더 효율적일 수 있고, 더 긴 바늘과 더 큰 알파벳의 경우 KMP 또는 Boyer-Moore 알고리즘이 더 나을 수 있습니다. 다음은 가능한 전략을 설명하기위한 예일뿐입니다.

다중 알고리즘은 새로운 아이디어가 아닙니다. 나는 그것이 몇몇 상용 Sort / Search 패키지에 의해 사용되었다고 생각한다 (예를 들어 메인 프레임에서 일반적으로 사용되는 SYNCSORT는 몇 가지 정렬 알고리즘을 구현하고 휴리스틱을 사용하여 주어진 입력에 가장 적합한 것을 선택한다)

각 검색 알고리즘은 예를 들어이 백서에서 설명하는 것처럼 성능에 큰 차이를 만들 수있는 몇 가지 변형이 있습니다 .

서비스를 벤치마킹하여 추가 검색 전략이 필요한 영역을 분류하거나 선택기 기능을보다 효과적으로 조정하십시오. 이 방법은 빠르거나 쉽지는 않지만 잘 수행하면 매우 좋은 결과를 얻을 수 있습니다.


1
응답, 특히 전에 보지 못한 Sustik-Moore에 대한 링크에 감사드립니다. 다중 알고리즘 접근법은 확실히 널리 사용됩니다. Glibc는 needle_len이 1, <32 또는> 32인지에 따라 기본적으로 strchr, 잘못된 문자 이동표가없는 양방향 또는 잘못된 문자 이동표가있는 양방향을 수행합니다. 현재 접근 방식은 항상 시프트 테이블을 사용한다는 점을 제외하고 동일합니다. 테이블의 어떤 요소가 초기화되었는지 표시하는 데 사용되는 비트 세트에서 32 바이트 memset으로 필요한 1kb memset을 교체했으며 작은 바늘에도 이점이 있습니다 (오버 헤드는 아님).
R .. GitHub 중지 지원 얼음

1
그것에 대해 생각한 후, Sustik-Moore의 의도 된 응용 프로그램이 무엇인지 정말로 궁금합니다. 작은 알파벳을 사용하면 크게 변화하지 않을 것입니다 (알파벳의 모든 문자가 바늘 끝 근처에 거의 확실하게 나타남). 그래서 Sustik-Moore가 최적 일 수있는 어떤 시나리오도 상상할 수 없습니다 ...
R .. GitHub 중지 도움 얼음

훌륭한 답변-이 특정 답변에 별표를 표시 할 수 있다면 가능합니다.
Jason S

1
@R .. sustik-moore 알고리즘의 이론은 바늘이 상대적으로 크고 알파벳이 상대적으로 작을 때 (예 : DNA 서열 검색) 더 큰 평균 이동량을 제공해야한다는 것입니다. 이 경우 더 큰 것은 동일한 입력이 주어지면 기본 Boyer-Moore 알고리즘이 생성하는 것보다 더 큰 것을 의미합니다. 유한 오토마타 접근 방식이나 다른 보이어-무어 변형 (많은 것들이 있음)과 비교하여 이것이 얼마나 효율적인지 말하기는 어렵습니다. 그렇기 때문에 후보 알고리즘의 특정 강점 / 약점을 연구하는 데 시간을 투자하는 것을 강조했습니다.
NealB

1
흠, Boyer-Moore의 나쁜 캐릭터 이동이라는 의미에서 변화에 대해 생각하고 있었을 것 같습니다. BM에서 좋은 접미사 이동을 개선함으로써 Sustik-Moore는 DNA 검색에 대한 DFA 접근 방식을 능가 할 수있었습니다. 깔끔한 물건.
R .. GitHub 중지 지원 얼음

21

이 토론에서 기술 보고서가 인용 된 것을보고 놀랐습니다. 위의 Sustik-Moore라는 알고리즘의 저자 중 하나입니다. (우리는이 용어를 우리 논문에서 사용하지 않았습니다.)

여기서 알고리즘의 가장 흥미로운 특징은 각 문자가 최대 한 번만 검사된다는 것을 증명하는 것이 매우 간단하다는 점입니다. 이전의 보이어-무어 (Boyer-Moore) 이전 버전에서는 각 문자가 최대 3 회 이상 최대 2 회까지 검사되었으며, 그 증거가 더 많이 관련되어 있음을 증명했습니다 (서류 인용 참조). 그러므로 나는 또한이 변형을 제시 / 연구하는데 교훈적인 가치를 볼 수 있습니다.

이 백서에서는 이론적 보장을 완화하면서 효율성을 향한 추가 변형을 설명합니다. 짧은 논문이며 자료는 제 생각에 보통의 고등학교 졸업생에게 이해할 수 있어야합니다.

우리의 주요 목표는이 버전을 더 향상시킬 수있는 다른 사람들의 관심을 끌기위한 것입니다. 문자열 검색에는 많은 변형이 있으므로이 아이디어가 이점을 가져올 수있는 모든 것을 생각할 수는 없습니다. (고정 텍스트 및 패턴 변경, 고정 패턴 다른 텍스트, 전처리 가능 / 불가능, 병렬 실행, 큰 텍스트에서 일치하는 하위 집합 찾기, 오류 허용, 일치하는 것 등)


1
사용 가능한 C 또는 C ++ 구현에 대해 알고 있습니까? 일부 DNA 모티프 검색 (정확한 모티프 일치)에 이것을 사용하려고합니다. 그렇지 않다면, 아마도 직접 구현을 개발하고 알고리즘을 향상시키기 위해 제출할 것입니다.
JDiMatteo 18:32의

4
알려진 구현이 없기 때문에 Sustik-Moore / 2BLOCK 알고리즘은 실제로 사용되지 않을 것 같으며 "정확한 문자열 일치 문제 : 포괄적 인 실험 평가"
JDiMatteo

18

가장 빠른 하위 문자열 검색 알고리즘은 컨텍스트에 따라 다릅니다.

  1. 알파벳 크기 (예 : DNA 대 영어)
  2. 바늘 길이

2010 년 논문 "정확한 문자열 일치 문제 : 포괄적 인 실험 평가" 에서는 51 개의 알고리즘 (알파벳 크기와 바늘 길이가 다른)에 대한 런타임이있는 테이블을 제공하므로 상황에 가장 적합한 알고리즘을 선택할 수 있습니다.

이러한 모든 알고리즘에는 C 구현과 테스트 스위트가 있습니다.

http://www.dmi.unict.it/~faro/smart/algorithms.php


4

정말 좋은 질문입니다. 작은 비트를 추가하십시오 ...

  1. 누군가가 DNA 서열 매칭에 대해 이야기하고있었습니다. 그러나 DNA 서열의 경우, 일반적으로 건초 더미에 대한 데이터 구조 (예 : 접미사 배열, 접미사 트리 또는 FM 색인)를 작성하고 많은 바늘을 일치시키는 것입니다. 이것은 다른 질문입니다.

  2. 누군가가 다양한 알고리즘을 벤치마킹하고 싶다면 정말 좋을 것입니다. 압축 및 접미사 배열 구성에 대한 벤치 마크는 매우 좋지만 문자열 일치에 대한 벤치 마크는 보지 못했습니다. 잠재적 건초 더미 후보는 SACA 벤치 마크 에서 올 수 있습니다 .

  3. 며칠 전 권장 페이지 에서 Boyer-Moore 구현을 테스트하고있었습니다 (편집 : memmem ()과 같은 함수 호출이 필요하지만 표준 함수는 아니므로 구현하기로 결정했습니다). 내 벤치마킹 프로그램은 임의 건초 더미를 사용합니다. 이 페이지의 Boyer-Moore 구현은 glibc의 memmem () 및 Mac의 strnstr ()보다 몇 배나 빠릅니다. 관심이 있으시면 구현이 여기 있고 벤치마킹 코드가 여기 있습니다 . 이것은 실제로 현실적인 벤치 마크는 아니지만 시작입니다.


SACA 벤치 마크의 건초 더미 후보와 함께 테스트 할 바늘이 좋으면 다른 질문에 대한 답변으로 게시하고 더 나은 답변을 얻지 못하는 것으로 인정합니다.
R .. GitHub 중지 지원 얼음

3
Memmem과 Boyer-Moore에 대해서는 Boyer-Moore (또는 Boyer-Moore의 향상된 기능 중 하나)가 임의의 데이터에서 가장 잘 수행 될 가능성이 큽니다. 랜덤 데이터는주기 확률이 매우 낮고 부분 일치가 길어 2 차 최악의 경우가 발생합니다. Boyer-Moore와 Two-Way를 결합하거나 Boyer-Moore가 "사용하기에 안전"한시기를 효율적으로 감지하는 방법을 찾고 있지만 지금까지 성공하지 못했습니다. BTW 나는 glibc의 memmem을 비교로 사용하지 않을 것입니다. glibc와 기본적으로 동일한 알고리즘에 대한 구현은 몇 배 더 빠릅니다.
R .. GitHub 중지 지원 얼음

내가 말했듯이, 그것은 내 구현이 아닙니다. Christian Charras와 Thierry Lecroq에게 감사드립니다. 랜덤 입력이 벤치마킹에 나쁜 이유를 상상할 수 있으며 glibc가 이유로 알고리즘을 선택한다고 확신합니다. 또한 memmem ()이 효율적으로 구현되지 않은 것 같습니다. 노력하겠습니다. 감사.
user172818

4

나는 그것이 오래된 질문이라는 것을 알고 있지만, 대부분의 나쁜 교대 테이블은 단일 문자입니다. 데이터 세트에 적합하다면 (예 : 단어를 쓰는 경우), 여유 공간이 있으면 단일 문자가 아닌 n-gram으로 구성된 잘못된 시프트 테이블을 사용하여 속도를 크게 높일 수 있습니다.


3

stdlib 사용 strstr:

char *foundit = strstr(haystack, needle);

매우 빠르며 입력하는 데 약 5 초가 걸렸습니다.


26
그리고 당신이 내 질문을 읽는다면 당신은 내가 그것을 능가하는 아주 쉬운 시간을 보았을 것입니다. 나는 당신의 풍자를 좋아하지만 -1을 건너 뛸 것입니다.
R .. GitHub 중지 지원 얼음

3

다음 은 코어 전체에서 사용되는 Python의 검색 구현 입니다. 주석은 압축 된 boyer-moore delta 1 테이블을 사용함을 나타냅니다 .

문자열 검색을 사용하여 꽤 광범위한 실험을 수행했지만 여러 검색 문자열을 대상으로했습니다. HorspoolBitap 의 어셈블리 구현은 종종 패턴 수가 적은 Aho-Corasick 과 같은 알고리즘에 대해 자체적으로 유지할 수 있습니다.


3

더 빠른 "단일 일치 문자 검색"(ala strchr) 알고리즘.

중요 사항 :

  • 이 함수들은 "내장 / 후행 0" gcc컴파일러 내장 함수를 사용 __builtin_ctz합니다. 이러한 기능은이 작업을 수행하는 명령어 (예 : x86, ppc, arm)가있는 시스템에서만 빠를 수 있습니다.

  • 이러한 기능은 대상 아키텍처가 32 및 64 비트 정렬되지 않은로드를 수행 할 수 있다고 가정합니다. 대상 아키텍처가이를 지원하지 않으면 읽기 로직을 ​​올바르게 정렬하기 위해 시작 로직을 추가해야합니다.

  • 이러한 기능은 프로세서 중립적입니다. 대상 CPU에 벡터 명령어가있는 경우 더 많은 작업을 수행 할 수 있습니다. 예를 들어, strlen아래 함수는 SSE3을 사용하며 이외의 바이트를 찾기 위해 스캔 한 바이트를 XOR로 간단하게 수정할 수 있습니다 0. Mac OS X 10.6 (x86_64)을 실행하는 2.66GHz Core 2 랩탑에서 수행 된 벤치 마크 :

    • 843.433MB / 초 strchr
    • 2656.742MB / 초 findFirstByte64
    • 13094.479MB / 초 strlen

... 32 비트 버전 :

#ifdef __BIG_ENDIAN__
#define findFirstZeroByte32(x) ({ uint32_t _x = (x); _x = ~(((_x & 0x7F7F7F7Fu) + 0x7F7F7F7Fu) | _x | 0x7F7F7F7Fu); (_x == 0u)   ? 0 : (__builtin_clz(_x) >> 3) + 1; })
#else
#define findFirstZeroByte32(x) ({ uint32_t _x = (x); _x = ~(((_x & 0x7F7F7F7Fu) + 0x7F7F7F7Fu) | _x | 0x7F7F7F7Fu);                    (__builtin_ctz(_x) + 1) >> 3; })
#endif

unsigned char *findFirstByte32(unsigned char *ptr, unsigned char byte) {
  uint32_t *ptr32 = (uint32_t *)ptr, firstByte32 = 0u, byteMask32 = (byte) | (byte << 8);
  byteMask32 |= byteMask32 << 16;
  while((firstByte32 = findFirstZeroByte32((*ptr32) ^ byteMask32)) == 0) { ptr32++; }
  return(ptr + ((((unsigned char *)ptr32) - ptr) + firstByte32 - 1));
}

... 및 64 비트 버전 :

#ifdef __BIG_ENDIAN__
#define findFirstZeroByte64(x) ({ uint64_t _x = (x); _x = ~(((_x & 0x7F7F7F7F7f7f7f7full) + 0x7F7F7F7F7f7f7f7full) | _x | 0x7F7F7F7F7f7f7f7full); (_x == 0ull) ? 0 : (__builtin_clzll(_x) >> 3) + 1; })
#else
#define findFirstZeroByte64(x) ({ uint64_t _x = (x); _x = ~(((_x & 0x7F7F7F7F7f7f7f7full) + 0x7F7F7F7F7f7f7f7full) | _x | 0x7F7F7F7F7f7f7f7full);                    (__builtin_ctzll(_x) + 1) >> 3; })
#endif

unsigned char *findFirstByte64(unsigned char *ptr, unsigned char byte) {
  uint64_t *ptr64 = (uint64_t *)ptr, firstByte64 = 0u, byteMask64 = (byte) | (byte << 8);
  byteMask64 |= byteMask64 << 16;
  byteMask64 |= byteMask64 << 32;
  while((firstByte64 = findFirstZeroByte64((*ptr64) ^ byteMask64)) == 0) { ptr64++; }
  return(ptr + ((((unsigned char *)ptr64) - ptr) + firstByte64 - 1));
}

2011/06/04 편집 OP는이 솔루션에 "극복 할 수없는 버그"가 있다는 의견에서 지적합니다.

검색된 바이트 나 널 (null) 종료자를지나 읽을 수 있으며, 읽기 권한없이 매핑되지 않은 페이지 나 페이지에 액세스 할 수 있습니다. 정렬되지 않으면 문자열 함수에서 큰 읽기를 사용할 수 없습니다.

이것은 기술적으로 사실이지만 주석에서 OP가 제안한 방법을 포함 하여 단일 바이트보다 큰 청크에서 작동하는 거의 모든 알고리즘에 적용됩니다 .

일반적인 strchr구현은 순진하지 않지만 제공 한 것보다 훨씬 효율적입니다. 가장 널리 사용되는 알고리즘은 다음을 참조하십시오 . http://graphics.stanford.edu/~seander/bithacks.html#ZeroInWord

또한 정렬 자체 와는 아무런 관련이 없습니다 . 사실 이것은 잠재적으로 사용되는 대부분의 일반적인 아키텍처에서 논의되는 동작을 잠재적으로 유발할 수 있지만, 마이크로 아키텍처 구현 세부 사항과 관련이 있습니다. 다음 4K 페이지 경계가 매핑 해제되면 종료 오류가 발생합니다.

그러나 이것은 답변에 주어진 알고리즘에서 "버그"가 아닙니다. 행동은 함수 가 검색 크기를 제한 하는 인수 strchr와 같고 인수를 strlen허용하지 않기 때문 length입니다. 검색 char bytes[1] = {0x55};논의의 목적을 위해 너무 4K의 VM 페이지 경계와 다음 페이지의 맨 끝에 배치 할 일이있는 것은, 함께, 매핑되지 strchr(bytes, 0xAA)(여기서 strchrA는 한 번에 한 바이트 씩 구현됩니다) 정확히 충돌합니다 같은 길. strchr관련 사촌을 위한 Ditto strlen.

없이 length인수는 바이트 단위 알고리즘 고속 알고리즘과 다시 밖으로 전환해야 할 때 알 수있는 방법이 없습니다. 훨씬 더 "버그"는 "할당 된 과거 크기"를 읽는 것인데, 이는 기술적으로 undefined behavior다양한 C 언어 표준 에 따라 나타나며 다음과 같은 오류로 표시됩니다 valgrind.

요약이 답변의 코드가 수행하고 코드가 OP 지적하지만, 바이트 정확한 읽기의 의미를 가지고 있어야하는 것은 전혀 존재하지 않는 경우 "버그"를 할 가능성이 높습니다로 빠르게 이동 바이트 청크보다 큰에서 작동 아무것도 length에 인수 "마지막 판독"의 코너 케이스를 제어합니다.

이 답변의 코드는 대상 CPU에 빠른 ctz명령 이있는 경우 자연 CPU 단어 크기 청크에서 첫 번째 바이트를 빠르게 찾을 수있는 커널입니다 . 올바로 정렬 된 자연 경계 또는 일부 형태의 경계에서만 작동하는지 확인하는 것과 같은 것을 추가하는 것은 사소한 length일입니다. 이렇게하면 고속 커널에서 바이트 단위로 느린 속도로 전환 할 수 있습니다.

OP는 또한 의견에 다음과 같이 언급합니다.

ctz 최적화에 관해서는 O (1) 꼬리 연산에만 차이가 있습니다. 작은 줄로 성능을 향상시킬 수 있습니다 (예를 들어 strchr("abc", 'a');, 큰 크기의 줄에서는 그렇지 않습니다).

이 진술이 사실인지 아닌지는 문제의 마이크로 아키텍처에 크게 좌우됩니다. 표준 4 단계 RISC 파이프 라인 모델을 사용하면 거의 확실합니다. 그러나 코어 속도가 메모리 스트리밍 속도를 완전히 떨어 뜨릴 수있는 최신의 비 순차 슈퍼 스칼라 CPU에 대해서는 사실인지 알기가 매우 어렵습니다. 이 경우 "스트리밍 할 수있는 바이트 수"에 비해 "사용할 수있는 명령어 수"에 큰 차이가 있으므로 " "스트리밍 할 수있는 각 바이트에 대해 폐기 될 수있는 명령 수". 이 값이 충분히 크면 ctz+ 시프트 명령을 "무료로"수행 할 수 있습니다.


"길이가 1 인 바늘의 경우을 사용하십시오 strchr."-가장 빠른 하위 문자열 검색 알고리즘을 요청했습니다. 길이가 1 인 부분 문자열을 찾는 것은 최적화 할 수있는 특별한 경우입니다. strchr위와 같은 길이 1 ( )의 하위 문자열에 대한 현재 특수 사례 코드를 바꾸면 ( strchr구현 방법에 따라 ) 속도가 빨라집니다. 위의 알고리즘은 일반적인 순진한 strchr구현 보다 거의 3 배 빠릅니다 .
johne

2
OP는 문자열이 적절하게 null로 종료되었으므로 토론에 char bytes[1] = {0x55};관련이 없다고 말했습니다 . 미리 길이를 모르는 단어 읽기 알고리즘에 대해 이것이 사실이라는 귀하의 의견과 매우 관련이 있습니다.
세스 로버트슨

1
정렬 된 포인터에서만 사용하기 때문에 인용 한 버전에는 문제가 적용되지 않습니다. 적어도 올바른 구현입니다.
R .. GitHub 중지 지원 얼음

2
@R, "정렬 포인터"와는 아무런 관련이 없습니다. 가정 해, 당신은 바이트 수준의 세분화와 VM 보호를 지원하는 아키텍처가 있고, 각각의 경우 malloc할당이 양쪽에 "충분히 패딩"이었다 VM 시스템이 그 할당 바이트 세분화 된 보호 기능을 강화 .... 여부 포인터가 정렬되지 ( 사소한 32 비트 int자연 정렬을 가정 할 때 ) 문제가 발생하지만 정렬 된 읽기가 할당 크기를 초과하여 읽을 수 있습니다. 할당 크기를 초과 한 모든 읽기는 undefined behavior입니다.
johne

5
@johne : 댓글 +1 개념적으로는 옳지 만, 실제로는 바이트 단위 보호가 저장 및 적용에 너무 비싸서 존재하지 않으며 존재하지 않을 것입니다. 기본 스토리지가에 해당하는 페이지 세분화 매핑임을 알고 있으면 mmap정렬로 충분합니다.
R .. GitHub 중지 지원 얼음

3

"가장 빠른 strstr"을 검색하고 관심있는 것이 있으면 저에게 물어보십시오.

내 견해로는 너 자신에 너무 많은 제한을 가하지 만 (그렇다. 우리 모두 max searcher에서 sub-linear linear를 원한다.) 실제 프로그래머가 개입해야 해시 접근 방식이 단순히 멋진 림보 솔루션이라고 생각한다. 더 짧은 2..16 패턴을 위해 BNDM에 의해 강화 됨).

간단한 예 :

로 - 한 줄의 문자열 (206908949bytes)에 패턴 (32 바이트)에 대한 검색을 수행 ... 건너 뛰기 - 성능 (더 - 더 - 더 나은) 3041 % 6,801,754 스킵 / 반복의 Railgun_Quadruplet_7Hasherezade_hits / Railgun_Quadruplet_7Hasherezade_clocks : 0/58 Railgun_Quadruplet_7Hasherezade 성능 : 3천4백83킬로바이트 / 시계

1천5백54%, 13,307,181 스킵 / 반복의 Boyer_Moore_Flensburg_hits / Boyer_Moore_Flensburg_clocks : 0/83로 - 한 줄 ... 건너 뛰기 - 성능 (더 - 더 - 더) 문자열 (206908949bytes)에 패턴 (32 바이트) 검색하고 Boyer_Moore_Flensburg의 성능 : 2,434킬로바이트를 / 시계

문자열 (206908949bytes)로 패턴 (32bytes)을 한 줄로 검색하기 ... Skip-Performance (더 나은) : 129 %, 160239051 건너 뛰기 / 반복 Two-Way_hits / Two-Way_clocks : 0/816 Two -웨이 성능 : 247KB / 시계

산 메이스,
안부


3

귀하의 질문에 언급 한 양방향 알고리즘 (최근에 놀라운 것입니다!)은 최근에 한 번에 멀티 바이트 단어에서 효율적으로 작동하도록 향상되었습니다 : Optimal Packed String Matching .

나는 전체 논문을 읽지는 않았지만 시간 복잡성 주장에 대해 O (1) 인 몇 가지 새로운 특수 CPU 명령어 (예 : SSE 4.2에 포함)에 의존하는 것 같습니다. 너무 나쁘지 않은 w 비트 단어에 대해 O (log log w) 시간에 시뮬레이션하십시오.


3

예를 들어 4 가지 알고리즘을 구현할 수 있습니다. 경험적으로 결정될 M 분마다 현재 실제 데이터에 대해 4 개를 모두 실행합니다. N 번 실행에 대한 통계를 축적합니다 (또한 TBD). 그런 다음 M 분 동안 승자 만 사용하십시오.

결코 승리하지 않는 알고리즘을 새로운 것으로 대체 할 수 있도록 승리에 대한 통계 기록. 가장 성공한 루틴에 최적화 노력을 집중하십시오. 하드웨어, 데이터베이스 또는 데이터 소스를 변경 한 후 통계에 특히주의하십시오. 가능하면 통계 로그에 해당 정보를 포함 시키므로 로그 날짜 / 시간 스탬프에서 알아낼 필요가 없습니다.


3

최근에 다양한 사용 가능한 알고리즘의 성능을 측정 할 수있는 훌륭한 도구를 발견했습니다. http://www.dmi.unict.it/~faro/smart/index.php

유용 할 수 있습니다. 또한 하위 문자열 검색 알고리즘을 빠르게 호출 해야하는 경우 Knuth-Morris-Pratt를 사용하십시오.


링크 주셔서 감사합니다. 테스트는 일반적인 경우에는 흥미롭지 만 최악의 경우에는 들리지 않습니다.
R .. GitHub STOP ICE HELPING ICE

2

성능에 큰 영향을 줄 수 있으므로 여러 유형의 문자열로 다양한 벤치 마크를 원할 수도 있습니다. algos는 자연어 검색 (그리고 여기에도 다른 형태로 인해 세분화 된 차이가있을 수 있음), DNA 문자열 또는 임의의 문자열 등을 기반으로 차별화를 수행합니다.

알파벳 크기는 바늘 크기와 마찬가지로 많은 알고에서 중요한 역할을합니다. 예를 들어 Horspool은 영어 텍스트에서는 우수하지만 알파벳 크기가 다르기 때문에 DNA에서는 좋지 않아 나쁜 문자 규칙을 적용하기가 어렵습니다. 좋은 접미사를 소개하면 이것을 크게 완화시킬 수 있습니다.


0

그것이 최선인지는 모르겠지만 Boyer-Moore 와 좋은 경험을했습니다 .


Boyer-Moore의 불량 이동 테이블을 양방향과 결합하는 방법을 알고 있습니까? Glibc는 긴 바늘 (> 32 바이트)에 대한 변형을 수행하지만 마지막 바이트 만 검사합니다. 문제는 양방향 오른쪽 바늘을 왼쪽에서 오른쪽으로 검색해야하는 반면 Boyer-Moore의 잘못된 이동은 오른쪽에서 왼쪽으로 검색 할 때 가장 효율적입니다. 나는 양방향으로 왼쪽에서 오른쪽으로 사용하려고 시도했지만 (시프트 테이블 또는 일반적인 양방향 오른쪽 불일치 중 더 길거나 더 길어짐) 더 길지만 대부분의 경우 일반적인 양방향에 비해 5-10 %의 둔화를 얻었습니다. 성능이 개선 된 사례를 찾지 못했습니다.
R .. GitHub 중지 지원 얼음

0

이것은 질문에 직접 대답하지는 않지만 텍스트가 매우 큰 경우 텍스트를 겹치는 섹션으로 나누는 방법 (패턴 길이로 겹침)은 스레드를 사용하여 섹션을 동시에 검색하는 방법에 대한 것입니다. 가장 빠른 알고리즘과 관련하여 Boyer-Moore-Horspool은 Boyer-Moore의 변형 중에서 가장 빠르지는 않지만 가장 빠른 알고리즘 중 하나라고 생각합니다. BMH (Boyer–Moore–Horspool) Search보다 알고리즘 이라는 주제에 Boyer-Moore 변형 (그들의 이름을 모른다)을 게시했습니다 .


0

S. Faro와 OM Kulekci가 현재 가장 빠른 EPSM입니다. 참조 http://www.dmi.unict.it/~faro/smart/algorithms.php?algorithm=EPSM&code=epsm를

SIMD SSE4.2 (x86_64 및 aarch64)에 최적화 된 "정확한 팩 문자열 일치". 모든 크기에서 안정적으로 작동합니다.

내가 링크 한 사이트는 199 개의 빠른 문자열 검색 알고리즘을 비교하며 일반적인 알고리즘 (BM, KMP, BMH)은 상당히 느립니다. EPSM은이 플랫폼에서 언급 된 다른 모든 제품보다 뛰어납니다. 또한 최신입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.