2010-06-28 6 views
14

Cactus Kev's Poker Hand Evaluator을 통해 읽으면 다음과 같은 문구가 나타납니다.어떤 것이 더 빠릅니까? 작은 배열의 요소를 정렬하거나 배가 시키시겠습니까?

처음에는 손을 먼저 평가자에게 전달하기 전에 먼저 정렬 할 수 있다고 생각했습니다. 그러나 정렬에는 시간이 걸리고 손을 정렬하는 CPU 사이클을 낭비하고 싶지 않았습니다. 나는 다섯 장의 카드가 어떤 순서로 주어 졌는지 신경 쓰지 않는 방법이 필요했습니다.
...
많은 생각을 한 후, 나는 소수를 사용하는 브레인 스톰을 가졌습니다. 나는 각 카드 랭크에 소수 값을 할당 할 것이다 ...이 시스템의 장점은 각 카드 랭킹의 소수를 곱하면 주문에 관계없이 고유 한 제품을 얻을 수 있다는 것이다. 다섯 장의 카드 중 하나.

곱셈은 컴퓨터가 할 수있는 가장 빠른 계산 중 하나이기 때문에 평가 전에 각 손을 정렬해야했던 시간을 수백 밀리 초 줄였습니다.

나는 이것을 믿기가 힘듭니다.

Cactus Kev는 각 카드를 4 바이트 정수로 나타내며 eval_5cards(int c1, int c2, int c3, int c4, int c5)을 호출하여 손을 평가합니다. 우리는 카드를 1 바이트로 표현하고 포커 핸드를 5 바이트 배열로 표현할 수 있습니다. 고유 한 손을 얻기 위해이 5 바이트 배열을 정렬하는 것은 꽤 빨라야합니다. 그의 접근 방식보다 더 빠릅니까?

우리가 그의 표현 (카드를 4 바이트 정수로)을 유지한다면 어떨까요? 5 개의 정수 배열을 곱하는 것보다 빠르게 정렬 할 수 있습니까? 그렇지 않은 경우 적은 수의 요소를 빠르게 정렬하기 위해 어떤 종류의 하위 수준 최적화를 수행 할 수 있습니까?

감사합니다.

모두에게 좋은 답변입니다. 나는 성능 대 성능 통계를 얻기 위해 승수를 정렬하는 성능을 벤치마킹하기 위해 노력하고 있습니다.

+0

그는 승산을 사용할 필요조차 없습니다 - 등급을 나타 내기 위해 숫자를 다르게 선택하여 더 잘 작동하도록 추가 할 수 있습니다. 그것은 본질적으로 단지 입력 항목의 재정렬 하에서 불변 인 해시 함수입니다. – caf

+0

곱셈은 하드웨어에서 수행되는 원시 연산임을 기억하십시오. 그의 이야기에는 장점이있을 수 있습니다. 이것은 본질적으로 해싱입니다 (그의 해시 함수는 일대일로 매우 빠르게 계산할 수 있습니다). – ldog

+0

이 글을 읽을 때 나는 회의적이었습니다. 최적의 정렬 네트워크는 분명히 느리지 만, 비트 마스크를 사용하고 카드를 결합하면 더 간단했을 것입니다. 그는 계산하기가 더 복잡한 Combinadics를 사용할 수도 있지만 모든 유효한 포커 핸드에 대해 연속적인 범위를 가지므로 해시 테이블을 필요로하지 않고 배열에서 손을 찾을 수 있습니다. –

답변

5

정렬은 숫자를 곱하는 것보다 본질적으로 어렵지 않습니다. 종이에서 볼 때, 거의 동일합니다. 또한 정교한 곱셈 알고리즘을 사용하여 큰 배율에 비해 큰 곱셈을 경쟁력있게 만듭니다. 게다가, 제안 된 곱셈 알고리즘이 가능할 때, 점근 적으로 더 빠른 버킷 정렬을 사용할 수 있습니다.

그러나 포커 핸드는 점근적인 문제가 아닙니다. 단지 5 장의 카드 일 뿐이며 카드의 13 가지 숫자 값 중 하나에 대해서만 신경을 씁니다. 곱셈이 원칙적으로 복잡하더라도 실제로는 마이크로 코드로 구현되며 믿을 수 없을 정도로 빠릅니다. 그가하는 일은 일합니다.

이제 이론적 인 질문에 관심이 있다면 곱셈이 아닌 덧셈을 사용하는 솔루션도 있습니다. 어떤 값으로도 4 장의 카드 만있을 수 있으므로 1,5,25, ..., 5^12 값을 할당하고 추가 할 수 있습니다. 여전히 32 비트 산술 연산에 적합합니다. 다른 수학적 속성을 가진 다른 추가 기반 솔루션도 있습니다. 그러나 마이크로 코드 연산이 컴퓨터가하는 것보다 훨씬 빠르기 때문에 실제로는 중요하지 않습니다.

