2009-06-13 2 views
10

소비자 스레드와 제작자 스레드로 프로그램을 작성하고 있습니다. 이제는 큐 동기화가 프로그램의 큰 오버 헤드로 보이고 일부 잠금 대기열 구현을 찾았지만 Lamport의 버전과 개선 된 버전 만 발견했습니다. PPoPP '08 :C에서 단일 소비자 단일 제작자 잠금 장치 사용 가능 큐 구현?

enqueue_nonblock(data) { 
    if (NULL != buffer[head]) { 
     return EWOULDBLOCK; 
    } 
    buffer[head] = data; 
    head = NEXT(head); 
    return 0; 
} 

dequeue_nonblock(data) { 
    data = buffer[tail]; 
    if (NULL == data) { 
     return EWOULDBLOCK; 
    } 
    buffer[tail] = NULL; 
    tail = NEXT(tail); 
    return 0; 
} 

는 두 버전 모두 동적으로 공간을 할당으로 malloc()를 사용하는 단일 소비자 단일 생산자 잠금없는 큐 구현이 내 질문 즉, 데이터에 대한 사전 할당 된 배열을 필요로 ?

또 다른 관련 질문은 대기열 동기화의 정확한 오버 헤드를 어떻게 측정 할 수 있습니까? pthread_mutex_lock() 등의 시간이 얼마나 걸리는지 등.

답변

6

성능이 걱정되면 malloc()을 믹스에 추가하면 도움이되지 않습니다. 그리고 성능에 대해 걱정하지 않는다면 뮤텍스를 통해 대기열에 대한 액세스를 단순히 제어하지 않는 것이 어떻습니까? 그러한 구현의 성능을 실제로 측정 했습니까? 조기 최적화의 길을 걷고있는 것처럼 들립니다.

+0

malloc point에 동의하지만 mutex에는 동의하지 않습니다. 죽여라. 따라서 한 명의 제작자와 한 명의 소비자가 무료로 작품을 잠그고 이것을 사용해야합니다. 이제이 소비자는 샤딩 논리를 적용하여 다른 소비자에게 데이터를 전달할 수 있습니다. 자물쇠가 죽어. – siddhusingh

4

표시되는 알고리즘은 두 스레드가 리소스 (즉, 대기열)를 공유하지만 매우 특정한 방식으로 공유하기 때문에 작동합니다. 하나의 쓰레드 만이 큐의 head-index (생산자)를 변경하고 tail 인덱스 (소비자)의 모든 쓰레드 만 변경하기 때문에 공유 객체의 일관성없는 상태를 얻을 수 없습니다. 생산자가 머리 인덱스를 업데이트하기 전에 에 실제 데이터를 넣는 것이 중요하며 소비자는 앞에 테일 인덱스를 업데이트하기 전에 원하는 데이터를 읽는 것이 중요합니다.

b/c와 마찬가지로 작동하지만 배열은 매우 정적입니다. 두 스레드는 거기에있는 요소의 저장 영역에 포함될 수 있습니다. 배열을 완전히 대체 할 수는 없지만 일 수 있습니다. 배열의 용도가 변경됩니다.

즉, 배열에 데이터를 유지하는 대신 데이터 포인터를 유지하는 데 사용하십시오. 그런 다음 배열을 통해 스레드간에 참조 (포인터)를 전달하면서 데이터 항목을 malloc() 및 free() 할 수 있습니다.

또한 실제 정밀도는 시스템에 따라 다르지만 posix는 나노초 클럭 판독을 지원합니다. 이 고해상도 시계를 앞뒤로 읽고 그냥 빼기 만하면됩니다.

+4

확실히 알고리즘에 메모리 장벽이 추가되어야합니까? – bdonlan

+1

그렇습니다. 그는 말합니다 "헤드 인덱스를 업데이트하기 전에 생산자가 실제 데이터를 입력하고 꼬리 색인을 업데이트하기 전에 소비자가 원하는 데이터를 읽는 것이 중요합니다. " – ben

+1

@bdonlan : (et et 알) 그렇지 않습니다. 그것은 운영의 순서와 단일 생산자, 단일 소비자의 사실을 전제로합니다. 그런 상황에서는 괜찮습니다. – JustJeff

2

몇 년 전에 재미있어 보였던 것을 보았습니다. 지금은 찾을 수 없지만. : 제안 된 잠금없는 구현에는 CAS primitive이 필요하지만 잠금 구현 (심지어 CAS 프리미티브를 사용하지 않으려는 경우)은 성능이 매우 뛰어났습니다. 잠금은 여러 독자 또는 여러 생산자가 동시에 대기열을 때리지 않고 생산자는 소비자와 경쟁하지 않았습니다.

