addl $0x1,0xXXXXXXXX 코드의 사용중에 결과가 이상하게 나옵니다...

criny333의 이미지

테스트 환경 :
머신 - x86_64 머신(AMD A8-4555M APU quad-core) (가상머신이 아닙니다)
OS - linux 3.13.0 32bit (kubuntu 14.04 32bit)

일단 소스는 다음과 같습니다.

#include <stdio.h>
#include <pthread.h>
#define NUM_THREAD	100
 
void * thread_inc(void * arg);
void * thread_des(void * arg);
 
int num=0;
 
int main(int argc, char *argv[]) 
{
	pthread_t thread_id[NUM_THREAD];
	int i;
 
	for(i=0; i<NUM_THREAD; i++)
	{
		if(i%2)
			pthread_create(&(thread_id[i]), NULL, thread_inc, NULL);
		else
			pthread_create(&(thread_id[i]), NULL, thread_des, NULL);	
	}	
 
	for(i=0; i<NUM_THREAD; i++)
		pthread_join(thread_id[i], NULL);
 
	printf("result: %d \n", num);
	return 0;
}
 
void * thread_inc(void * arg) 
{
	int i;
	for(i=0; i<100000; i++)
		num+=1; <--- (1)
	return NULL;
}
void * thread_des(void * arg)
{
	int i;
	for(i=0; i<100000; i++)
		num-=1; <--- (2)
	return NULL;
}

일단 코드를 간략하게 설명하자면, 0값이 할당된 전역변수 num을 ++시키는 pthread 50개, --시키는 phread 50개를 만든 후, 모든 스레드의 종료를 기다린 후 최종적으로 num값을 출력 하는 프로그램입니다.
당연히 num을 더하는 부분에 동기화를 해주지 않았기에 최종적인 값은 0이 아닌 다른 값이 나옵니다.

저 코드를 동기화 시키지 않고 0값이 나오게 하기 위해서, 위에서 화살표 표시한 (1), (2)번 부분의 x86 코드를 메모리값을 바로 연산하여 메모리에 저장하는 x86코드로 바꾸는 방법 시도했습니다.

즉, (1)번 코드의 경우, 아래의 코드를

804862f:  a1 2c a0 04 08    mov    0x804a02c,%eax
8048634: 83 c0 01              add    $0x1,%eax
8048637: a3 2c a0 04 08    mov    %eax,0x804a02c
( 0x804a02c 는 num전역변수 주소 )

다음과 같이 변경하였습니다.

804862f: 83 05 2c a0 04 08 01  addl   $0x1,0x804a02c
8048636: 90                               nop
8048637: 90                               nop
8048638: 90                               nop
8048639: 90                               nop
804863a: 90                               nop
804863b: 90                               nop
( 0x804a02c 는 num전역변수 주소 )

(2)번 코드의 경우, 아래을 코드를

804865f: a1 2c a0 04 08        mov    0x804a02c,%eax
8048664: 83 e8 01                 sub    $0x1,%eax
8048667: a3 2c a0 04 08        mov    %eax,0x804a02c
( 0x804a02c 는 num전역변수 주소 )

다음과 같이 변경하였습니다.

804865f: 83 2d 2c a0 04 08 01  subl   $0x1,0x804a02c
8048666: 90                               nop
8048667: 90                               nop
8048668: 90                               nop
8048669: 90                               nop
804866a: 90                               nop
804866b: 90                               nop
( 0x804a02c 는 num전역변수 주소 )

이렇게 실행파일의 코드를 고친후 테스트 해보면, 0이라는 결과값이 나오지 않습니다.
메모리값을 직접 add하는 코드의 경우 cpu자체적으로 코드를 쪼개서 실행해서 그런건가요...케시관련 문제인가요...아니면 파이프라인 문제인가요?
도무지 감이 안잡힙니다...

gilgil의 이미지

메모리 증가도 되는군요. 제가 잘못 알고 있었습니다. (-.-)(_._)(-.-)

gilgil의 이미지

1. op code 수행은 atomic한 것으로 알고 있는데...
2. 다른 thread라 하더라도 메모리는 공유되기 때문에 쫑날 일은 없을 것 같은데...
이상하네요.

익명 사용자의 이미지

메모리에서 load -> ALU에 값 전달 -> ALU가 전달된 값 계산 -> 메모리에 store

이 정도면 답변이 됬을까요...

criny333의 이미지

아니요...
제가 지식이 많이 모자라서 그렇게 너무 함축적으로 설명해주시면...ㅜㅜ

코어간에 동기화 문제인가요...?
하나 이상의 코어에서 동시에 같은 메모리 주소를 엑세스시에 머신 자체적으로 동기화 되지 않고, 코어사이 메세지 통신이나 다른 방법을 통해서 따로 동기화를 해줘야 하는건가요....?

익명 사용자의 이미지

메모리에 직접 쓴다고 했지만 결국 1을 증가시키거나 감소시키기 위해서는

ALU에 메모리에 저장된 값을 읽어와서 전달하게 됩니다.

그다음 ALU에서 계산을 하고 다시 메모리에 저장을 하겠죠

즉, 이 부분에서도 메모리에 쓰이기전까지 일시적인 값 저장이 생긴다는 것입니다.

좀 쉽게 예를 들자면

thread1이 ALU에서 계산하는 단계 즉, 메모리에 저장하기 전 일때

thread2가 메모리에 있는 데이터를 읽어와 ALU에 전달하게 되면

thread2의 ALU에 전달되는 값은 thread1이 계산하기 전 값이 되겟죠

criny333의 이미지


//

익명 사용자의 이미지

결국은 알고 계시는 동기화 문제와 같은 맥락입니다.

일시적으로 값이 저장되기 때문에 생기는 것이죠.

위에 mov add ..등등은 레지스터에 일시적으로 저장이 되는 것이고

addl subl은 alu에 일시적으로 저장되기 때문이라고 생각하시면 됩니다

criny333의 이미지

자세한 답변 감사합니다... 코어간 동기화 메커니즘 그래서 필요한 거군요...
한번 더 자세하게 질문드리자면,

질문1) 좀더 확실하게 질문드리면 "addl $0x1,0x804a02c" 라는 메모리 다루는 코드 1개를 "코어1(스레드1)"이 수행 완료하는 것을 다른 코어(스레드)들이 기다려 주지 않는다는 말인가요?

질문2)
제가 알기로 x86에서 머신코드는 다시 cpu내에서 atomic한 명령어로 쪼개져서 수행되는 걸로 알고있습니다.
그렇다면 결국
addl $0x1,0x804a02c
이 코드와
mov 0x804a02c,%eax
add $0x1,%eax
mov %eax,0x804a02c
이 코드는 fetch해온 후 명령어 해석한 뒤, atomic 한 명령어의 수행은 거의 같다는 말씀인가요?
(물론 전자의 경우 eax가 아닌 임시레지스터를 이용할것이라고 예상이 됩니다만...)

질문3)
그렇다면 코어 하나에서는 올바른 결과값이 나오는 것인가요?
싱글코어 컴퓨터가 없어서 일단은, 가상머신으로 테스트해보니 전자의 코드로 올바른 결과값이 나옵니다만....
코어1개일 경우에도 하이퍼스레드를 지원하는 코어의 경우에는 또 다른 경우가 생길려나요...

bacon의 이미지

x86/x86_64에 lock prefix가 있습니다. 예를 들어,

lock addl $0x1, num
lock subl $0x1, num
criny333의 이미지

아... lock prefix를 깜빡하고 있었습니다... 답변감사합니다...

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.