6

테스트하지 않고, 나는 그의 주장에 동조합니다. 정렬과 비교하여 4 곱셈으로 수행 할 수 있습니다 (n log n). 특히, 최적의 sorting network은 9 번의 비교가 필요합니다. 그런 다음 평가자는 정렬 된 배열의 모든 요소 (적어도 5 개의 연산)를 살펴 봐야합니다.

+20

고정 된 n = 5에 대해 이야기 할 때 Big O의 복잡성은 완전히 관련이 없습니다. –

+2

질문 텍스트는 5에 고유하지만 제목은 일반적으로 작은 배열에 대해 묻습니다. 어쨌든이 예제에 필요한 정확한 비교 횟수를 넣었습니다. –

+5

스택에 4 개의 정수를 할당하는 것은 포인터를 증가시킴으로써 수행 할 수 있습니다. 그것은 거의 병목 현상이 될 것 같지 않습니다. –

1

정말 관련이 없어야하지만 그는 맞습니다. 정렬은 곱하기보다 훨씬 오래 걸립니다.

진짜 문제는 (그것을 감안 이후 정렬보다 오래 걸릴 것으로 예상됩니다.

+0

내가 정확하게 기억하면, 그는 그것을 거대한 테이블로 훑어보기로 사용했습니다. – AShelly

+1

그는 5,598,960 개의 고유 한 5 카드 포커 핸드 각각을 7462 개의 고유 한 값 중 하나에 매핑합니다. 같은 종류의 [동등한 관계] (http://en.wikipedia.org/wiki/Equivalence_relation). – Rudiger

+0

Asymptotically, sorting은 곱하기보다 훨씬 오래 걸립니다. 이것은 거의 점근 적 성능의 반대입니다! – Rudiger

1

을 그것은 될 수있는 정렬 작업을 생각하기 어렵다 그 결과 소수와 무슨 짓을했는지, 그 도움이 얼마나입니다 동일한 숫자 세트를 곱하는 것보다 빠릅니다. 프로세서 레벨에서 곱셈은 누적 기의 일부 조작과 함께 load, load, multiply, load, multiply, ... 일뿐입니다. 직선적이며 쉽게 파이프 라인되며 관련 분기 오류 예측 비용과의 비교가 없습니다. 값 당 약 2 명령이 곱 해져야합니다. 곱하기 명령이 고통스럽게 느린 것이 아니라면 더 빠른 정렬을 상상하기 란 정말로 어렵습니다.

+0

+1 정렬의 메모리 액세스 패턴 (일반적으로 mov/load는 곱하기보다 훨씬 느립니다)은 5 개의 적분을 곱하는 것만 큼 빠르지는 않습니다. – stinky472

6

물론 컴퓨터의 CPU에 따라 다르지만 일반적인 Intel CPU (예 : 코어 2 듀오)는 3 CPU 클럭 사이클 내에서 2 개의 32 비트 숫자를 곱할 수 있습니다. 정렬 알고리즘이이 알고리즘을 능가하려면 알고리즘이 3 * 4 = 12 CPU주기보다 빠를 필요가 있습니다. 이는 매우 엄격한 제약입니다. 표준 정렬 알고리즘으로는 12 사이클 미만으로 처리 할 수 ​​없습니다.혼자 두 숫자의 비교는 하나의 CPU주기를 취할 것이며, 결과의 조건부 분기도 하나의 CPU주기를 취하게됩니다. 그러면 당신이하는 일은 적어도 하나의 CPU주기를 취할 것입니다 (두 개의 카드를 교체하는 데 실제로 최소한 4 CPU주기가 소요됩니다). 이렇게 곱하는 승리.

물론 이것은 1 차 또는 2 차 레벨 캐시 또는 심지어 메모리에서 카드 값을 가져 오는 데 대기 시간을 고려하지 않고 있습니다. 그러나이 대기 시간은 곱하기 및 정렬의 경우 모두에 적용됩니다.

+0

그는 카드 값을 가져와 13 소수의 목록을 조회해야합니다. 정렬보다 더 빠릅니다. – phkahler

+0

사실을 고려하면, 13 개의 소수는 1 개 또는 2 개의 검색 후에 1 차 레벨 캐시에있을 확률이 높으므로 각 카드 조회를 위해 3 개의 추가 클럭 사이클이 추가됩니다. 그러나 당신 말이 맞습니다. 이것은 여전히 ​​더 빠릅니다. – Mecki

+1

요점은 좋지만주기 계산은 약간 꺼져있는 것 같습니다. 곱셈의 경우 곱셈 중 하나가 파이프 라인 될 수 있으므로 대기 시간은 9 사이클입니다. 조건부 분기는 오 예측으로 인해 평균적으로 1 사이클 이상 걸릴 것입니다. CMOV는 제어 흐름을 데이터 종속성으로 바꿀 수 있지만 파이프 라이닝을 고려하더라도 최적의 정렬 네트워크에 대해 CMOV = 11 사이클에 대해 CMP + 5 * 2 사이클에 대해 1 사이클이 가장 좋습니다. –

2

5 개의 요소는 최적화 된 의사 결정 트리를 사용하여 정렬 할 수 있으며 범용 정렬 알고리즘을 사용하는 것보다 훨씬 빠릅니다.

그러나 사실은 분류가 많은 분기를 의미한다는 것입니다 (이후에 필요한 비교가 그렇듯이). 브랜치는 입니다. 실제는입니다. 이는 현대 파이프 라인 CPU 아키텍처, 특히 유사한 가능성으로 어느 방향 으로든 갈 수있는 브랜치 (따라서 분기 예측 로직을 무효화)에 적합하지 않습니다. 즉, 이론적 인 곱셈과 비교의 비용보다 훨씬 많은 것은 곱셈을 더 빠르게 만듭니다.

그러나 사용자 지정 하드웨어를 구축하여 정렬을 수행 할 수 있다면 일 수도 있고 일 수 있습니다.

+0

특수 기수 정렬은 매우 빠르고 분기가 없습니다. – Dolphin

1

언급 할 가치가있는 한 가지 사실은 CPU의 곱셈 명령어가 느린 (또는 존재하지 않는 ...) 경우에도 조회 테이블을 사용하여 더욱 빠르게 처리 할 수 ​​있다는 것입니다.

+0

7- 카드 평가 기는 가능한 한 7 개의 카드 (약 130,000,000 개의 조합)의 모든 단일 조합에 대한 손 등급을 나타내는 트리를로드하기 위해 정확히 이것을 수행합니다. 그것은 초고속이지만 수백 메가의 RAM과 초기화에 드는로드 시간이 필요합니다. – stinky472

1

많은 생각을 한 후, 나는 소수를 사용하는 브레인 스톰을 가졌습니다. 나는 각 카드 랭크에 소수 값을 할당 할 것이다 ...이 시스템의 장점은 각 카드 랭킹의 소수를 곱하면 주문에 관계없이 고유 한 제품을 얻을 수 있다는 것이다. 다섯 장의 카드 중 하나.

이것은 비 위치 시스템의 예입니다.

이론에 대한 링크를 찾을 수 없습니다. 적용 할 수있는 대수학의 일부로, 오일러의 전체 및 암호화 주변을 연구했습니다. (나는 모국어로 모든 것을 공부 했으므로 용어로 잘못 될 수 있습니다.)

우리가 그의 표현 (카드를 4 바이트 정수로)을 유지한다면 어떻게 될까요? 5 개의 정수 배열을 곱하는 것보다 빠르게 정렬 할 수 있습니까?

RAM은 외부 리소스이므로 일반적으로 CPU에 비해 ​​느립니다. int 중 5 개를 정렬하면 스왑 작업으로 인해 RAM으로 이동해야합니다. 함수 자체를 정렬하는 오버 헤드를 여기에 추가하면 곱셈이 그다지 나쁘지 않게 보입니다.

CPU를 RAM에 연결하는 버스가 하나 밖에 없지만 여러 개의 곱셈이 동시에 다른 ALU에서 실행될 수 있으므로 정수 곱셈은 정렬보다 훨씬 빠를 것이라고 생각합니다.

그렇지 않은 경우 적은 수의 요소를 빠르게 정렬하기 위해 어떤 수준의 최적화를 수행 할 수 있습니까? 잘 최적화 된 버블 정렬은 D-캐시에서 완전히 작동합니다 동안 (재귀) 더 많은 메모리를 사용합니다 qsort가 :

5 정수는 bubble sort를 사용하여 매우 신속하게 정렬 할 수 있습니다.

+2

"5 개의 int를 정렬하면 스왑 작업으로 인해 항상 RAM으로 이동해야합니다." - 어떤 종류의 CPU를 사용하고 있습니까? 5 개의 32 비트 레지스터 만 있습니까? 말할 필요도없이 레지스터에서 다음 단계는 RAM이 아닌 L1 캐시입니다. –

+0

@Nick, 어떤 컴파일러도 가변 길이를 가진 배열을 레지스터에 캐시하려고하지 않습니다. 고정 배열 길이의 사용자 정의 함수가 사용되지 않는 한. 그리고 예, 저는 기억 조직의 세부 사항을 약간 닦았습니다. 캐시 라인 정렬/i $/d $/L1/L2/L3/TLB/분기 예측/캐시 쓰기 쓰루 기능을 사용하면 설명이 불필요하게 복잡해집니다. 일반적인 규칙이 적용됩니다. 값 비싼 리소스를 사용하지 않아도됩니다. 그리고 기억은 비쌉니다. 아주 작은 배열 길이에서, qsort()의 재귀 메모리 오버 헤드는 단순히 이분법의 이점을 능가합니다. – Dummy00001

+0

아무도 배열과 일반적인 정렬 알고리즘을 사용해야한다고 말한 사람은 없습니다. 실제로 여러 사람들이 네트워크 정렬을 제안했습니다. 그리고 제가 말했듯이, 다음 레벨은 RAM이 아닌 캐시입니다. –

0

다른 사람들이 지적한 것처럼, 정렬만으로 5 값을 곱하는 것보다 빠르지는 않습니다. 그러나 이것은 그의 해결책의 나머지 부분을 무시합니다. 5 요소 정렬을 경멸 한 후에, 4888 값의 배열에 대해 2 진 검색을 수행합니다. - 적어도 12 개의 비교가 필요합니다.

정렬과 관련된 더 나은 솔루션이 있다는 것을 말하는 것은 아닙니다. 개인적으로 충분한 생각을주지 않았기 때문에, 그 정렬만으로 문제의 일부일뿐입니다.

그는 또한 소수를 사용할 필요가 없었습니다. 그가 각 카드의 값을 4 비트로 간단하게 인코딩했다면 손을 표현하기 위해 20 비트가 필요하며 0에서 2^20 = 1048576의 범위가 주어지며 소수를 사용하여 생성 된 범위의 약 1/100이며 충분히 작습니다 (여전히 캐시 일관성 문제를 겪고 있지만) 조회 테이블을 생성합니다.

물론 재미있는 변형은 Texas Holdem과 같은 게임에서 발견되는 것과 같이 7 장의 카드를 가져 와서 만들 수있는 최상의 5 카드 손을 찾는 것입니다.

+0

고정 된 단계 수의 2 진 검색은 비교할 필요가 없습니다. 결과를 산출하는 산술 및 비트 연산으로 완전히 수행 될 수 있습니다. 단계 수 (검색 할 표의 크기)가 고정되어 있지 않으면 반복 당 하나의 (매우 예측 가능한) 조건부 점프 인 루프/카운터가 필요하지만 그렇지 않은 경우 동일한 산술 방식이 계속 적용됩니다. –

+0

@ R 뭐라고 말할까요? 현재 노드의 값을 각 단계에서 검색하는 값과 비교해야합니다. 그걸 최적화하는 방법이 있다면, 나는 그것을 듣고 싶습니다. –

+0

비교 결과에 근거한 조건부 점프를 의미합니다. 대신 각 단계에서 현재 노드를 업데이트하기 위해 산술 (부호 확장을 사용)을 수행하면 점프가 발생하지 않고 단계 당 몇 개의 연산 코드 만 나오게됩니다. –

0

곱셈이 빠릅니다.

곱하기 결과를 의미있는 결과로 가정하면 주어진 배열의 곱셈은 항상 배열을 정렬하는 것보다 빠를 것입니다. 코드는 포커 핸드를 평가하도록 설계되었으므로 관련이 없습니다. 어쨌든 정렬 된 집합에 대한 조회.

0

준비가 완료된 텍사스 홀덤 7 및 5 카드 평가 기의 예로는 here과 설명서가 있으며 here에 대해 자세히 설명합니다. 모든 의견은 전자 메일 주소에서 환영합니다.

정렬 할 필요가 없으며 일반적으로 (7 %의 손을 계산할 때) 단지 6 회의 추가 및 2 비트 시프트로 빠져 나갈 수 있습니다 (~ 97 %). algo는 약 9MB의 RAM을 차지하고 거의 즉석에서 생성 된 생성 된 룩업 테이블을 사용합니다. 싼. 이 모든 작업은 32 비트 내부에서 이루어지며, 7- 카드 평가 기의 "인라이닝"은 랩톱에서 약 50m 무작위로 생성 된 손을 평가하는 데 유용합니다.

아, 곱셈은 정렬보다 빠릅니다.

관련 문제