아토믹 증가 예제코드. 8개 쓰레드에서 정확히 아토믹처리 해준다.
빌드할 때는 GCC 특화 기능이므로 gcc -o atom2 atom2.c -lpthread -D_GNU_SOURCE
와 같이 한다.
내용을 보면 CPU_SET이 있는데… CPU를 타겟팅한다. 보통 커널이 알아서 나눠주는 것 같다.
만약 CPU_SET을 1로 지정하면 어떻게 될까? 더 빠르다.
CPU를 8개 다 쓰기 vs CPU 1개 다 쓰기를 time으로 재면, 13초 vs 4.5초가 나온다.
그러니까 CPU 1개만 쓸 때가 더 빠르다. 여기서 스핀락은 없다.
아래는 예전에 쓴 내용인데 틀렸다. NUMA는 소켓단위 CPU 분할일때만 동작한다고. 아무튼 CPU 단위 L2의 캐시 어긋남 때문인 것 같긴 하다.
이유가 뭘까? 아마도 NUMA 탓일것이다. NUMA에서는 CPU마다 각각 부여된 주소를 갖고 있는 상황이다. 그런데 변수는 한 개고 주인 CPU도 한 개이다. 주인이 아닌 CPU가 해당 메모리에 접근하려면 몇 턴 더 거쳐야한다. 아마도 L2 캐시가 매번 깨질지도 모른다. (정확히는 모르겠다. 잘 모르겠다..)
sched_setaffinity 는 CPU를 타겟팅한다.
__sync_fetch_and_add 는 gcc atomic 기능으로서 크리티컬 섹션 구현하지 않아도 된다. 해당 부분 주석처리하고 그냥 inc로 바꾸면 예상 값이 틀어진다. 동기화가 어긋나기 때문이다.
– fork로 2개 프로세스에서 tmp / mmap 태우고 하니까 3.5초 걸린다.
#define _GNU_SOURCE 1
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#include <sched.h>
#include <linux/unistd.h>
#include <sys/syscall.h>
#include <errno.h>
#define INC_TO 100000000 // one million...
int global_int = 0;
pid_t gettid( void )
{
return syscall( __NR_gettid );
}
void *thread_routine( void *arg )
{
int i;
int proc_num = (int)(long)arg;
cpu_set_t set;
CPU_ZERO( &set );
CPU_SET( proc_num, &set );
// CPU_SET( 1, &set );
if (sched_setaffinity( gettid(), sizeof( cpu_set_t ), &set ))
{
perror( "sched_setaffinity" );
return NULL;
}
for (i = 0; i < INC_TO; i++)
{
// global_int++;
__sync_fetch_and_add( &global_int, 1 );
}
return NULL;
}
int main()
{
int procs = 0;
int i;
pthread_t *thrs;
// Getting number of CPUs
procs = (int)sysconf( _SC_NPROCESSORS_ONLN );
if (procs < 0)
{
perror( "sysconf" );
return -1;
}
thrs = malloc( sizeof( pthread_t ) * procs );
if (thrs == NULL)
{
perror( "malloc" );
return -1;
}
printf( "Starting %d threads...\n", procs );
for (i = 0; i < procs; i++)
{
if (pthread_create( &thrs[i], NULL, thread_routine,
(void *)(long)i ))
{
perror( "pthread_create" );
procs = i;
break;
}
}
for (i = 0; i < procs; i++)
pthread_join( thrs[i], NULL );
free( thrs );
printf( "After doing all the math, global_int value is: %d\n",
global_int );
printf( "Expected value is: %d\n", INC_TO * procs );
return 0;
}