대기열의 기본 개념은 항상 하나의 여분의 "빈"노드가있는 연결 목록을 만드는 것입니다. 이 여분의 노드는 목록의 머리와 꼬리 포인터가 목록이 비어있을 때만 동일한 데이터를 참조한다는 것을 의미했습니다. 나는 그 논문을 찾을 수 있었으면 좋겠다. 나는 알고리즘에 대한 설명을하고 있지 않다. ..

AH-ha!

나는 the algorithm without the remainder of the article을 전사 한 사람을 발견했습니다. 이것은 유용한 출발점이 될 수 있습니다.

+0

가장 주목할만한 것은 그 URL에있는 작은 글씨를 읽고 ("powerpc"로 검색) 자신의 lock-free 구조를 발명하기 시작할 때 명심하십시오. –

+0

당신이주는 설명은 Michael과 Scotts의 작품입니다 - 그리고 나는 위의 코멘트에서 실제로이 작품임을 알 수 있습니다. 그곳에있는 코드는 종이에서 직접 가져온 것입니다. 더미 노드의 아이디어는 사실 Valois에서 나왔습니다. –

2

나는 매우 간단한 대기열 구현으로 작업하여 대부분의 기준을 충족시킵니다. 정적 최대 크기의 바이트 풀을 사용한 다음 그 안에 메시지를 구현했습니다. 하나의 프로세스가 이동할 헤드 포인터와 다른 프로세스가 이동할 테일 포인터가있었습니다.

잠금은 여전히 ​​필요하지만 우리는 시스템 호출과 관련이 없으므로 매우 가벼운 Peterson's 2-Processor Algorithm을 사용했습니다. 잠금 장치는 매우 작고 잘 정돈 된 영역에만 필요합니다. 최대 몇 개의 CPU 사이클이 필요하므로 오래 동안 블록하지 마십시오.

1

할당자가 성능 문제가 될 수 있다고 생각합니다. 해제 된 블록을 유지하기 위해 링크드리스트를 사용하는 커스텀 멀티 스레드 메모리 할당자를 사용할 수 있습니다. 귀하의 블록이 (거의) 같은 크기가 아니라면 "버디 시스템 메모리 할당 자"를 구현할 수 있습니다. 마녀는 매우 빠릅니다. 대기열 (링 버퍼)을 뮤텍스와 동기화해야합니다.

동기화가 너무 많이 발생하지 않도록 각 액세스마다 대기열에 값을 쓰거나 읽을 수 있습니다.

잠금없는 알고리즘을 계속 사용하려면 사전 할당 된 데이터를 사용하거나 잠금이 필요없는 할당기를 사용해야합니다. 예 Circular lock-free buffer

3

: 잠금이없는 할당 "확장 잠금 - 무료 동적 메모리 할당"에 관한 논문 및 잠금이없는 물건을 시작하기 전에 구현 Streamflow

가있다 , 봐.

많은 수의 잠금없는 다중 판독기 다중 기록기 대기열이 있습니다.

필자는 1996 년 논문에서 마이클과 스콧이 구현했습니다.

나는이 큐를 포함 할 잠김없는 데이터 구조 (C 언어)의 작은 라이브러리를 발표 할 예정이다.

+0

1. 성능을 죽이는 경향이있는 malloc 노드를 사용합니다. 2.이 알고리즘은 CAS를 사용합니다. CAS는 메모리에 LOCK을 걸기 때문에 위의 것보다 열등합니다. 실제로 잠김이 거의 일어나지 않는 경우 (예 : 빠른 잠금) CAS == 다중 코어의 SpinLock. 그래도보고 싶습니다. – ben

+0

OP는 malloc을 요구합니다. 라이브러리가 여기에 있습니다. http://www.liblfds.org –

1

malloc을 추가하면 성능을 향상시킬 수 있고 잠금 기반 구조도 효과적입니다. 이는 malloc이 힙에 대해 일종의 CAS 잠금을 필요로하기 때문에 malloc의 일부 형식이 자체 잠금을 갖기 때문에 메모리 관리자에서 잠글 수 있습니다.

은 당신이 확장 된 경우 잠글 필요가 확장 배열의 형태를 만들 수있는 모든 노드를 할당하고 다른 큐를 관리 미리 할 필요가 malloc에 ​​...

주를 사용합니다.

또한 CPU에서 잠금이 해제되어있는 동안 명령이 실행되는 동안 메모리 잠금 및 블록 메모리를 수행하고 종종 파이프 라인을 정지시킵니다.

3

FastFlow 라이브러리를보아야합니다.