<?xml version="1.0" encoding="utf-8" ?>
<?xml-stylesheet href="http://rss.egloos.com/style/blog.xsl" type="text/xsl" media="screen"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
<channel>
	<title>The truth will set you free</title>
	<link>http://sunyzero.egloos.com</link>
	<description>There are three kinds of lies: lies, damned lies, and statistics - Benjamin Disraeli (British Prime Minister)</description>
	<language>ko</language>
	<pubDate>Tue, 24 Nov 2009 06:26:44 GMT</pubDate>
	<generator>Egloos</generator>
	<image>
		<title>The truth will set you free</title>
		<url>http://pds8.egloos.com/logo/200806/09/55/b0026155.jpg</url>
		<link>http://sunyzero.egloos.com</link>
		<width>80</width>
		<height>53</height>
		<description>There are three kinds of lies: lies, damned lies, and statistics - Benjamin Disraeli (British Prime Minister)</description>
	</image>
  	<item>
		<title><![CDATA[ ANSI color로 리눅스 프롬프트를 예쁘게... ]]> </title>
		<link>http://sunyzero.egloos.com/4282610</link>
		<guid>http://sunyzero.egloos.com/4282610</guid>
		<description>
			<![CDATA[ 
  <strong>* 목차</strong><br />
1. ANSI color<br />
1.1 ANSI escape code<br />
1.2 ANSI color 예제<br />
2. Linux prompt<br />
2.1 프롬프트 변수<br />
2.2 프롬프트에 색상 넣기<br />
<hr><br />
<strong><span style="FONT-SIZE: 120%">1. ANSI color</span></strong><br />
ANSI 컬러는 ANSI escape code의 기능중 하나이다. 원래 ANSI escape code는 터미널의 텍스트 포맷을 제어하기 위해서 만들어진 코드이며 현재 ISO/IEC-6429 표준으로 제정되어있다. 따라서 대부분의 UNIX, Linux, DOS, Windows 등은 터미널에서 ANSI escape code를 지원한다.(일부 기능은 지원하지 않을 수도 있다.)<br />
<br />
여기서는 리눅스(or 유닉스 계열)의 사용자를 기준으로 설명할 것이며 DOS/Windows 사용자들은 어차피 이 기능을 잘 안쓰므로 알아봐야 별 의미가 없을수도 있다.(즉 윈도우 사용자들에게는 별 도움이 안되는 글이다.)<br />
<br />
<strong>1.1 ANSI escape code</strong><br />
ANSI escape code를 사용하기 위해서는 ANSI escape code를 지원하는 터미널이 필요하다.<br />
그러나 안심하라. 현재 대부분의 리눅스/유닉스의 터미널은 ANSI escape code를 지원한다.<br />
(지원하지 않는 기능을 굳이 설명할 이유도 없는것 아닌가?)<br />
<br />
이제 터미널을 하나 열고 echo -e "\e[33m"를 타이핑 해보자.<br />
<pre>[sunyzero@dev1 ~]$ echo -e "\e[33m"<br />
<span style="COLOR: #ffff00">$[sunyzero@dev1 ~]$ </span></pre>echo 문이 실행된 후에는 프롬프트 색상이 모두 노란색으로 바뀌어 있음을 알 수 있다. 이 후 타이핑하는 모든 글자는 다 노랗게 보일 것이다. 원상복귀를 위해 echo -e "\e[m"명령을 타이핑 해두자.<br />
<br />
앞서 예제의 "\e[33m"과 같은 형태를 ANSI escape sequence라고 부르는데, echo문에서 이를 출력하여 실행하려면 ANSI escape code를 인식하는 옵션인 -e를 같이 넣어줘야 한다. 넣지 않으면 그냥 문자열로 인식해서 단순출력해버린다.<br />
<br />
또한 ANSI escape sequence에서 시작문자인<strong>"\e["는 CSI(Control Sequence Introducer)라고 부르며</strong> 뒤의 33m, m 같은 것이 ANSI escape sequence의 코드 내용 부분이 되겠다.<br />
<br />
CSI의 \e는 ASCII코드의 ESC키를 의미하므로 ESC의 ASCII값인 8진수 033(=decimal 27)을 사용하여 \033으로 표기할 수도 있다. 따라서 위의 "\e[33m"는 "\033[33m"으로 써도 된다.<br />
<pre><strong>ANSI Escape sequence = <span style="COLOR: #0000ff">CSI</span> + n [;+ ...] + letter</strong><br />
                       <span style="COLOR: #0000ff">CSI = \e[ = \033[</span></pre>CSI 다음에 나오는 n은 숫자이며 복수개가 나올때는 세미콜론(;)으로 구분한다. 마지막의 영문자 1개는 ANSI escape sequence의 명령(command)에 해당한다. 예를 들어 T는 스크롤 다운, m은 색상 변경등의 기능을 가진다. 색상 변경은 SGR(Select Graphic Rendition)이라고 부른다.<br />
<br />
<strong>여기서는 ANSI Escape code 중 색상만 다루므로 m 명령만 다루도록 할 것이다.</strong> 그 외 명령어는 하단의 [1]의 링크를 참고하라.<br />
<br />
<br />
<br />
<strong>1.2 ANSI color 예제</strong><br />
ANSI의 색상에서 사용 가능한 색상 밑 기능 테이블은 다음과 같다. <sup>[1]</sup><br />
<table><caption>Color table</caption><tbody><tr><th>Intensity</th><th>0</th><th>1</th><th>2</th><th>3</th><th>4</th><th>5</th><th>6</th><th>7</th><th>9</th></tr><tr><th>Normal</th><td style="BACKGROUND: black; COLOR: white">Black</td><td style="BACKGROUND: maroon; COLOR: white">Red</td><td style="BACKGROUND: green; COLOR: white">Green</td><td style="BACKGROUND: olive; COLOR: white">Yellow</td><td style="BACKGROUND: navy; COLOR: white">Blue</td><td style="BACKGROUND: purple; COLOR: white">Magenta</td><td style="BACKGROUND: teal; COLOR: white">Cyan</td><td style="BACKGROUND: silver; COLOR: black">White</td><td>reset</td></tr><tr><th>Bright</th><td style="BACKGROUND: gray; COLOR: white">Black</td><td style="BACKGROUND: red; COLOR: black">Red</td><td style="BACKGROUND: lime; COLOR: black">Green</td><td style="BACKGROUND: yellow; COLOR: black">Yellow</td><td style="BACKGROUND: blue; COLOR: white">Blue</td><td style="BACKGROUND: fuchsia; COLOR: black">Magenta</td><td style="BACKGROUND: cyan; COLOR: black">Cyan</td><td style="BACKGROUND: white; COLOR: black">White</td></tr></tbody></table>색상은 0부터 7까지 사용가능한데, 그냥 사용하는게 아니라 뒤의 30, 40, 90, 100을 더해서 사용한다. 즉 normal yellow는 33이 된다. 40을 더해서 43을 만들면 Bright 옵션이 켜지게 된다. 그러면 이제 30~, 40~ 따위의 코드를 정리해보자.<br />
<table><caption><font size="+0">SGR (Select Graphic Rendition) parameters</font></caption><tbody><tr><th>Code</th><th>Effect</th><th>Note</th></tr><tr><td>0</td><td>Reset / Normal</td><td>all attributes off</td></tr><tr><td>1</td><td>Intensity: Bold</td><td></td></tr><tr><td>2</td><td>Intensity: Faint</td><td>not widely supported</td></tr><tr><td>3</td><td>Italic: on</td><td>not widely supported. Sometimes treated as inverse.</td></tr><tr><td>4</td><td>Underline: Single</td><td></td></tr><tr><td>5</td><td>Blink: Slow</td><td>less than 150 per minute</td></tr><tr><td>6</td><td>Blink: Rapid</td><td>MS-DOS ANSI.SYS; 150 per minute or more</td></tr><tr><td>7</td><td>Image: Negative</td><td>inverse or reverse; swap foreground and background</td></tr><tr><td>8</td><td>Conceal</td><td>not widely supported</td></tr><tr><td>21</td><td>Underline: Double</td><td>not widely supported</td></tr><tr><td>22</td><td>Intensity: Normal</td><td>not bold and not faint</td></tr><tr><td>24</td><td>Underline: None</td><td></td></tr><tr><td>25</td><td>Blink: off</td><td></td></tr><tr><td>27</td><td>Image: Positive</td><td></td></tr><tr><td>28</td><td>Reveal</td><td>conceal off</td></tr><tr><td>30–39</td><td>Set foreground color, normal intensity</td><td>3x, where x is from the color table above</td></tr><tr><td>40–49</td><td>Set background color, normal intensity</td><td>4x, where x is from the color table above</td></tr><tr><td>90–99</td><td>Set foreground color, high intensity</td><td>aixterm</td></tr><tr><td>100–109</td><td>set background color, high intensity</td><td>aixterm</td></tr></tbody></table>(위의 Table은 하단 레퍼런스에 있는 위키<a title="" href="http://en.wikipedia.org/wiki/ANSI_escape_code" target="_blank">[1]</a>에서 인용했다.)<br />
<br />
이제부터 예제를 보면서 실습하자. 가장 간단한 Hello world부터 시작해보자.<br />
<pre>[sunyzero@dev1 ~]$ echo -e "\e[31mHello World\e[m"<br />
<span style="COLOR: #800000">Hello World</span></pre>항상 마지막에 나오는 \e[m은 숫자가 생략되었으므로 \e[0m과 같다. 즉 Reset이다. 이렇게 하지 않으면 변경된 색상이 계속 적용되어 프롬프트 색이 이상하게 나오기 때문이다. 앞으로 ANSI 색상을 쓸 때는 마지막에 Reset을 넣는 것을 잊지말자. 그 외의 것은 설명할 것이 없으니 이번에는 여러 속성을 넣어본 것을 실습해보자.<br />
<pre>[sunyzero@dev1 ~]$ echo -e "\e[96mHello \e[0;4;93mWorld\e[m"<br />
<span style="COLOR: #00ffff">Hello</span><span style="COLOR: #ffff00"><u>World</u></span></pre>96에서 십자릿수의 90번대(90-99)는 위의 표에서 "90-99 Set foreground color, high intensity"라고 나온다. 일자릿수의 6번과 조합해보면 Bright Cyan색이 된다. 두번째 ANSI escape sequence는 0;4;93이 연달아 나오므로 0은 Reset, 즉 앞에서 설정된 bright cyan색을 우선 해제하고, 4는 underline, 93에서 십자릿수의 90번대(90-99)는 high inensity색상이며 그 중 3번은 Yellow계열이므로 진노랑색이 나온다.<br />
<br />
<strong>* 참고: high intensity 글자를 쓰기 위해서 90-99번대의 숫자를 사용하는 방법외에도 코드 1번(bold, intensity)를 넣어도 결과는 같다. 즉 93m이나 1;33m 은 같은 결과이므로 편한대로 쓰면 된다.</strong><br />
<br />
그렇다면 이번에는 배경색까지 지정하는 연습으로 high intensity yellow 배경에 normal green색 글씨로 Hellow world를 써보자.<br />
<div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds16.egloos.com/pds/200911/24/55/b0026155_4b0b529a9904d.png" width="475" height="42" onclick="Control.Modal.openDialog(this, event, 'http://pds16.egloos.com/pds/200911/24/55/b0026155_4b0b529a9904d.png');" /></div><br />
<br />
<br />
<br />
<strong><span style="FONT-SIZE: 120%">2. Linux prompt</span></strong><br />
리눅스(혹은 유닉스)의 bash 쉘에서는 PS1 환경변수를 바꾸면 현재 프롬프트를 변경할 수 있다.<br />
bash쉘 외에 ksh나 POSIX sh쉘등도 PS1 환경변수를 바꾸면 쉽게 프롬프트가 변경된다.<br />
<br />
환경변수를 설정하는 리눅스 명령은 export나 declare -x 를 사용하면 되는데, export가 더 많이 쓰이므로 여기 예제에서는 export명령을 기준으로 설명하겠다.<br />
<br />
<br />
<strong>2.1 프롬프트 변수</strong><br />
리눅스의 bash 쉘 프롬프트 기본값은 "[유저명@호스트명 현재디렉토리]$"의 형태로 되어있다.(root유저는 맨 끝의 프롬프트 문자가 #으로 나타남)<br />
이 중에 유저명은 \u이고 호스트명은 \h, 현재 작업디렉토리는 \W의 변수로 PS1 환경변수에 쓰여진다.<br />
따라서 기본값 PS1은 "[\u@\h \w]$ "가 된다. 그러면 어떤 변수들을 사용할 수 있는지부터 알아보자.<br />
<table><tbody><tr><th width="80">variable </th><th>note </th></tr><tr><td>\a</td><td style="TEXT-ALIGN: left">an ASCII bell character (07)</td></tr><tr><td>\d</td><td style="TEXT-ALIGN: left">the date in "Weekday Month Date" format (e.g., "Tue May 26")</td></tr><tr><td>\D{format}</td><td style="TEXT-ALIGN: left">the format is passed to strftime(3) and the result is inserted into the prompt string; an empty format results in a locale-specific time representation. The braces are required</td></tr><tr><td>\e</td><td style="TEXT-ALIGN: left">an ASCII escape character (033)</td></tr><tr><td>\h</td><td style="TEXT-ALIGN: left">the hostname up to the first ‘.’</td></tr><tr><td>\H</td><td style="TEXT-ALIGN: left">the hostname</td></tr><tr><td>\j</td><td style="TEXT-ALIGN: left">the number of jobs currently managed by the shell</td></tr><tr><td>\l</td><td style="TEXT-ALIGN: left">the basename of the shell’s terminal device name</td></tr><tr><td>\n</td><td style="TEXT-ALIGN: left">newline</td></tr><tr><td>\r</td><td style="TEXT-ALIGN: left">carriage return</td></tr><tr><td>\s</td><td style="TEXT-ALIGN: left">the name of the shell, the basename of $0 (the portion following the final slash)</td></tr><tr><td>\t</td><td style="TEXT-ALIGN: left">the current time in 24-hour HH:MM:SS format</td></tr><tr><td>\T</td><td style="TEXT-ALIGN: left">the current time in 12-hour HH:MM:SS format</td></tr><tr><td>\@</td><td style="TEXT-ALIGN: left">the current time in 12-hour am/pm format</td></tr><tr><td>\A</td><td style="TEXT-ALIGN: left">the current time in 24-hour HH:MM format</td></tr><tr><td>\u</td><td style="TEXT-ALIGN: left">the username of the current user</td></tr><tr><td>\v</td><td style="TEXT-ALIGN: left">the version of bash (e.g., 2.00)</td></tr><tr><td>\V</td><td style="TEXT-ALIGN: left">the release of bash, version + patch level (e.g., 2.00.0)</td></tr><tr><td>\w</td><td style="TEXT-ALIGN: left">the current working directory, with $HOME abbreviated with a tilde (uses the $PROMPT_DIRTRIM variable)</td></tr><tr><td>\W</td><td style="TEXT-ALIGN: left">the basename of the current working directory, with $HOME abbreviated with a tilde</td></tr><tr><td>\!</td><td style="TEXT-ALIGN: left">the history number of this command</td></tr><tr><td>\#</td><td style="TEXT-ALIGN: left">the command number of this command</td></tr><tr><td>\$</td><td style="TEXT-ALIGN: left">if the effective UID is 0, a #, otherwise a $</td></tr><tr><td>\nnn</td><td style="TEXT-ALIGN: left">the character corresponding to the octal number nnn</td></tr><tr><td>\\</td><td style="TEXT-ALIGN: left">a backslash</td></tr><tr><td>\[</td><td style="TEXT-ALIGN: left">begin a sequence of non-printing characters, which could be used to embed a terminal control sequence into the prompt</td></tr><tr><td>\]</td><td style="TEXT-ALIGN: left">end a sequence of non-printing characters</td></tr></tbody></table><span style="COLOR: #ff0000">(* 위 변수목록은 man bash의 PROMPTING 부분에서 인용함)</span><br />
위의 표에서 나온 것을 사용해서 유용한 프롬프트 예를 3가지 만들어보겠다. 시각편의성을 위해서 프롬프트는 red, 명령어 부분은 blue로 표기했다.<br />
<pre><span style="COLOR: #ff0000"><br />
[sunyzero@dev1 pub]$</span> export PS1="\u:\w $ "<br />
<span style="COLOR: #0000ff">sunyzero:/var/ftp/pub $</span> export PS1="[\u@\h] \w\n[\@ \$ "<br />
<span style="COLOR: #ff0000">[sunyzero@dev1] /var/ftp/pub<br />
[02:02 PM $</span> export PS1="[\D{%Y-%m-%d %H:%M:%S}] [\u@\h] \w\n\$ "<br />
<span style="COLOR: #0000ff">[2009-11-24 14:11:52] [sunyzero@dev1] /var/ftp/pub<br />
$</span></pre>첫번째는 아주 간결한 프롬프트로서 \u와 \w로 구성했다. 두번째는 2행짜리 프롬프트로서 첫째 행에는 유저,호스트,작업디렉토리를 배치하고 둘째 행에는 시간을 배치했다. 세번째도 2행짜리 프롬프트로서 모든 정보를 첫째 행에 배치하고 둘째 행을 넓게 쓰는게 특징이다.<br />
<br />
<br />
<br />
<strong>2.2 프롬프트에 색상 넣기</strong><br />
이제 앞의 프롬프트에 색상을 넣는 작업을 할 시간이다. 색상은 자신의 입맛에 넣으면 된다.<br />
필자는 다음과 같이 넣어보았다. 중간에 너무 길어서 \로 개행하고 다음라인에 타이핑했다.(맨끝의 <span style="COLOR: #ff0000">\</span>는 다음행 개행을 의미하며 다음행은 <span style="COLOR: #ff0000">&gt;</span>으로 시작한다.)<br />
<pre><strong>$ export PS1="\e[1;37m[\e[36m\D{%Y-%m-%d %H:%M:%S}\e[37m] <span style="COLOR: #ff0000">\<br />
&gt;</span> [\e[32m\u\e[31m@\e[33m\h\e[37m] \e[34m\w\e[m\n\$ "<br />
[<span style="COLOR: #00ffff">2009-11-24 14:58:04</span>] [<span style="COLOR: #008000">sunyzero</span><span style="COLOR: #800000">@</span><span style="COLOR: #ffff00">atom</span>] <span style="COLOR: #0000ff">/var/ftp/pub</span><br />
$</strong></pre><br />
이제 본인이 필요한 내용을 넣어서 본인만의 PS1을 만들면 된다.<br />
<br />
그리고 위의 PS1 설정 명령은 홈디렉토리에 있는 .bashrc 파일에 넣어두면 매번 로그인할때 자동으로 실행된다.(아래는 .bashrc의 예제이다.)<br />
<pre># .bashrc<br />
<br />
# Source global definitions<br />
if [ -f /etc/bashrc ]; then<br />
        . /etc/bashrc<br />
fi<br />
<br />
# User specific aliases and functions<br />
<span style="COLOR: #ff0000">export PS1="\e[1;37m[\e[36m\D{%Y-%m-%d %H:%M:%S}\e[37m] \<br />
[\e[32m\u\e[31m@\e[33m\h\e[37m] \e[34m\w\e[m\n\$ "</span></pre><br />
<br />
이상으로 허접한 PS1과 ANSI color에 대한 이야기를 마친다.<br />
<hr><br />
* Reference<br />
[1] <a title="" href="http://en.wikipedia.org/wiki/ANSI_escape_code" target="_blank">http://en.wikipedia.org/wiki/ANSI_escape_code</a><br/><br/>tag : <a href="/tag/linux" rel="tag">linux</a>,&nbsp;<a href="/tag/리눅스" rel="tag">리눅스</a>,&nbsp;<a href="/tag/쉘" rel="tag">쉘</a>,&nbsp;<a href="/tag/bash" rel="tag">bash</a>,&nbsp;<a href="/tag/ANSI" rel="tag">ANSI</a>,&nbsp;<a href="/tag/프롬프트" rel="tag">프롬프트</a>,&nbsp;<a href="/tag/prompt" rel="tag">prompt</a>			 ]]> 
		</description>
		<category>Com. Science</category>
		<category>linux</category>
		<category>리눅스</category>
		<category>쉘</category>
		<category>bash</category>
		<category>ANSI</category>
		<category>프롬프트</category>
		<category>prompt</category>

		<comments>http://sunyzero.egloos.com/4282610#comments</comments>
		<pubDate>Tue, 24 Nov 2009 06:26:29 GMT</pubDate>
		<dc:creator>SY Kim</dc:creator>
	</item>
	<item>
		<title><![CDATA[ Multi-Core Software - Intel technology journal ]]> </title>
		<link>http://sunyzero.egloos.com/4277348</link>
		<guid>http://sunyzero.egloos.com/4277348</guid>
		<description>
			<![CDATA[ 
  <strong>Multi-Core Software </strong><br />
<br />
http://www.intel.com/technology/itj/2007/v11i4/5-foundations/1-abstract.htm<br />
PDF version : http://download.intel.com/technology/itj/2007/v11i4/5-foundations/5-Foundations_for_Scalable_Multi-core_Software.pdf<br />
<br />
<div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds15.egloos.com/pds/200911/17/55/b0026155_4b0270fe2072a.jpg" width="500" height="394.642857143" onclick="Control.Modal.openDialog(this, event, 'http://pds15.egloos.com/pds/200911/17/55/b0026155_4b0270fe2072a.jpg');" /></div><br />
<br />
멀티 코어 프로그래밍(i.e. 멀티 쓰레드 프로그래밍)에 대한 개괄적인 방법, 주의점, TBB(Thread-Building-Block)에 대한 이야기가 나와있다. 멀티 쓰레드 프로그래밍에 대해서 한 번쯤 읽어볼만한 자료이다.<br />
<br />
PS. Intel과 일을 하다보니 인텔빠가 되어가는듯한 느낌이다. 예전에는 인텔의 마케팅부분에서의 각종 치팅행위때문에 별로 안좋아했는데... 사람이란 엮이다보면 자연스럽게 자기합리화를 위한 인지부조화가 일어나나보다. 지금은 인텔 좋아좋아라고 하고 있다. -_-;;; (사실 인텔이 읽을거리가 참 잘나오는 편이다. IBM과 Sun쪽 자료만 읽다가 Intel자료를 읽어보니 범용 데스크탑 기술도 상당히 서버쪽에 근접하고 있다는 느낌이 든다.)<br/><br/>tag : <a href="/tag/thread" rel="tag">thread</a>,&nbsp;<a href="/tag/intel" rel="tag">intel</a>,&nbsp;<a href="/tag/인텔" rel="tag">인텔</a>,&nbsp;<a href="/tag/프로그래밍" rel="tag">프로그래밍</a>			 ]]> 
		</description>
		<category>Com. Science</category>
		<category>thread</category>
		<category>intel</category>
		<category>인텔</category>
		<category>프로그래밍</category>

		<comments>http://sunyzero.egloos.com/4277348#comments</comments>
		<pubDate>Mon, 16 Nov 2009 09:11:37 GMT</pubDate>
		<dc:creator>SY Kim</dc:creator>
	</item>
	<item>
		<title><![CDATA[ C언어: OpenMP를 이용한 멀티 쓰레드 프로그래밍 HOWTO #4 ]]> </title>
		<link>http://sunyzero.egloos.com/4258873</link>
		<guid>http://sunyzero.egloos.com/4258873</guid>
		<description>
			<![CDATA[ 
  <strong><span style="COLOR: #ff6600; FONT-SIZE: 130%">C언어: OpenMP를 이용한 멀티 쓰레드 프로그래밍 HOWTO #4</span></strong><br />
<hr>- HOWTO #1 : <a href="http://sunyzero.egloos.com/4227785">http://sunyzero.egloos.com/4227785</a><br />
- HOWTO #2 : <a href="http://sunyzero.egloos.com/4229235">http://sunyzero.egloos.com/4229235</a><br />
- HOWTO #3 : <a href="http://sunyzero.egloos.com/4234766">http://sunyzero.egloos.com/4234766</a><br />
<hr><br />
<br />
<strong>9. critical &amp; atomic construct</strong><br />
<br />
critical construct와 atomic construct는 공유(shared) 영역을 보호하는 lock의 일종이다. 따라서 critical과 atomic을 사용한 영역은 쓰레드 중에 단 하나의 쓰레드만 진입하고 나머지는 대기하게 된다. 즉 직렬실행구간이 된다. 따라서 역으로 말하면 critical과 atomic을 남발하는 프로그래밍을 하면 성능이 확 떨어진다는 소리이다. 그럼에도 불구하고 꼭 lock이 필요한 기능이므로 잘 익혀두어야만 한다.<br />
<br />
우선 둘의 차이부터 이해하고 넘어가자. 만일 두 단어만 보고도 감이 오는 사람은 학생 때 운영체제 수업에서 critical section이나 atomic operation을 배운 사람일 것이다.<br />
<br />
• critical : 범용적인 lock<br />
• atomic : critical보다 가볍고 빠른 lock (critical의 special한 케이스)<br />
<br />
critical은 노멀한 lock이지만 atomic은 매우 가벼운 용도에만 사용한다. 가볍다는 것은 보통 1개의 스칼라 변수를 업데이트 하는 정도를 의미한다. 만일 변수 업데이트를 넘어서 계산이나 캐스팅, 복잡한 데이터 핸들링을 하는 경우라면 critical을 사용하도록 한다.<br />
<span style="COLOR: #3366ff">[참고로 정수형 변수 1개의 값을 변경하는 정도라면 atomic 없이 프로그래밍을 하는 것도 추천한다. atomic을 쓰지 않으면 수치상의 오류가 생기는 경우도 있겠지만 일반적으로는 10ppm(part per million) 이하인 경우이므로 무시할 수 있는 오차 일 수 있다. 왜냐하면 atomic의 가벼운 lock도 멀티 쓰레드에서는 성능을 많이 떨어뜨리기 때문이다.]</span><br />
<span style="color:#009900;">[보충 설명: 일반적으로 공유된 데이터에 복수개의 쓰레드가 접근할 경우에는 무조건 lock으로 보호해야만 한다. 모든 교과서나 매뉴얼에는 이렇게 나와있다. 그러나 프로그래머가 어느 정도 멀티 쓰레드에 대한 이해가 깊어지면 교과서나 매뉴얼을 응용할 수 있어야만 한다. 필자가 앞에서 정수형 변수 1개의 값을 업데이트하는 경우에는 lock을 쓰지 않는 것도 좋다고 했는데 이는 감히 교과서에는 쓸 수 없는 내용이다. 하지만 공유된 변수가 약간의 오차가 생겨도 괜찮은 경우, 즉 근접해나 근사치를 계산하는 시뮬레이션이나 휴리스틱 코드라면 atomic을 쓰지 않을 경우 더 빠르게 작동할 수 있다. 그리고 빠른 실행이 가능해졌기 때문에 시행횟수를 더 늘림으로서 오차를 보정할 수 있게된다. 그러므로 오차 보정이 가능한 수치계산이나 오차보다 속도가 더 중요한 문제라면 atomic을 생략하는 것도 고려하라는 뜻이 담겨있는 것이다. 그러나 주의할 점은 꼭 프로그래밍을 할 때 atomic을 선택적으로 적용할 수 있도록 코딩해두고 atomic을 사용한 버전과 사용하지 않은 버전의 차이를 꼭 비교해봐야 한다. 만일 atomic을 사용하지 않은 버전이 메리트가 없다면 반드시 lock을 사용한 버전을 택하는 것이 좋다.</span><br />
<br />
<br />
<strong>9.1 critical construct</strong><br />
<pre>#pragma omp critical [(lock_name)]</pre><br />
critical에서 락 이름(lock_name)을 생략하는 경우는 동일한 lock을 사용하는 것으로 간주되어 critical 지시어가 여러 번 나오더라도 동일하게 보호되는 구간이 된다. 자세한 것은 예제를 보면서 익히도록 하자. 예제로 아래와 같은 pseudo code가 있다.<br />
<pre>#pragma omp parallel for<br />
for (i=0; i&lt;MAX_ITERATION; i++) {<br />
    process_a();<br />
    chk_process();<br />
    process_b();<br />
    chk_process();<br />
    process_c();<br />
    chk_summary();<br />
}</pre>위의 예제에서 chk_process() 함수가 lock으로 보호되어야 한다면 critical 지시어를 사용하여 아래와 같이 할 수 있다.<br />
<pre>#pragma omp parallel for<br />
for (i=0; i&lt;MAX_ITERATION; i++) {<br />
    process_a();<br />
#pragma omp critical<br />
    chk_process();<br />
    process_b();<br />
#pragma omp critical<br />
    chk_process();<br />
    process_c();<br />
    chk_summary();<br />
}</pre>critical construct을 2군데에서 사용했지만 lock_name을 지정하지 않았기 때문에 같은 lock으로 인식하여 보호된다. [위와 같이 프로그래밍하지 않고 chk_process()함수 내부에 critical을 선언하여도 결과는 같다.]<br />
<br />
그렇다면 이번에는 chk_summary()에 critical을 추가해보겠다. 하지만 chk_process()와는 상관없이 다른 락으로 보호하도록 하겠다. 이 경우에는 필수적으로 lock_name을 사용해야 한다.<br />
<pre>#pragma omp parallel for<br />
for (i=0; i&lt;MAX_ITERATION; i++) {<br />
    process_a();<br />
#pragma omp critical (lock1)<br />
    chk_process();<br />
    process_b();<br />
#pragma omp critical (lock1)<br />
    chk_process();<br />
    process_c();<br />
#pragma omp critical (lock2)<br />
    chk_summary();<br />
}</pre>이제 critical의 사용방법에 대해서는 어느정도 감이 왔을 것이다. 하지만 거듭 강조하지만 왠만하면 lock은 최소한으로 사용하는 것이 좋다. 멀티 쓰레드 프로그래밍에서 lock을 통한 직렬구간을 많이 만들면 프로그래밍은 쉽지만 성능은 바닥을 치기 때문에 멀티 쓰레드 프로그래밍을하는 의미가 사라지게 된다.<br />
<br />
<br />
<br />
<strong>9.2 atomic construct</strong><br />
<pre>#pragma omp atomic</pre>atomic은 스칼라 변수 한개를 업데이트 한다고 했다. 너무 간단하기 때문에 간단한 예제 하나로 끝내겠다.<br />
<pre>#pragma omp parallel<br />
...생략...;<br />
#pragma omp atomic<br />
    n_count++;<br />
}</pre>atomic에서 쓸 수 있는 표현식은 ++나 --같은 unary operation이나 "변수=값"정도의 간단한 대입식 정도이다.<br />
<br />
<br />
<br />
<hr><br />
<strong>10. OpenMP API</strong><br />
앞에서 omp_get_thread_num() 같은 openmp의 함수들을 보았다. 이번에는 주로 사용되는 함수들을 정리해서 보도록 하겠다.<br />
<br />
* 쓰레드 풀에 관련된 함수들<br />
<span style="COLOR: #993399">void omp_set_num_threads(int num_threads); 병렬 구간에서 생성할 쓰레드 개수를 설정<br />
int omp_get_num_threads(void); 병렬 구간에서 생성할 쓰레드 개수를 리턴<br />
int omp_get_max_threads(void); 쓰레드 최대값 리턴<br />
int omp_get_thread_num(void); 현재 쓰레드 번호 리턴<br />
int omp_get_num_procs(void); 물리적 프로세서의 개수 리턴<br />
<br />
int omp_in_parallel(void); 병렬 구간일 경우 true(0이 아닌값), 아니면 false(0)을 리턴<br />
<br />
void omp_set_dynamic(int dynamic_threads); 생성할 쓰레드 개수를 동적으로 조정할 지 여부 (dynamic_threads가 1이면 true, 0이면 false)<br />
int omp_get_dynamic(void); 위의 omp_set_dynamic의 설정을 리턴</span><br />
<br />
아래 예제처럼 omp_set_dynamic이 설정되면 사용량에 따라서 쓰레드의 개수가 10개를 넘지 않는 범위에서 적당하게 조정된다.<pre>#include &lt;omp.h&gt;<br />
int main()<br />
{<br />
    omp_set_dynamic(1);<br />
#pragma omp parallel num_threads(10)<br />
{<br />
    /* do work here */<br />
}<br />
    return 0;<br />
}</pre>예제 출처: OpenMP 2.5 Specification<br />
<br />
<hr><br />
<strong>* OpenMP에서 제공하는 lock 기능 API : 정교한 모델을 만들때 유용하므로 꼭 읽어두자.</strong><br />
<span style="COLOR: #3366ff"><br />
void omp_set_nested(int nested); nested가 1이면 병렬구간의 중첩을 허용, 0이면 허용하지 않음<br />
int omp_get_nested(void); 위의 nested 설정을 리턴<br />
<br />
void omp_init_lock(omp_lock_t *lock); OMP의 lock을 초기화<br />
void omp_init_nest_lock(omp_nest_lock_t *lock); 중첩 lock버전의 위와 같은 기능의 함수 (=재귀잠금이 가능한 lock)<br />
<br />
void omp_destroy_lock(omp_lock_t *lock); OMP의 lock을 제거<br />
void omp_destroy_nest_lock(omp_nest_lock_t *lock); nested lock버전의 위와 같은 기능의 함수<br />
<br />
void omp_set_lock(omp_lock_t *lock); OMP의 lock을 잠금<br />
void omp_set_nest_lock(omp_nest_lock_t *lock); nested lock버전의 위와 같은 기능의 함수<br />
<br />
void omp_unset_lock(omp_lock_t *lock); OMP의 lock을 잠금 해제<br />
void omp_unset_nest_lock(omp_nest_lock_t *lock); nested lock버전의 위와 같은 기능의 함수<br />
<br />
int omp_test_lock(omp_lock_t *lock); 잠금 여부를 테스트하여 가능한 상태면 잠그고 다른 쓰레드에 의해 잠긴 상태면 false를 리턴 (=nonblocking 버전의 함수임)<br />
int omp_test_nest_lock(omp_nest_lock_t *lock); nested lock버전의 위와 같은 기능의 함수</span><br />
<hr><br />
double omp_get_wtime(void); 병렬처리 구간에서의 수행 시간(단위: 초)<br />
double omp_get_wtick(void); omp_get_wtime()에서 제공하는 시간 정밀도 (최소 단위 시간)<br />
<pre>double start;<br />
double end;<br />
start = omp_get_wtime();<br />
    ... work to be timed ...<br />
end = omp_get_wtime();<br />
printf("Work took %f seconds\n", end - start);</pre>예제 출처: OpenMP 2.5 Specification<br />
<br />
OpenMP 2.5에서 제공하는 모든 API를 다루었다. 특히 lock관련 함수는 중요하기 때문에 잘 알아두면 도움이 된다.<br />
<br />
<br />
<br />
<hr><br />
<strong>11. OpenMP에서 제공하는 환경변수 및 전처리기</strong><br />
<br />
OpenMP에서는 스케줄링, 생성할 쓰레드 개수, 동적 조정 여부, 병렬구간에서 쓰레드의 중첩 허용을 runtime시에 결정할 수 있도록 환경변수를 설정할 수 있다.<br />
<br />
• OMP_SCHEDULE : 스케줄링 타입과 chunk 크기를 설정<br />
• OMP_NUM_THREADS : 병렬 구간에서 생성할 쓰레드 개수<br />
• OMP_DYNAMIC : 쓰레드 개수의 동적 조정 여부를 설정<br />
• OMP_NESTED : 쓰레드 중첩의 허용 여부를 설정<br />
<br />
사용예 : bash (or POSIX shell)의 명령<pre>export OMP_SCHEDULE="dynamic"</pre><pre>export OMP_SCHEDULE "guided,4"</pre><pre>export OMP_NUM_THREADS 16</pre><pre>export OMP_DYNAMIC true</pre><pre>export OMP_NESTED false</pre><br />
<br />
* OMP 조건부 컴파일을 위한 전처리기<br />
<pre><br />
#include &lt;stdio.h&gt;<br />
int main()<br />
{<br />
# ifdef _OPENMP<br />
    printf("Compiled by an OpenMP-compliant implementation.\n");<br />
# endif<br />
    return 0;<br />
}</pre><br />
<br />
<br />
<br />
<hr><br />
<strong>12. OpenMP vs pthread (POSIX thread)</strong><br />
<br />
OpenMP로 작성한 프로그램을 그대로 pthread 버전으로 바꾸면서 둘의 차이를 보도록 하겠다. <br />
예제로 볼 것은 앞에서 numerical integration으로 pi를 계산한 프로그램을 두가지 버전으로 작성해서 비교하도록 하겠다.<br />
<br />
우선 HOWTO #1에서 봤던 OpenMP 버전을 다시 보도록 하겠다.<br />
예제 파일 : <a href="http://pds17.egloos.com/pds/200910/21/55/pi_numerical_integration.c">pi_numerical_integration.c</a><pre>#include &lt;stdio.h&gt;<br />
#include &lt;stdlib.h&gt;<br />
int num_steps=1000000000; /* 10억번 : 너무 많으면 조금 줄이시길... */<br />
<br />
int main()<br />
{<br />
    int i;<br />
    double x, step, sum = 0.0;<br />
    step = 1.0/(double) num_steps;<br />
#pragma omp parallel for private(x) reduction(+:sum)<br />
    for (i=0; i&lt;num_steps; i++) {<br />
        x = (i+0.5) * step;<br />
        sum += 4.0/(1.0 + x*x);<br />
    }<br />
    printf("PI = %.8f (sum = %.8f)\n", step*sum, sum);<br />
    return EXIT_SUCCESS;<br />
}</pre>OpenMP버전은 이미 설명했었기 때문에 이를 바로 Pthread 버전으로 바꾸도록 하겠다.<br />
<br />
pthread 버전 예제 파일 : <a href="http://pds16.egloos.com/pds/200910/19/55/pi_integration_pth.c">pi_integration_pth.c</a><br />
<pre>#include &lt;stdio.h&gt;<br />
#include &lt;stdlib.h&gt;<br />
#include &lt;pthread.h&gt;<br />
<br />
static int num_steps=1000000000;<br />
double pi, step;<br />
struct start_arg {<br />
    int     i_start;<br />
    int     i_end;<br />
    double  sum;<br />
} start_arg[2];<br />
<br />
void *start_func(void *arg)<br />
{<br />
    int     i;<br />
    double  x, sum = 0.0;<br />
    struct start_arg *p_arg = (struct start_arg *) arg;<br />
<br />
    for (i=p_arg-&gt;i_start; i&lt;p_arg-&gt;i_end; i++) {<br />
        x = (i+0.5) * step;<br />
        sum += 4.0/(1.0 + x*x);<br />
    }<br />
    p_arg-&gt;sum = sum;<br />
    return NULL;<br />
}<br />
<br />
int main()<br />
{<br />
    int     i;<br />
    double  sum;<br />
    pthread_t   pt_id[2];<br />
<br />
    step = 1.0/(double) num_steps;<br />
    start_arg[0].i_start = 0;<br />
    start_arg[0].i_end   = num_steps&gt;&gt;1;<br />
    start_arg[1].i_start = start_arg[0].i_end + 1;<br />
    start_arg[1].i_end   = num_steps;<br />
<br />
    printf("%d~%d, %d~%d\n",<br />
            start_arg[0].i_start, start_arg[0].i_end ,<br />
            start_arg[1].i_start, start_arg[1].i_end );<br />
<br />
    for (i=2; i--; ) { /* create pthread */<br />
        if (pthread_create(&amp;pt_id[i], NULL, start_func, &amp;start_arg[i])){<br />
            perror("pthread_create");<br />
            return 1;<br />
        }<br />
    }<br />
    for (i=2; i--; ) { /* join pthread : explicit barrier */<br />
        if (pthread_join(pt_id[i], NULL)){<br />
            perror("pthread_join");<br />
            return 1;<br />
        }<br />
    }<br />
    sum = start_arg[0].sum + start_arg[1].sum;<br />
    printf("PI = %.8f (sum = %.8f)\n", step*sum, sum);<br />
    return EXIT_SUCCESS;<br />
}</pre>pthread 버전으로 바꾸면 openmp버전보다 코딩량이 훨씬 늘어난다.<br />
<br />
POSIX thread를 사용하면 OpenMP에서 없는 explicit barrier를 넣어주어야 하므로 pthread_join()함수를 사용했으며, OpenMP에서 간단하게 reduction을 했던 것이 POSIX thread에서는 개별적인 start_arg라는 구조체를 통해서 확인했다. 이로 인해서 코딩량과 자료구조까지 신경써야 하는 불편함이 생긴다.<br />
<br />
<strong>그러나 OpenMP가 POSIX thread보다 우월하다고 말하는 것은 아니다. 문제가 정교함을 요구하는 경우에는 오히려 OpenMP가 적합하지 않은 경우도 많기 때문이다. 실제로 조건변수를 이용한다든지 쓰레드 키를 이용한 라이브러리등을 사용하는 경우라면 POSIX thread외에는 대안이 없는 경우가 많기 때문이다. 이 글은 OpenMP를 소개하는 글이므로 OpenMP에 대해 좋은 말만 늘어놓고 있지만 필자의 말을 비판없이 받아들여서 OpenMP가 최고라는 편협한 사고에 휩싸이지는 않았으면 좋겠다.</strong><br />
<br />
잡설이 길었는데 마지막으로 두가지 버전의 실행 결과를 확인해보자. 첫번째 실행한 파일인 pi_integration이 OpenMP버전이고, 두번째의 pi_integration_pth가 Pthread버전이다.<br />
<pre>$ time ./pi_integration<br />
PI = 3.14159265 (sum = 3141592653.59036255)<br />
<br />
real    0m3.910s<br />
user    0m7.484s<br />
sys    0m0.018s</pre><br />
<pre>$ time ./pi_integration_pth<br />
0~500000000, 500000001~1000000000<br />
PI = 3.14159265 (sum = 3141592650.38979244)<br />
<br />
real    0m4.071s<br />
user    0m7.399s<br />
sys    0m0.022s</pre>결과를 보면 실행결과나 성능에 큰 차이가 없어 보인다. 하지만 몇몇 컴파일러에서는 특정 버전이 더 느린 경우도 있다.(이는 해당 플랫폼의 특징에서 기인한다. 표준안에는 성능에 대한 제약은 없기 때문에 몇몇 플랫폼은 OpenMP 구현시 덜 최적화 되어있는 경우도 있다.)<br />
<br />
<br />
<br />
<hr><br />
<strong>13. 마치면서.</strong><br />
<br />
처음 시작하면서 멀티 쓰레드 프로그래밍의 필요성에 대해서 언급했었다. CPU의 개별 코어의 주파수(frequency)는 거의 한계치에 와 있기 때문에 개별 코어의 속도는 파이프라인의 증설이나 몇가지 기술로 약간의 진보밖에 이룰 수 없게 되었다. 따라서 멀티코어를 적극적으로 사용하는 기술이 뒷받침되지 않고는 프로그램의 성능을 높일 수 있는 방법은 거의 없다.(일각에서 언급되는 그래픽카드의 코어를 병렬로 이용하는 GPGPU도 같은 맥락이다.) 하지만 멀티 쓰레드 프로그래밍은 다음과 같은 난제가 있다.<br />
<br />
<ol><li>멀티 쓰레드 프로그램의 문제점 : 디버깅이 힘들다. (재현성이 나쁘기 때문)</li><li>아키텍처와 메모리 모델에 대한 이해가 필요하다. (본인도 부족하다. 그래서 실수를 할 때가 있다.)</li></ol><br />
<br />
첫째로 문제인 디버깅에서 언급되는 재현성은 쓰레드 프로그래밍에서 큰 난제이다. 디버깅을 위해서는 같은 조건하에서 시행하였을때 동일한 문제가 재현되어야만 버그를 찾아낼 수 있다. 그러나 멀티 쓰레드 프로그램은 각각의 쓰레드들이 같은 순서로 실행된다는 보장이 없기 때문에 버그가 발생한 순서조합이 디버깅때도 항상 발생하지 않는다. 즉 문제를 찾아내려고 할때는 오히려 제대로 작동하는 경우도 많다는 것이다.<br />
<br />
따라서 멀티 쓰레드 프로그램을 대형 모델에 적용할 때는 투명성을 높여서 각각의 데이터나 태스크의 흐름을 모니터링할 수 있도록 만들어야 한다. 이를 위해서 여러 방법론이 나와있지만 스페셜한 케이스에는 딱 들어맞는 것은 없다. 그러므로 반복적인 프로그래밍 훈련만이 투명성을 높이고 디버깅이 쉬운 개발철학을 만들 수 있게 해준다.<br />
<br />
둘째로 우리가 종종 사용하는 x86 인텔 호환 플랫폼은 PC환경에서 가장 많이 쓰이지만 산업체에서는 IA64, Sparc, MIPS, PPC(PowerPC) 등등의 CPU에 여러가지 OS조합의 플랫폼이 사용된다. 그러나 같은 프로그래밍을 짜도 표준안에서 언급한 semantic을 만족하면서 미묘한 차이를 보이는 경우가 많다. 특히 기능은 제대로 작동하는데 성능은 이상하게 차이나는 경우가 발생할 수 있다. 이를 해결하기 위해서는 각각의 플랫폼에 대한 이해와 하드웨어 지식이 필요한데, 한사람의 프로그래머가 모든 플랫폼에 정통할 수는 없기 때문에 적극적으로 다른 플랫폼의 전문가와 소통할 수 있는 채널이 있어야만 한다. (<strong>더 중요한 점은 다른 플랫폼의 전문가와 제대로 소통할 수 있도록 질문을 잘 하는 방법을 익히는 것이다. 간혹 질문을 잘 못하고 횡설수설하면 답변을 해주고 싶어도 못한다. 필자도 좀 횡설수설하는 스타일이라 고치려고 하는데 참 어렵다.</strong>)<br />
<br />
<br />
하지만 이런 어려움은 점차 해소될 전망이다. 앞으로 나오는 많은 기술들과 소프트웨어 방법론들은 프로그래머가 복잡한 이론이나 방법론을 몰라도 멀티 쓰레드 프로그래밍을 쉽게 할 수 있도록 돕고 있다. 실제로 10년전과 지금은 하늘과 땅 차이로 멀티 쓰레드 프로그래밍이 쉬워졌다.(그럼에도 불구하고 아직도 멀티 쓰레드 프로그래밍은 어려운 분야이지만...)<br />
<br />
비슷한 예를 하나 들자면 15년전쯤에 필자가 프로그래밍을 처음 접하던 시절에는 그래픽 처리를 위해 어셈블리어를 배우는 것이 유행했던 시절이 있었다. 속도와 최적화 문제에서 다른 대안이 없었기 때문이었다. 그러나 지금은 어셈블리어를 몰라도 왠만한 그래픽 최적화를 하는데 무리가 없다.(아주 가끔 어셈블러에 의지해야 하는 스페셜 케이스가 있긴하다.) 이는 시간이 지나면 좀 더 편리한 프로그래밍 환경이 만들어진다는 것을 의미한다.<br />
<br />
그렇다면 이쯤에서 독자들은 필자에게 반문할 것이다. "<strong>멀티 쓰레드 프로그래밍이 쉬워지기를 기다리라는 말입니까?</strong>" 이에 대한 필자의 대답은 "절반은 그렇다"이다. 왜 절반만 Yes를 하는지는 그 쉬워지는 시점 때문이다. 많은 선도자들은 예측하기를 멀티 쓰레드 프로그래밍의 하드웨어적, 소프트웨어적 난제들이 해결되어 많은 부분에서 변화가 생기려면 적어도 5~10년후가 되어야 할 것이라고 한다. 그렇다면 본인이 앞으로 5~10년을 기다릴 수 있는 형편인지 아니면 당장 써야 하는지 결정해야한다. 한창 공부하는 학생이라면 더 기다리다가 공부해도 되겠지만 field에서 일하는 산업역군이라면 당장 공부하라고 말해주겠다.<br />
<br />
<br />
<span style="COLOR: #ff0000">그럼 여기까지 수고하셨습니다.~ 그리고 본문에서는 반말로 작성해서 죄송합니다. 글자수를 줄이기 위한 고육지책이었습니다.<br />
<br />
그리고 OpenMP 3.0의 새로운 기능들은 조만간 포스팅하도록 하겠습니다. 참고로 3.0에서는 task construct라는 좋은 기능이 추가되었습니다.</span><br />
<hr><br />
* History<br />
2009.10.20 초안 작성<br />
2009.10.21 atomic 보충 설명, 예제 파일 등록<br/><br/>tag : <a href="/tag/openmp" rel="tag">openmp</a>,&nbsp;<a href="/tag/thread" rel="tag">thread</a>,&nbsp;<a href="/tag/쓰레드" rel="tag">쓰레드</a>,&nbsp;<a href="/tag/프로그래밍" rel="tag">프로그래밍</a>,&nbsp;<a href="/tag/C언어" rel="tag">C언어</a>			 ]]> 
		</description>
		<category>책 강의 PDF,PPT</category>
		<category>openmp</category>
		<category>thread</category>
		<category>쓰레드</category>
		<category>프로그래밍</category>
		<category>C언어</category>

		<comments>http://sunyzero.egloos.com/4258873#comments</comments>
		<pubDate>Tue, 20 Oct 2009 03:56:01 GMT</pubDate>
		<dc:creator>SY Kim</dc:creator>
	</item>
	<item>
		<title><![CDATA[ RealForce 87U 기계식 키보드 ]]> </title>
		<link>http://sunyzero.egloos.com/4255230</link>
		<guid>http://sunyzero.egloos.com/4255230</guid>
		<description>
			<![CDATA[ 
  얼마전 조카가 키보드위에다 포카리 스웨트를 쏟는 바람에 키보드가 약간 뻑뻑해졌다.<br />
알콜로 세척하고 윤활제를 바르고 별짓을 다 했어도 FILCO 키보드의 쫀득한 키감이 무둑둑한 키감으로 바뀌어서<br />
세컨 컴퓨터에 달려있던 Sejin 103기계식(SKM-1080)을 가져와서 쓰고 있다.<br />
<br />
사실 Sejin SKM-1080도 알프스 스위치가 들어있어서 좋은 감압을 가지고 있는데 최고의 감압이라고 부르는<br />
리얼포스가 점점 가지고 싶어진다. 아 긴축재정인데 자꾸 이러면 안되는데...<br />
<br />
결국 RealForce를 찾아보니 tenkeyless의 신제품 RealForce 87U가 있기는 있다.<br />
<strong>하지만 가격이 36만원. -_-;</strong><br />
<br />
신중하게 생각하고 질러야겠다. 흠...<br />
<br />
http://www.leopold.co.kr/?doc=cart/item.php&amp;it_id=1237185827<br />
<br />
<div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds16.egloos.com/pds/200910/14/55/b0026155_4ad5d4c10fb00.jpg" width="500" height="280.555555556" onclick="Control.Modal.openDialog(this, event, 'http://pds16.egloos.com/pds/200910/14/55/b0026155_4ad5d4c10fb00.jpg');" /></div><br/><br/>tag : <a href="/tag/리얼포스" rel="tag">리얼포스</a>,&nbsp;<a href="/tag/키보드" rel="tag">키보드</a>,&nbsp;<a href="/tag/realforce" rel="tag">realforce</a>			 ]]> 
		</description>
		<category>Hobby</category>
		<category>리얼포스</category>
		<category>키보드</category>
		<category>realforce</category>

		<comments>http://sunyzero.egloos.com/4255230#comments</comments>
		<pubDate>Wed, 14 Oct 2009 14:21:35 GMT</pubDate>
		<dc:creator>SY Kim</dc:creator>
	</item>
	<item>
		<title><![CDATA[ Oracle 11g on Fedora11 (FAQ) ]]> </title>
		<link>http://sunyzero.egloos.com/4250369</link>
		<guid>http://sunyzero.egloos.com/4250369</guid>
		<description>
			<![CDATA[ 
  <strong>오라클 11g 설치후 문제가 발생했을때의 대처방법</strong><br />
<hr><br />
<strong>Q. Oracle EM manager의 관리자 웹 페이지에 접속할 수 없습니다.</strong><br />
A. Oracle EM Web based Manager의 기본 포트번호는 1158번이고 https로 접속하면 된다.<br />
하지만 간혹 설치 중에 다른 포트번호가 할당되는 경우가 있다. 이런 경우는 $ORACLE_HOME/install/portlist.ini 파일에 포트번호가 적혀있다. 아래는 기본값이 아닌 5500번으로 세팅된 것을 보여준다.<br />
<pre>$ cat $ORACLE_HOME/install/portlist.ini<br />
Enterprise Manager 콘솔 HTTP 포트(orcl) = 5500<br />
Enterprise Manager 에이전트 포트(orcl) = 3938</pre>위와 같은 경우라면 https://localhost:5500/으로 접속해야 한다.<br />
<hr><br />
<strong>Q. emctl 실행시 "EM Configuration issue. ...." 메시지가 나타납니다.</strong><br />
A. 설치 후에 재부팅을 했다면 Oracle EM manager를 재시동해야 한다.<br />
<pre>$ emctl start dbconsole</pre>허나 emctl 가동시 간혹 "EM Configuration issue. /u01/app/oracle/product/11.2.0/dbhome_1/localhost.localdomain_orcl" 등의 비슷한 메시지가 나오는 경우가 있다. 이는 EM Configuration에 잡아놓은 호스트명과 다르기 때문에 나타난다.<br />
<br />
해결방법은 일단 $ORACLE_HOME에 가서 ls 로 디렉토리를 확인하자.<br />
<br />
<pre>[oracle@localhost ~]$ cd $ORACLE_HOME<br />
[oracle@localhost dbhome_1]$ ls<br />
EMStagePatches_orcl  ctx               instantclient   mesg         owm           srvm<br />
OPatch               cv                inventory       network      perl          sysman<br />
apex                 dbs               j2ee            nls          plsql         timingframework<br />
assistants           dc_ocm            javavm          oc4j         precomp       ucp<br />
bin                  deinstall         jdbc            odbc         racg          uix<br />
ccr                  demo              jdev            olap         rdbms         utl<br />
cdata                diagnostics       jdk             ons          relnotes      wwg<br />
cfgtoollogs          emcli             jlib            opmn         root.sh       xdk<br />
clone                has               ldap            oraInst.loc  scheduler<br />
config               hs                lib             oracore      slax<br />
crs                  ide               localhost_orcl  ord          sqldeveloper<br />
csmig                install           log             oui          sqlj<br />
css                  install.platform  md              owb          sqlplus</pre>디렉토리에 보면 SID이름인 orcl이 붙어있는 디렉토리를 볼 수 있다. 위에서는 localhost_orcl 로 보인다. 즉 오라클이 인식하는 호스트명은 localhost라는 소리다.<br />
<br />
그렇다면 ORACLE_HOSTNAME에 localhost를 지정하고 다시 시도해보자.<br />
<pre>$ export ORACLE_HOSTNAME=localhost<br />
$ emctl start dbconsole<br />
Oracle Enterprise Manager 11g Database Control Release 11.2.0.1.0 <br />
Copyright (c) 1996, 2009 Oracle Corporation.  All rights reserved.<br />
https://localhost:1158/em/console/aboutApplication<br />
Starting Oracle Enterprise Manager 11g Database Control ........<br />
------------------------------------------------------------------<br />
Logs are generated in directory /u01/app/oracle/product/11.2.0/dbhome_1/localhost_orcl/sysman/log</pre>ORACLE_HOSTNAME이 잘 설정되었다면 oracle 유저의 환경 설정에 위의 내용을 넣어두자. 앞의 설치에서는 ~/.oraenv에 넣어두었으니 거기에 추가해두면 편리할 것이다.<br/><br/>tag : <a href="/tag/oracle" rel="tag">oracle</a>,&nbsp;<a href="/tag/오라클" rel="tag">오라클</a>,&nbsp;<a href="/tag/데이터베이스" rel="tag">데이터베이스</a>,&nbsp;<a href="/tag/oracle11g" rel="tag">oracle11g</a>			 ]]> 
		</description>
		<category>Com. Science</category>
		<category>oracle</category>
		<category>오라클</category>
		<category>데이터베이스</category>
		<category>oracle11g</category>

		<comments>http://sunyzero.egloos.com/4250369#comments</comments>
		<pubDate>Fri, 09 Oct 2009 06:34:00 GMT</pubDate>
		<dc:creator>SY Kim</dc:creator>
	</item>
	<item>
		<title><![CDATA[ Oracle 11g on Fedora11 (or Fedora9) ]]> </title>
		<link>http://sunyzero.egloos.com/4241364</link>
		<guid>http://sunyzero.egloos.com/4241364</guid>
		<description>
			<![CDATA[ 
  Oracle 11g R2를 설치한 과정을 자세한 그림과 함께 살펴보도록 하겠다.<br />
<strong>* 목차<br />
<a href="#preinstall">0. 설치전 확인</a><br />
<a href="#runOUI">1. 오라클 설치 프로그램 실행</a><br />
<a href="#DBsecurityupdate">2. 데이터베이스 보안 갱신 구성</a><br />
<a href="#installoption">3. 데이터베이스 설치 옵션</a><br />
<a href="#systemclass">4. 시스템 클래스 설정</a><br />
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#SystemclassDesktop">(4-A) 데스크탑 클래스를 지정한 경우</a><br />
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#SystemclassServer">(4-B) 서버 클래스를 지정한 경우</a><br />
<a href="#prerequisites">5. 필요 조건 검사 수행</a><br />
<a href="#installoptionsummary">6. 설치 옵션 요약</a><br />
<a href="#startinstallation">7. 제품 설치 시작 (or 데이터베이스 생성)</a><br />
<a href="#emca">8. 오라클 엔터프라이즈 매니저</a><br />
<a href="#oraenv">9. 오라클 환경 변수의 수정</a></strong><br />
<hr>우선 오라클을 설치한 테스트 플랫폼은 다음과 같다.<br />
<br />
<ul><li>호스트 1: Fedora 9</li><li>호스트 2: Fedora 11</li></ul>오라클 설치 프로그램은 X윈도우 기반이므로 X윈도우가 일단 설치되어있어야 한다. 그리고 GNOME환경을 추천하는데 이는 간혹 KDE나 OpenGL의 compiz에서는 메시지 창이 먹통이 되는 경우가 있기 때문이다. <span style="COLOR: #ff0000">설치 과정은 시스템에 따라서 다르지만 테스트한 E8400 CPU에서는 프로그램 설치에 약 50여분, DB생성에 약 40여분이 걸렸었다. 따라서 넉넉하게 2시간 걸렸다.</span><br />
<hr><br />
<strong><a name="preinstall">0. 설치전 확인</a></strong><br />
<br />
<span style="COLOR: #3366ff">첫번째로 SELINUX 설정을 Permissive로 바꿔야 한다</span>.(enforcing레벨에서 디렉토리, 리소스 권한 조절이 가능하면 해도 된다. 하지만 그 정도 기술이 있으면 최소한 expert 레벨이므로 이런 글을 보지 않을 것이라고 확신하기에 permissive로 바꾸는 것부터 하겠다.) SELINUX 설정의 확인은 sestatus로 확인가능하고 현재 설정을 바꾸는 것은 setenforce를 사용한다. 아래 예를 보자.<br />
<br />
<pre>[root@localhost ~]# sestatus<br />
SELinux status:                 enabled<br />
SELinuxfs mount:                /selinux<br />
Current mode:                   enforcing<br />
Mode from config file:          enforcing<br />
Policy version:                 24<br />
Policy from config file:        targeted<br />
[root@localhost ~]# setenforce permissive<br />
[root@localhost ~]# sestatus<br />
SELinux status:                 enabled<br />
SELinuxfs mount:                /selinux<br />
Current mode:                   permissive<br />
Mode from config file:          enforcing<br />
Policy version:                 24<br />
Policy from config file:        targeted</pre>"setenforce permissive"대신에 "setenforce 0"으로 명령해도 결과는 같다. 길게 permissive로 치는게 더 직관적일 것 같아서 예제는 길게 타이핑했다. 그런데 Current mode가 permissive로 바뀌었지만 여전히 Mode from config file이 enforcing이므로 재부팅하면 다시 enforcing이 된다. 따라서 설정 파일을 바꿔야 한다. 설정 파일은 /etc/sysconfig/selinux이다. vim 에디터로 열어보자.<br />
<br />
<pre># This file controls the state of SELinux on the system.<br />
# SELINUX= can take one of these three values:<br />
#       enforcing - SELinux security policy is enforced.<br />
#       permissive - SELinux prints warnings instead of enforcing.<br />
#       disabled - No SELinux policy is loaded.<br />
SELINUX=enforcing<br />
# SELINUXTYPE= can take one of these two values:<br />
#       targeted - Targeted processes are protected,<br />
#       mls - Multi Level Security protection.<br />
SELINUXTYPE=targeted</pre>중간에 "SELINUX=enforcing"의 부분을 "SELINUX=permissive"로 고쳐놓으면 다음번에 재부팅할때도 permissive로 보이게 된다. 설정후 다시 sestatus를 실행하면 Current mode와 Mode from config file 부분이 둘다 permissive로 되어 있을 것이다.<br />
<br />
<span style="COLOR: #3366ff">두번째로 오라클 설치에 필요한 공간을 확보해야 한다. 우선 RAM 공간부터 확인하자.</span><br />
<ul><li>RAM 메모리 : 2G 이상 (최소 1G이지만 2G는 되어야 한다.)</li><li>SWAP 메모리 : RAM 크기 or 최대 2배 (RAM이 2G이면 SWAP도 2G정도면 충분하다.)</li></ul><p>이제는 디스크 공간을 확인해야 한다.</p><ul><li>Oracle 11g 압축을 풀어놓을 임시 공간 : 약 3G</li><li>Oracle 11g 프로그램을 설치할 공간 : 약 7G</li><li>Oracle 데이터베이스를 구축할 공간 : 약 4G</li><li>/tmp 디렉토리 : 최소 400M 정도의 빈 공간</li></ul>위의 공간은 조금 여유있게 잡은 크기이다.&nbsp;설치할 공간과 DB구축의 공간을 최소로 잡으면 약 8G정도에도 가능하다.(하지만 넉넉하게 잡기를 권장한다)&nbsp;오라클은 일반적으로 /u01, /u02 이런 식의 디렉토리에 설치한다. 꼭 그래야 되는 것은 아니지만 이왕이면 관습적으로 /u01 디렉토리에 10G이상의 공간을 확보해서 DB까지 같은 곳에 넣어두면 간편하긴 하다. (하지만 제대로 설치해서 사용할 요령이라면 DB공간은 프로그램 설치 공간과 다른 파티션이나 디스크로 분리하는 것이 좋다.)<br />
<br />
<span style="COLOR: #3366ff">세번째로&nbsp;오라클 설치에 필요한 각종 패키지의 버전을 확인해야 한다.</span> 그러나 여기서는 확인하지 않고 넘어가도록 하겠다. 왜냐하면 설치 프로그램이 자동으로 패키지의 버전을 확인해주기 때문에 그때&nbsp;가서 경고가 뜨는 패키지를 골라 설치해도 늦지 않기 때문이다.<br />
<br />
<span style="COLOR: #3366ff">네번째로 오라클 설치에 필요한 각종 시스템 자원의 커널 파라메터를 조정해야 한다.</span>&nbsp;오라클은 세마포어 개수, 공유메모리 제한 등등 여러가지를 조정해야 하는데 위와 마찬가지로 오라클 설치 프로그램이 자동으로 확인해준다. 더군다나 확인 후 부족한 자원의 파라메터를 조정할 수 있는&nbsp;쉘 스크립트 프로그램을 작성해 주므로 그때 가서 간단하게 실행해서 설정하도록 한다.(귀찮게 미리 타이핑 하는 것은 시간 낭비다.)<br />
<br />
<span style="COLOR: #3366ff">다섯번째로 오라클 설치 및 구동에 필요한 그룹명과 유저를 만들어야 한다.</span> 우선 root로 로그인 한다.<br />
<pre># groupadd oinstall<br />
# groupadd dba<br />
# useradd -g oinstall -G dba oracle<br />
# passwd oracle</pre><span style="COLOR: #ff0000">설치에 필요한 그룹은 oinstall, dba이며 유저명은 oracle 이다</span>. oracle유저의 primay group은 oinstall이고, suppliment group은 dba이다. oracle 유저의 암호는 알아서 정하자. 유저가 만들어 졌으면 이번에는 /etc/pam.d/login 파일을 손봐야 한다.<br />
참고로 /etc/pam.d/login 파일은 유저가 로그인 할 때 각종 테스트할 기능들을 정의하는 파일이다. <br />
<br />
이 파일에 추가할 PAM 기능은 pam_limits.so 로서 유저가 로그인할 때 /etc/security/limits.conf에 정의된 자원제한값을 적용하는 기능이다. 오라클은 유저가 사용가능한 최대프로세스 개수, 최대 파일 개수를 기본값보다 크게 잡아줘야 하기 때문에 이 기능을 설정해줘야 한다. <span style="COLOR: #ff0000">주의할 점은 /etc/pam.d/login파일을 잘못 건드리면 새롭게 로그인이 안되는 불상사가 발생하므로 root 유저로 로그인된 terminal을 1개 여분으로 열어두자.</span><br />
<br />
이제 /etc/pam.d/login을 열어서 "session&nbsp;&nbsp; required&nbsp; pam_limits.so" 라는 행이 있는지 확인하고, 없다면 맨 아래에 추가해두자. 설정이 완료된 /etc/pam.d/login 파일은 다음과 같다. (내용은 약간 다를수 있다. 맨 끝에 pam_limits.so 기능이 들어있는 행만 있으면 된다.)<br />
<br />
<pre># cat /etc/pam.d/login<br />
#%PAM-1.0<br />
auth [user_unknown=ignore success=ok ignore=ignore default=bad] pam_securetty.so<br />
auth       include      system-auth<br />
account    required     pam_nologin.so<br />
account    include      system-auth<br />
password   include      system-auth<br />
# pam_selinux.so close should be the first session rule<br />
session    required     pam_selinux.so close<br />
session    required     pam_loginuid.so<br />
session    optional     pam_console.so<br />
# pam_selinux.so open should only be followed by sessions to be executed in the user context<br />
session    required     pam_selinux.so open<br />
session    required     pam_namespace.so<br />
session    optional     pam_keyinit.so force revoke<br />
session    include      system-auth<br />
-session   optional     pam_ck_connector.so<br />
session    required     pam_limits.so</pre>설정이 다 되었으면 확인을 위해서 <span style="COLOR: #ff0000">CTRL+ALT+F2</span>를 눌러 콘솔로 나가든지, 혹은 ssh를 통해 자신의 서버에 oracle 유저로 로그인 해보자. 만일 로그인이 실패했다면 위의 설정중에 어딘가를 잘못 타이핑한 것이다. 미리 로그인해둔 터미널에서 재확인해보자.(만일 설정을 잘못했는데 실수로 로그아웃했다면 정상적인 방법으로는 로그인이 불가능해진다. 따라서 설치용 CD/DVD로 복구 모드로 들어가서 /etc/pam.d/login 파일을 다시 수정해야 한다.)<br />
<br />
<span style="COLOR: #3366ff">여섯번째로 오라클을 설치할 디렉토리와 권한을 조정해둬야 한다.</span>&nbsp;여기서는 /u01에 오라클을 설치하는 것으로 진행하겠다.(여기서는 미리 /u01에 충분한 파티션을 할당해 둔 것을 전제한다.)<br />
<pre># mkdir /u01/app<br />
# chown -R oracle:oinstall /u01 </pre>이제 본격적인 설치를 위해서 X 윈도우를 다시 oracle 유저로 로그인 하자. 이제부터 특별한 언급이 없으면 모든 작업은 oracle 유저로 작업하는 것이다.<br />
<br />
<span style="COLOR: #3366ff">일곱번째로 오라클 환경변수를 설정해야 한다. </span>환경변수는 관습적으로 ~/.oraenv 파일명으로 만든다. 파일 내용은 다음과 같다.<br />
<pre>ORACLE_BASE=/u01/app/oracle<br />
ORACLE_SID=orcl<br />
export ORACLE_BASE ORACLE_SID</pre><br />
ORACLE_SID는 오라클 데이터베이스 SID 값인데, 주고 싶은 값을 사용하면 된다. 최대 30자까지 가능하다. (설치시 기본 값은 orcl을 사용한다.) 파일을 만들었으면 로그인 할 때 반영되게 해야 하므로 bash runtime configuration인 .bashrc에 넣어줘야 한다. "echo ".  ~/.oraenv  >>  ~.bashrc"라고 명령하면 된다.<br />
<br />
<br />
<strong><a name="runOUI">1. 오라클 설치 프로그램 실행</a></strong><br />
오라클 설치 프로그램은 여러 개의 zip 파일로 압축되어있는데 다 합쳐서 약 2G가 좀 넘는다. 압축을 풀면 database 디렉토리가 보이는데 들어가보면 runInstaller 라는 실행파일이 있다. 실행해보자.<br />
<br />
<pre>[oracle@localhost database]$ ./runInstaller <br />
Oracle Universal Installer 시작 중...<br />
<br />
임시 공간 확인 중: 80MB 이상이어야 합니다..   실제 728MB    성공<br />
스왑 공간 확인 중: 150MB 이상이어야 합니다..   실제 511MB    성공<br />
모니터 확인 중: 최소 256 색상을 표시하도록 구성되어 있어야 합니다..    실제 16777216    성공<br />
다음에서 Oracle Universal Installer의 시작을 준비하는 중 /tmp/OraInstall2009-10-06_03-39-30PM. 기다리십시오.</pre>위와 같은 메시지가 나오면 좀 기다려주자. 설치 프로그램 화면이 나오기 까지는 시스템에 따라서 수 분이 걸리는 경우도 있으니 다운되었다고 착각하여 터미널을 닫아버리는 잘못을 범하지는 말자.<br />
<br />
<br />
<strong><a name="DBsecurityupdate">2. 데이터베이스 보안 갱신 구성</a></strong><br />
<br />
<div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds16.egloos.com/pds/200910/07/55/b0026155_4acb9c206fe77.png" width="500" height="375" onclick="Control.Modal.openDialog(this, event, 'http://pds16.egloos.com/pds/200910/07/55/b0026155_4acb9c206fe77.png');" /></div><br />
보안 설정에 대한 알림 메일을 받으려면 email 주소를 넣어주면 된다. Oracle Support에 유저를 등록한 경우라면 더 편리하지만 이도저도 아닌 그냥 테스트용으로 설치하는 경우는 "다음"으로 진행하면 된다.<br />
<br />
<br />
<strong><a name="installoption">3. 데이터베이스 설치 옵션</a></strong><br />
<br />
<div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds16.egloos.com/pds/200910/07/55/b0026155_4acb9ceb0ff79.png" width="500" height="375" onclick="Control.Modal.openDialog(this, event, 'http://pds16.egloos.com/pds/200910/07/55/b0026155_4acb9ceb0ff79.png');" /></div>설치옵션에서는 3가지 설치옵션을 지정할 수 있다. 여기서는 데이터베이스 생성 및 구성으로 진행하는 것을 보이도록 하겠다.<br />
<br />
- 데이터베이스 생성 및 구성 : 소프트웨어 설치 후 데이터베이스를 생성해준다.<br />
- 데이터베이스 소프트웨어만 설치 : 소프트웨어만 설치하고 데이터베이스는 생성하지 않는다.<br />
- 기존 데이터베이스 업그레이드 : 말그대로 업그레이드다. 구버전의 오라클이 설치되어있다면 업그레이드 시켜준다.<br />
<br />
<br />
<br />
<strong><a name="systemclass">4. 시스템 클래스 설정</a></strong><br />
시스템 클래스 설정은 데이터베이스를 어떤 목적으로 구성할 것인지를 결정한다. 데스크탑용인지 서버용인지를 선택할 수 있다.<br />
<br />
<div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds16.egloos.com/pds/200910/07/55/b0026155_4acb9d98188c1.png" width="500" height="375" onclick="Control.Modal.openDialog(this, event, 'http://pds16.egloos.com/pds/200910/07/55/b0026155_4acb9d98188c1.png');" /></div>- 데스크톱 클래스 : 작은 규모용으로 설치한다.<br />
- 서버 클래스 : 자동 백업 및 분산 복제등 여러가지 기능이 추가된다.<br />
<br />
<strong><a name="SystemclassDesktop">(4-A) 데스크톱 클래스를 지정한 경우 : 총 9개의 필드를 가진 설치 구성화면으로 넘어간다.</a></strong><div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds17.egloos.com/pds/200910/07/55/b0026155_4acc3137bada6.png" width="500" height="375" onclick="Control.Modal.openDialog(this, event, 'http://pds17.egloos.com/pds/200910/07/55/b0026155_4acc3137bada6.png');" /></div>- Oracle Base : Oracle software가 설치될 기준 디렉토리이다. OUI(Oracle Universal Installer)가 자동으로 인식하기 위해서는 /u0[1-9]/app로 시작해야 한다.<br />
- 소프트웨어 위치 : 오라클 DB 소프트웨어가 위치할 디렉토리로서 ORACLE_BASE/product/&lt;버전&gt;/dbhome_1로 자동 결정됩니다.<br />
- 데이터베이스 파일 위치 : DB파일이 저장되는 곳<br />
- 데이터베이스 버전 : Enterprise Edition, Standard Edition, Standard Edition One중에 하나로 설정한다.(각 버전의 특징은 Help참고)<br />
- 문자 집합 : DB내에서 사용할 문자세트(국제화 규격에 따라 UTF-8 유니코드를 추천)<br />
- OSDBA 그룹 : DBA 그룹<br />
- 전역 데이터베이스 이름 : DB이름+도메인으로 구성된 전역 데이터베이스 이름<br />
- 관리 비밀번호, 비밀번호 확인 : 비밀번호 8~30자가 되어야 하며, 대소문자와 숫자가 혼합되어야만 한다.(또한 사전상의 단어를 사용하면 안된다.)<br />
<br />
<br />
<strong><a name="SystemclassServer">(4-B) 서버 클래스를 지정한 경우 : 우선 "단일 인스턴스 데이터베이스"와 "Real Application Clusters" 중에 하나를 선택해야 한다.</a></strong><br />
<div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds17.egloos.com/pds/200910/07/55/b0026155_4acc369e142cc.png" width="500" height="375" onclick="Control.Modal.openDialog(this, event, 'http://pds17.egloos.com/pds/200910/07/55/b0026155_4acc369e142cc.png');" /></div>서버 클래스 설치에서는 저장 영역 유형과 ASMSNMP 비밀번호를 지정할 수 있게 된다.<br />
- 저장 영역 유형 : 파일시스템, 원시 블록 장치 중에 선택할 수 있다.<br />
<br />
<br />
<br />
<strong><a name="prerequisites">5. 필요 조건 검사 수행</a></strong><br />
기본 설치를 지정한 뒤에는&nbsp;필요조건 검사를 행하게 된다. 여기서 커널 파라메터, 사용자 자원 제한, 패키지 버전들을 검사해준다.<br />
커널 파라메터와 사용자 자원 제한은 "수정 및 다시 확인"버튼을 누르면 자동으로 수정가능한 스크립트를 만들어 주기 때문에 패키지 버전이나 없는 패키지만 설치해주면 된다.<br />
<br />
<div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds15.egloos.com/pds/200910/07/55/b0026155_4acc379e134b1.png" width="500" height="375" onclick="Control.Modal.openDialog(this, event, 'http://pds15.egloos.com/pds/200910/07/55/b0026155_4acc379e134b1.png');" /></div><br />
<div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds17.egloos.com/pds/200910/07/55/b0026155_4acc388e5c29b.png" width="500" height="375" onclick="Control.Modal.openDialog(this, event, 'http://pds17.egloos.com/pds/200910/07/55/b0026155_4acc388e5c29b.png');" /></div>"수정 및 다시 확인" 버튼을 누르면 "수정 스크립트 실행"화면이 나타난다. 위의 경로의 runfixup.sh 파일을 root 계정의 권한으로 실행하면 된다.<br />
<div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds15.egloos.com/pds/200910/07/55/b0026155_4acc392bb8d99.png" width="500" height="196.96969697" onclick="Control.Modal.openDialog(this, event, 'http://pds15.egloos.com/pds/200910/07/55/b0026155_4acc392bb8d99.png');" /></div>위와 같이 runfixup.sh를 실행하면 각종 설정을 고쳐주게 된다. 수정되는 파일은 /etc/sysctl.conf와&nbsp;/etc/security/limits.conf이다. runfixup.sh가 실행한 뒤에 "다시 확인"을 누르면 재검사를 하게 된다.<br />
<div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds15.egloos.com/pds/200910/07/55/b0026155_4acc39dbaa63d.png" width="500" height="375" onclick="Control.Modal.openDialog(this, event, 'http://pds15.egloos.com/pds/200910/07/55/b0026155_4acc39dbaa63d.png');" /></div>위에 그림을 보면 실패 상태가 3개가 나오는데 교체크기(스왑 공간), /tmp, pdksh 패키지가 실패라고 나온다. 현재 설치하려는 시스템은 RAM이 2GB이상이므로&nbsp;설치중에 굳이 스왑 공간이 사용하지 않으므로 그냥 무시해도 된다. /tmp 디렉토리도 300MB이상이면 그냥 넘어가도 된다.&nbsp;<span style="COLOR: #3366ff">페도라에서는 pdksh대신에 mksh를 사용하므로 대신 mksh를 설치하면 된다.</span><br />
<br />
<br />
<br />
<strong><a name="installoptionsummary">6. 설치 옵션 요약</a></strong><br />
여기까지 설정한 것을 요약화면으로 보여준다. 이상한 점이 있다면 뒤로 가서 수정하고 오면 된다.<br />
<div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds15.egloos.com/pds/200910/07/55/b0026155_4acc3b824d050.png" width="500" height="375" onclick="Control.Modal.openDialog(this, event, 'http://pds15.egloos.com/pds/200910/07/55/b0026155_4acc3b824d050.png');" /></div>이상이 없다면&nbsp;"완료"버튼을 눌러 설치를 시작한다.<br />
<br />
<br />
<strong><a name="startinstallation">7. 제품 설치 시작 (or 데이터베이스 생성)</a></strong><br />
<br />
<div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds15.egloos.com/pds/200910/07/55/b0026155_4acc76d57e72c.png" width="500" height="375" onclick="Control.Modal.openDialog(this, event, 'http://pds15.egloos.com/pds/200910/07/55/b0026155_4acc76d57e72c.png');" /></div><br />
처음 설치할때 데이터베이스 구성을 선택했다면 파일 복사가 끝나고 dbca가 작동하여 데이터베이스를 구성하게 된다. 테스트한 호스트인 E8400에서는 거의 50여분을 걸렸다.<br />
<br />
<div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds16.egloos.com/pds/200910/07/55/b0026155_4acc777690e11.png" width="500" height="375" onclick="Control.Modal.openDialog(this, event, 'http://pds16.egloos.com/pds/200910/07/55/b0026155_4acc777690e11.png');" /></div><br />
데이터베이스를 구성하다보면 DB 유저의 암호를 정하는 창이 뜬다.<br />
<br />
<div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds15.egloos.com/pds/200910/07/55/b0026155_4acc77ba844eb.png" width="500" height="375" onclick="Control.Modal.openDialog(this, event, 'http://pds15.egloos.com/pds/200910/07/55/b0026155_4acc77ba844eb.png');" /></div>SYS와 SYSTEM 암호를 설정하고, 스크롤바를 더 내리면 SCOTT 유저가 있으므로 예제나 뭐 그런 것을 수행하려면 SCOTT유저의 암호도 넣어주자.<br />
<br />
<div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds15.egloos.com/pds/200910/07/55/b0026155_4acc77d50648c.png" width="500" height="375" onclick="Control.Modal.openDialog(this, event, 'http://pds15.egloos.com/pds/200910/07/55/b0026155_4acc77d50648c.png');" /></div>DB구성이 끝나면 root 유저로 해야할 작업을 root.sh란 쉘 스크립트로 만들어 준다. 이를 실행하고 원래 창으로 들어와서 확인을 하면 끝난다.<br />
<pre>[root@localhost ~]# /opt/app/oracle/product/11.2.0/dbhome_1/root.sh <br />
Running Oracle 11g root.sh script...<br />
<br />
The following environment variables are set as:<br />
    ORACLE_OWNER= oracle<br />
    ORACLE_HOME=  /opt/app/oracle/product/11.2.0/dbhome_1<br />
<br />
Enter the full pathname of the local bin directory: [/usr/local/bin]: <br />
   Copying dbhome to /usr/local/bin ...<br />
   Copying oraenv to /usr/local/bin ...<br />
   Copying coraenv to /usr/local/bin ...<br />
<br />
Creating /etc/oratab file...<br />
Entries will be added to the /etc/oratab file as needed by<br />
Database Configuration Assistant when a database is created<br />
Finished running generic part of root.sh script.<br />
Now product-specific root actions will be performed.<br />
Finished product-specific root actions.</pre><br />
<div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds16.egloos.com/pds/200910/07/55/b0026155_4acc77f41e7e2.png" width="500" height="375" onclick="Control.Modal.openDialog(this, event, 'http://pds16.egloos.com/pds/200910/07/55/b0026155_4acc77f41e7e2.png');" /></div>이제 DB 설치와 구성이 모두 끝났다. 위의 Enterprise Manager Database Control URL이 보인다. 웹 브라우저를 열어서 접속해보자.(간혹 접속이 안되는 경우는 방화벽 문제일 수 있다.) <br />
<br />
<br />
<br />
<strong><a name="emca">8. 오라클 엔터프라이즈 매니저</a></strong><br />
EM에 관리목적으로&nbsp;접속할 때는 sys유저의 SYSDBA권한으로 접속해야 한다.<br />
<br />
<div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds16.egloos.com/pds/200910/07/55/b0026155_4acc78dbaeed8.png" width="500" height="414.835164835" onclick="Control.Modal.openDialog(this, event, 'http://pds16.egloos.com/pds/200910/07/55/b0026155_4acc78dbaeed8.png');" /></div>emca화면은 웹 페이지이므로 직관적이다. 참고로 사용자 추가나 조정은 "서버"&gt;&gt;"사용자"에서 하면 된다.<br />
<br />
<div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds17.egloos.com/pds/200910/07/55/b0026155_4acc78f9b8b2d.png" width="500" height="378.125" onclick="Control.Modal.openDialog(this, event, 'http://pds17.egloos.com/pds/200910/07/55/b0026155_4acc78f9b8b2d.png');" /></div>그리고 항상 주의할 점은 서버 끌때는 위의 화면에서 DB를 "작동 중지" 시킨 후에 꺼야한다. 안그러면 다음번 DB 기동시에 문제가 발생할 수 있다.<br />
<br />
<br />
<strong><a name="oraenv">9. 오라클 환경 변수의 수정</a></strong><br />
앞에서 오라클 환경변수를 설정하기 위해서 ~/.oraenv 파일을 사용했는데, 설치후에는 이 파일을 수정해둬야 한다. 이유는 오라클의 바이너리 디렉토리를 PATH에 추가하고, Pro*C 컴파일등을 위한 라이브러리 경로를 추가해야 하기 때문이다. 따라서 앞의 3줄짜리 .oraenv를 다음과 같이 적절하게 수정한다.<br />
<br />
<pre>ORACLE_BASE=/u01/app/oracle<br />
ORACLE_SID=orcl<br />
ORACLE_HOME=/u01/app/oracle/product/11.2.0/dbhome_1<br />
export ORACLE_BASE ORACLE_HOME ORACLE_SID<br />
<br />
if [ x`echo $PATH | grep $ORACLE_HOME/bin` = 'x' ]; then<br />
    echo "Setting oracle binary path: $ORACLE_HOME/bin"<br />
    PATH=$PATH:$ORACLE_HOME/bin<br />
    export PATH<br />
fi<br />
<br />
if [ x`echo $LD_LIBRARY_PATH | grep $ORACLE_HOME/lib` = 'x' ]; then<br />
    echo "Setting oracle LD_LIBRARY_PATH : $ORACLE_HOME/lib"<br />
    if [ x$LD_LIBRARY_PATH = 'x' ]; then<br />
        LD_LIBRARY_PATH=$ORACLE_HOME/lib<br />
    else<br />
        LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$ORACLE_HOME/lib<br />
    fi<br />
    export LD_LIBRARY_PATH<br />
fi<br />
</pre><br />
<br />
수정 후에 로그인을 다시 해보면 Setting oracle binary path: /u01/app/oracle/product/11.2.0/dbhome_1/bin 와 Setting oracle LD_LIBRARY_PATH : /u01/app/oracle/product/11.2.0/dbhome_1/lib 메시지가 보일 것이다. 이제 어느 경로든지 sqlplus를 실행할 수 있게 될 것이다.<br />
<br />
<br />
* 그 외 오라클 관련 문서는 아래의 웹 사이트에서 볼 수 있다.<br />
<a href="http://www.oracle.com/technology/documentation">http://www.oracle.com/technology/documentation</a><br />
<br />
* History<br />
2009.10.10 "9. 오라클 환경 변수의 수정" 추가<br />
2009.10.07 첫번째 초안 작성<br/><br/>tag : <a href="/tag/오라클" rel="tag">오라클</a>,&nbsp;<a href="/tag/oracle" rel="tag">oracle</a>,&nbsp;<a href="/tag/fedora" rel="tag">fedora</a>,&nbsp;<a href="/tag/데이터베이스" rel="tag">데이터베이스</a>,&nbsp;<a href="/tag/oracle11g" rel="tag">oracle11g</a>			 ]]> 
		</description>
		<category>Com. Science</category>
		<category>오라클</category>
		<category>oracle</category>
		<category>fedora</category>
		<category>데이터베이스</category>
		<category>oracle11g</category>

		<comments>http://sunyzero.egloos.com/4241364#comments</comments>
		<pubDate>Wed, 07 Oct 2009 11:08:00 GMT</pubDate>
		<dc:creator>SY Kim</dc:creator>
	</item>
	<item>
		<title><![CDATA[ C언어: OpenMP를 이용한 멀티 쓰레드 프로그래밍 HOWTO #3 ]]> </title>
		<link>http://sunyzero.egloos.com/4234766</link>
		<guid>http://sunyzero.egloos.com/4234766</guid>
		<description>
			<![CDATA[ 
  <strong><span style="COLOR: #ff6600; FONT-SIZE: 130%">C언어: OpenMP를 이용한 멀티 쓰레드 프로그래밍 HOWTO #3</span></strong><br />
<br />
<hr>- HOWTO #1 : <a href="http://sunyzero.egloos.com/4227785">http://sunyzero.egloos.com/4227785</a><br />
- HOWTO #2 : <a href="http://sunyzero.egloos.com/4229235">http://sunyzero.egloos.com/4229235</a><br />
<hr><br />
<br />
<strong><span style="FONT-SIZE: 130%">6. Single Construct</span></strong><br />
<br />
single construct로 지시된 구간은 단 한번만 실행된다. 실행되는 쓰레드는 여러 쓰레드중에 제일 먼저 진입하는 쓰레드이다.<br />
<br />
<ul><li>single construct는 처음으로 진입한 쓰레드가 실행한다.</li><li>나머지 쓰레드들은 single construct 끝에 존재하는 implicit barrier에서 대기한다.</li><li>single construct가 끝나고 모든 쓰레드들은 implicit barrier에서 동시에 시작한다.</li></ul><br />
<div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds17.egloos.com/pds/200909/16/55/b0026155_4aafd7711fc57.png" width="500" height="290.281329923" onclick="Control.Modal.openDialog(this, event, 'http://pds17.egloos.com/pds/200909/16/55/b0026155_4aafd7711fc57.png');" /></div>그림에서 보이듯이 parallel 구간에서 쓰레드들 중에 한 개만 &nbsp;single construct를 실행하고 나머지는 뒤에 존재하는 implicit barrier에서 대기하는 것을 볼 수 있다. 그러면 위 소스코드를 컴파일하고 실행해보자. 실행결과는 예상대로 "1. Hello world"는 1번 출력되고, "2. Hello world"는 2번 실행된다.(테스트 호스트는 듀얼 코어이므로)<br />
<pre>$ gcc -o omp_single -fopenmp omp_single.c<br />
$ ./omp_single<br />
1. Hello world<br />
2. Hello world<br />
2. Hello world</pre><br />
<br />
<br />
<strong><span style="FONT-SIZE: 130%">7. Master Construct</span></strong><br />
<br />
master construct는 single construct와 매우 비슷하다.<br />
하지만 다른 점이 2가지 있다.<br />
<br />
<ul><li>master construct 구간은 무조건 master thread (main thread)가 1번 실행한다.</li><li>master construct 구간뒤에 implicit barrier가 없다. <br />
즉 모든 쓰레드는 master construct 실행되는 동안에도 계속 실행한다.</li></ul><div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds17.egloos.com/pds/200909/16/55/b0026155_4aafda6d8829f.png" width="500" height="290.281329923" onclick="Control.Modal.openDialog(this, event, 'http://pds17.egloos.com/pds/200909/16/55/b0026155_4aafda6d8829f.png');" /></div>실행 결과는 위의 single construct와 같지만 위 그림에서 보듯이 약간의 차이는 있다. master construct는&nbsp;implicit barrier가 없다는 점이다. 중요한 차이므로 꼭 기억해야 한다.&nbsp;<br />
<br />
<br />
<br />
<br />
<strong><span style="FONT-SIZE: 130%">8. Barrier</span></strong><br />
<br />
배리어란 동기화(synchronization)을 위해서 사용되는 기능이다.<br />
<br />
동기화는 시간적 개념이다.&nbsp;풀어서 설명하기 위해 예를 들자.&nbsp;스타크래프트 배틀넷은 왠만한 사람이면 다 해봤을 것이다. 최대 8명까지 게임에 참가할 수 있는데,&nbsp;어떤 유저가 매우 느린 모뎀을 쓰고 있으면 게임 중간에 타임을 세는 화면이 뜨고 기다려주는 것을 볼 수 있다. 이는 빠른 네트워크/컴퓨터를 가진 유저와 느린 네트워크/컴퓨터를 가진 유저의 게임 속도를 맞추기 위해서 배리어가 작동한 것이다. 따라서 결과적으로 배리어는 느린 사람에 맞춰서 앞서 가는 사람이 대기하도록 하는 기능이다.<br />
<br />
그러면&nbsp;프로그래밍에서는 배리어를 어떻게 사용해야 하는가? 작업이 병렬적으로 이뤄진다고 하더라도 전처리, 후처리 작업들이 나눠져 있을 경우에는 전처리 작업들을 병렬처리했을때 어떤 특정 쓰레드가 빨리 처리했다고 후처리 작업을 먼저 출발하면 데이터가 꼬이거나 로직이 망가질 수 있다. 이럴 경우 중간중간에 적절한 배리어를 넣어주면 깔끔하게 해결된다.(하지만 역으로 배리어가 많으면 그 만큼 대기도 많아질 수 있다.)<br />
<br />
<br />
<strong>8.a Implicit barrier</strong><br />
<br />
앞에서 implicit barrier(암묵적 배리어)에 대해서 이야기를 많이 했다. OpenMP는 각 작업의 동기화에 대한 편의성을 제공하기 위해서 implicit barrier를 잘 제공한다. 어떤 construct 에 대해서 implicit barrier가 제공되는지 정리하고 넘어가자.<br />
<br />
<ul><li>#pragma omp parallel</li><li>#pragma omp for</li><li>#pragma omp sections</li><li>#pragma omp single</li></ul>위의 4가지의 경우는 블록 끝에 자동적으로 implicit barrier가 들어간다. 하지만 위의 4가지 construct 의 끝에 nowait clause를 지정하면 implicit barrier가 제거되고 대기하지 않고 이후 코드가 즉시 실행된다.<br />
<br />
<div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds17.egloos.com/pds/200909/18/55/b0026155_4ab36d31cb9f2.png" width="500" height="290.281329923" onclick="Control.Modal.openDialog(this, event, 'http://pds17.egloos.com/pds/200909/18/55/b0026155_4ab36d31cb9f2.png');" /></div>위의 예제에서는 single construct에 nowait를 적용하여 implicit barrier를 제거하는 것을 볼 수 있다. <br />
(그림 아래에 있는 implicit barrier는 parallel construct에 있는 barrier다.)<br />
<br />
<br />
<strong>8.b Explicit barrier</strong><br />
<br />
이번에는 사용자가 직접 지정할 수 있는 explicit barrier 기능에 대해서 보겠습니다.<br />
<ul><li>#pragma omp barrier 구문을 지정하면 해당 부분에서 모든 쓰레드가 도착할 때까지 대기하게 된다.</li></ul><pre>char * get_time0(char *buf, size_t sz_buf);<br />
<br />
int main()<br />
{<br />
    int     t_sleep; char    buf[16];<br />
#pragma omp parallel private(t_sleep, buf)<br />
    {<br />
#pragma omp single nowait<br />
        sleep(1);<br />
        printf("[%s] phase1:sleep %ld sec.\n", get_time0(buf, sizeof(buf)), t_sleep = times(NULL)%8);<br />
        sleep(t_sleep);<br />
#pragma omp barrier<br />
        /* explicit barrier */<br />
        printf("[%s] phase2. Hello world\n", get_time0(buf, sizeof(buf)));<br />
    }<br />
    return 0;<br />
}<br />
<br />
char * get_time0(char *buf, size_t sz_buf)<br />
{<br />
    time_t  t0; struct tm   tm_now;<br />
    if (buf == NULL) return NULL;<br />
    if (time(&amp;t0) == ((time_t)-1)) return NULL;<br />
    localtime_r(&amp;t0, &amp;tm_now);<br />
    if (strftime(buf, sz_buf, "%H:%M:%S", &amp;tm_now) == 0) return NULL;<br />
    return buf;<br />
}</pre>이제 실행해보면 배리어 효과때문에 마지막 실행한 19:12:28에서 5초뒤에 phase2가 실행되는 것을 볼 수 있다.<br />
<pre>$ ./omp_barrier<br />
[19:12:27] phase1:sleep 1 sec.<br />
[19:12:28] phase1:sleep 5 sec.<br />
[19:12:33] phase2. Hello world<br />
[19:12:33] phase2. Hello world</pre><br />
이번에는 여기까지 마치고, 다음에 critical section이나 atomic, OpenMP API함수등에 대해서 다뤄보도록 하겠다.<br />
<hr><br />
* history<br />
2009.11.09 sections construct에 implicit barrier 지정되는 내용 수정 - namhyung님 지적<br/><br/>tag : <a href="/tag/openmp" rel="tag">openmp</a>,&nbsp;<a href="/tag/thread" rel="tag">thread</a>,&nbsp;<a href="/tag/C언어" rel="tag">C언어</a>,&nbsp;<a href="/tag/쓰레드" rel="tag">쓰레드</a>			 ]]> 
		</description>
		<category>책 강의 PDF,PPT</category>
		<category>openmp</category>
		<category>thread</category>
		<category>C언어</category>
		<category>쓰레드</category>

		<comments>http://sunyzero.egloos.com/4234766#comments</comments>
		<pubDate>Tue, 22 Sep 2009 12:31:00 GMT</pubDate>
		<dc:creator>SY Kim</dc:creator>
	</item>
	<item>
		<title><![CDATA[ C언어: OpenMP를 이용한 멀티 쓰레드 프로그래밍 HOWTO #2 ]]> </title>
		<link>http://sunyzero.egloos.com/4229235</link>
		<guid>http://sunyzero.egloos.com/4229235</guid>
		<description>
			<![CDATA[ 
  <strong><span style="COLOR: #ff6600; FONT-SIZE: 130%">C언어: OpenMP를 이용한 멀티 쓰레드 프로그래밍 HOWTO #2</span></strong><br />
<br />
<br />
<hr>- HOWTO #1 : <a href="http://sunyzero.egloos.com/4227785"><span style="COLOR: #5990bf">http://sunyzero.egloos.com/4227785</span></a><br />
<hr><br />
<br />
<span style="FONT-SIZE: 130%"><strong>* 연습문제 (멀티 쓰레딩과 reentrant 함수의 관계에 대한 문제)</strong></span><br />
<br />
<div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds17.egloos.com/pds/200909/14/55/b0026155_4aadf31587fd1.png" width="500" height="251.953125" onclick="Control.Modal.openDialog(this, event, 'http://pds17.egloos.com/pds/200909/14/55/b0026155_4aadf31587fd1.png');" /></div>이번에는 pi를 몬테 카를로 시뮬레이션을 이용해서 구해보도록 하겠다.<br />
각변의 길이가 1인 정사각형이 있다. 그리고 정사각형 안에 반지름(r)이 1인 원호를 그리도록 하자.<br />
그러면 중점으로부터 원호까지의 최단 길이는 무조건 1이 된다.<br />
<br />
그러면 이제 정사각형 안에 임의의 점(x,y)좌표를 찍어서 중점에서 선분을 연결하고 아랫변까지 선분을 내리면 직각삼각형이 된다.<br />
그리고&nbsp;이 직각삼각형의&nbsp;길이는 x, 높이는 y가 된다.<br />
피타고라스의 정리에 의해 (x의 제곱)+(y의 제곱)=(빗변의 제곱)이 되는데, 빗변이 길이가 1보다 작으면<br />
원호 안에 찍힌 점이 되고, 1보다 크면 원호 밖에 정사각형 내부에 찍힌 점이 된다.<br />
<br />
원호의 넓이는 반지름 r=1일 때 pi/4이 된다. 따라서 위의 수많은 랜덤 좌표를 찍은 뒤에 (원호 내부의 점의 개수)/(전체 랜덤 수)<br />
는 pi/4와 같아질 것이다. 그러면 이제 기본 코딩을 해보자.<br />
<br />
<pre>#include &lt;stdlib.h&gt;<br />
#include &lt;stdio.h&gt;<br />
#include &lt;time.h&gt;<br />
#include &lt;math.h&gt;<br />
#define LOOP_ITERATION 200000000<br />
int    hits;<br />
<br />
int main()<br />
{<br />
    int 	i;<br />
    double 	x, y, rns;<br />
    time_t	t_now;<br />
<br />
    printf("Loop iteration = %ld\n", (long)LOOP_ITERATION);<br />
    rns = 1.0/(double)RAND_MAX;<br />
<br />
    t_now = time(0);<br />
    srand((unsigned int)t_now);<br />
    for (i=0; i&lt;LOOP_ITERATION; i++) {<br />
        x = (double)rand() * rns;<br />
        y = (double)rand() * rns;<br />
        if (x*x + y*y &lt; 1) {<br />
            hits++;<br />
        }<br />
    }<br />
    printf("pi = %f\n", 4*(double)hits/LOOP_ITERATION);<br />
    return 0;<br />
}</pre>이제 컴파일 후 실행을 해 보겠다. 소스코드의 파일명은 pi_monte.c라고 저장했다.<br />
<br />
<pre>$ gcc -o pi_monte pi_monte.c<br />
$ time ./pi_monte<br />
Loop iteration = 200000000<br />
pi = 3.141588<br />
<br />
real    0m8.726s<br />
user    0m8.702s<br />
sys 0m0.023s<br />
</pre>CPU를 한 개만 사용했기 때문에 real과 user+sys 시간이 같게 나온다.<br />
이제 여기에 OpenMP를 적용해보도록 하자. 중복되는 코드는 모두 생략하고 중간에 loop부분만 적도록 하겠다.<br />
<pre>...생략...<br />
#pragma omp parallel for private(x,y) reduction(+:hits)<br />
for (i=0; i&lt;LOOP_ITERATION; i++) {<br />
    x = (double)rand() * rns;<br />
    y = (double)rand() * rns;<br />
    if (x*x + y*y &lt; 1) {<br />
        hits++;<br />
    }<br />
}<br />
...생략...</pre>자 이제 수정된 소스코드를 컴파일하고 다시 실행해보겠다.<br />
<pre>$ gcc -o pi_monte_omp -fopenmp pi_monte.c<br />
$ time ./pi_monte_omp <br />
Loop iteration = 200000000<br />
pi = 3.141513<br />
<br />
real    0m54.909s<br />
user    0m54.173s<br />
sys 0m53.115s</pre>OpenMP버전의 실행 시간이 엄청나게 증가한 것을 볼 수 있다.<br />
이는 뭔가 문제가 발생한 것이다. profiler가 있으면 cache miss가 많아진 것을 추적할 수 있다. 그러면 왜 cache miss가 많아졌을까?<br />
<br />
<strong>이는 멀티 쓰레드 안전(thread-safe, MT-safe)한 함수를 사용하지 않았기 때문이다.</strong><br />
단도직입으로 원인은 rand()함수이다. rand()함수는 내부에 static 변수(BSS영역)를 사용하기 때문에 lock 없이 사용하면<br />
오염된 공간을 쓸 수 있다. 그렇다고 lock으로 보호하는 것은 성능상으로 좋지 못하다. 따라서 MT-safe한 랜덤생성 함수로<br />
대체해야 한다. 마침 rand()의 reentrant 버전인 rand_r()이 있으므로 이를 사용하도록 바꾸면 된다.<br />
<br />
<strong><u>* reentrant에 대한 이해가 필요하다면...<a href="http://sunyzero.egloos.com/4188321">http://sunyzero.egloos.com/4188321</a> 글을 읽도록 한다.</u></strong><br />
<br />
그러면 이제 수정된 버전을 보도록 하겠다.<br />
<br />
<pre>#define _REENTRANT<br />
#include &lt;stdlib.h&gt;<br />
#include &lt;stdio.h&gt;<br />
#include &lt;time.h&gt;<br />
#include &lt;math.h&gt;<br />
#ifdef LOOP_ITERATION<br />
#define LOOP_ITERATION 200000000<br />
#endif<br />
int    hits;<br />
<br />
int main()<br />
{<br />
    unsigned int state;<br />
    int     i;<br />
    double  x, y, rns;<br />
    printf("Loop iteration = %ld\n", (long)LOOP_ITERATION);<br />
    rns = 1.0/(double)RAND_MAX;<br />
    state = (unsigned int)time(0);<br />
#pragma omp parallel for firstprivate(state) private(x, y) reduction(+:hits)<br />
    for (i=0; i&lt;LOOP_ITERATION; i++) {<br />
        x = (double)rand_r(&amp;state) * rns;<br />
        y = (double)rand_r(&amp;state) * rns;<br />
#if LOOP_ITERATION &lt; 100<br />
        printf("THR[%d:%d] x,y/state = %f,%f/%u\n", omp_get_thread_num(), i, x, y, state);<br />
#endif<br />
        if (x*x + y*y &lt; 1) {<br />
            hits++;<br />
        }<br />
    }<br />
    printf("pi = %f (hits = %d)\n", (double)hits/LOOP_ITERATION * 4, hits);<br />
    return 0;<br />
}</pre>중요한 변화를 확인하기 위해 LOOP_ITERATION이 100 이하인 경우에는 루프를 돌때마다 rand_r()로 얻어진 x, y, state의 값을 출력하도록 디버깅 코드를 심어놨다. 그러면 LOOP_ITERATION을 20으로 줄여서 컴파일&실행으로 디버깅 해보자.<br />
<br />
<pre>$ gcc -o pi_monte_omp_logging -DLOOP_ITERATION=20 -fopenmp pi_monte.c<br />
$ ./pi_monte_omp_logging<br />
Loop iteration = 20<br />
THR[0:0] x,y/state = 0.739258,0.373189/1534713604<br />
THR[0:1] x,y/state = 0.741886,0.152093/434332526<br />
THR[0:2] x,y/state = 0.399359,0.675170/1402811528<br />
THR[0:3] x,y/state = 0.140991,0.510551/3217124562<br />
THR[0:4] x,y/state = 0.117010,0.018486/2612351692<br />
THR[0:5] x,y/state = 0.151627,0.286311/535820534<br />
THR[0:6] x,y/state = 0.412198,0.450266/361487824<br />
THR[0:7] x,y/state = 0.126866,0.324974/1313573850<br />
THR[0:8] x,y/state = 0.923697,0.862684/1937324436<br />
THR[0:9] x,y/state = 0.828232,0.810037/3167316350<br />
THR[1:10] x,y/state = 0.739258,0.373189/1534713604<br />
THR[1:11] x,y/state = 0.741886,0.152093/434332526<br />
THR[1:12] x,y/state = 0.399359,0.675170/1402811528<br />
THR[1:13] x,y/state = 0.140991,0.510551/3217124562<br />
THR[1:14] x,y/state = 0.117010,0.018486/2612351692<br />
THR[1:15] x,y/state = 0.151627,0.286311/535820534<br />
THR[1:16] x,y/state = 0.412198,0.450266/361487824<br />
THR[1:17] x,y/state = 0.126866,0.324974/1313573850<br />
THR[1:18] x,y/state = 0.923697,0.862684/1937324436<br />
THR[1:19] x,y/state = 0.828232,0.810037/3167316350<br />
pi = 3.200000 (hits = 16)<br />
</pre>iteration이 20번이므로 pi의 정확도는 일단 포기하자. 지금은 pi의 결과를 보려는 것이 아니라, 각 쓰레드의 x, y값을 확인하기 위함이다. 0번째 쓰레드의 첫번째 데이터와 1번째 쓰레드의 첫번째 데이터를 비교하니 뭔가 이상하다. 이해를 돕기 위해 두개만 따로 떼어서 보도록 하자.<br />
<br />
THR[0:0] x,y/state = 0.739258,0.373189/1534713604<br />
THR[1:10] x,y/state = 0.739258,0.373189/1534713604<br />
<br />
둘이 놀랍도록 일치하지 않은가? 왜 이런 문제가 발생할까? 이는 rand_r()함수에 사용하는 seed값 변수인 state의 초기값이 모든 쓰레드들에 대해서 같기 때문에 발생하는 문제다. 그렇다면 각 쓰레드가 seed를 다르게 가져가도록 소스코드를 수정해야 한다.<br />
<br />
<pre>#define _REENTRANT<br />
#include &lt;stdlib.h&gt;<br />
#include &lt;stdio.h&gt;<br />
#include &lt;time.h&gt;<br />
#include &lt;math.h&gt;<br />
#ifdef LOOP_ITERATION<br />
#define LOOP_ITERATION 200000000<br />
#endif<br />
int    hits;<br />
<br />
int main()<br />
{<br />
    unsigned int state1, state2;<br />
    int     i;<br />
    double  x, y, rns;<br />
<br />
    printf("Loop iteration = %ld\n", (long)LOOP_ITERATION);<br />
    rns = 1.0/(double)RAND_MAX;<br />
    state1 = (unsigned int)times(NULL);<br />
#pragma omp parallel private(x, y, state2) reduction(+:hits) shared(state1)<br />
    {<br />
#pragma omp critical<br />
        state2 = rand_r(&amp;state1);<br />
        printf("THR[%d] state1/state2 = %u/%u\n", omp_get_thread_num(), state1, state2);<br />
#pragma omp for<br />
        for (i=0; i&lt;LOOP_ITERATION; i++) {<br />
            x = (double)rand_r(&amp;state2) * rns;<br />
            y = (double)rand_r(&amp;state2) * rns;<br />
#if LOOP_ITERATION &lt; 100<br />
            printf("THR[%d:%d] x,y/state,hits = %f,%f/%u,%d\n", omp_get_thread_num(), i, x, y, state2,hits);<br />
#endif<br />
            if (x*x + y*y &lt; 1) {<br />
                hits++;<br />
            }<br />
        }<br />
    }<br />
    printf("hits(%d), pi = %f\n", hits, (double)hits/LOOP_ITERATION * 4);<br />
    return 0;<br />
}</pre>#pragma omp critical 지시어는 아래의 블럭공간을 락으로 보호해준다. 즉 state1으로부터 state2라는 seed값을 생성하는 코드 부분은 각 쓰레드들이 직렬적으로 실행하게 되므로 각각 다른 seed (=state2)를 가질 수 있게 해준다.<br />
<br />
그러면 이제 수정된 버전의 확인을 위해 LOOP_ITERATION을 20번으로&nbsp;지정하고&nbsp;컴파일하여 다시 실행해보자.<br />
<pre>$ gcc -o pi_monte_omp_logging -DLOOP_ITERATION=20 -fopenmp pi_monte.c<br />
$ ./pi_monte_omp_logging<br />
Loop iteration = 20<br />
THR[0] state1/state2 = 2209777173/920556694<br />
THR[0:0] x,y/state,hits = 0.007502,0.800638/402955376,0<br />
THR[0:1] x,y/state,hits = 0.031584,0.366846/1879397754,1<br />
THR[0:2] x,y/state,hits = 0.084310,0.126795/2318644788,2<br />
THR[0:3] x,y/state,hits = 0.219188,0.054924/839319838,3<br />
THR[0:4] x,y/state,hits = 0.315477,0.640078/2081973432,4<br />
THR[0:5] x,y/state,hits = 0.423693,0.287051/4130577282,5<br />
THR[0:6] x,y/state,hits = 0.738140,0.376765/2380434428,6<br />
THR[0:7] x,y/state,hits = 0.127375,0.723816/3248220326,7<br />
THR[0:8] x,y/state,hits = 0.832496,0.256793/3481063424,8<br />
THR[0:9] x,y/state,hits = 0.939841,0.108509/3174395018,9<br />
THR[1] state1/state2 = 2209777173/754906038<br />
THR[1:10] x,y/state,hits = 0.446803,0.061819/2639594128,0<br />
THR[1:11] x,y/state,hits = 0.919237,0.585474/2439875226,1<br />
THR[1:12] x,y/state,hits = 0.122634,0.254468/2048737876,1<br />
THR[1:13] x,y/state,hits = 0.204702,0.526382/3058641982,2<br />
THR[1:14] x,y/state,hits = 0.642845,0.756832/477247192,3<br />
THR[1:15] x,y/state,hits = 0.550340,0.531024/1736039586,4<br />
THR[1:16] x,y/state,hits = 0.926036,0.217729/4074357788,5<br />
THR[1:17] x,y/state,hits = 0.266219,0.975137/2510577606,6<br />
THR[1:18] x,y/state,hits = 0.663719,0.557898/2880736800,6<br />
THR[1:19] x,y/state,hits = 0.491536,0.580296/2760522154,7<br />
hits(18), pi = 3.600000<br />
</pre>실행 결과를 보면 이제 x, y값이 각 쓰레드마다 달라지는 것을 볼 수 있다. <br />
<br />
그러면 이제 기능상의 문제는 해결되었으니 원래대로 200,000,000번의 시행 횟수로&nbsp;실행시간을 비교해보자.<br />
<pre>$ gcc -o pi_monte_omp -fopenmp pi_monte.c<br />
$ time ./pi_monte_omp<br />
Loop iteration = 200000000<br />
THR[0] state1/state2 = 319398670/265139008<br />
THR[1] state1/state2 = 319398670/1387545353<br />
hits(157082826), pi = 3.141657<br />
<br />
real    0m2.414s<br />
user    0m4.737s<br />
sys 0m0.013s</pre>싱글 쓰레드 버전이 8.6초 걸린 것에 비해 OpenMP로 돌린 버전은 2.4초로 확 줄어들었다. 원래대로라면 듀얼코어니 4.3초정도가 나와야 하겠지만, rand_r()자체가 rand()보다 가볍기 때문에 그로 인해서&nbsp;속도가 더&nbsp;빨라졌다. 즉 싱글 쓰레드 버전이라고 해도 rand()보다는 rand_r()을 사용하면 더 빠르다라는 교훈도 덤으로 알려드리는 문제였다.<br />
<br />
<br />
<br />
<span style="FONT-SIZE: 130%"><strong>5. Section construct</strong></span><br />
section construct는 task level parallelism에 사용하며, 각각의 작업들이 서로 관련이 없는 경우에 사용한다.<br />
(task 레벨의 병렬화이므로 divide and conquer 형태의 문제해결에도 적용가능하다.)<br />
<br />
<div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds17.egloos.com/pds/200909/14/55/b0026155_4aadf1267a348.png" width="500" height="290.281329923" onclick="Control.Modal.openDialog(this, event, 'http://pds17.egloos.com/pds/200909/14/55/b0026155_4aadf1267a348.png');" /></div>그림에서 볼 수 있듯이 오렌지색의 섹션과 블루색의 섹션이 각각 독립적으로 작동하도록 구성할 수 있다.<br />
단 각각의 섹션은 누가 먼저 종료하든지 #pragram omp sections 블록의 끝에는 implicit barrier가 있으므로 대기하게 된다.<br />
<br />
참고로 병렬구간내에 섹션구간만 존재하는 경우라면 #pragma omp parallel sections로 구문을 합칠 수 있다.<br />
<br />
그러면 이번에는 앞에서 loop construct때 연습했던 2가지 pi 구하는 방법(numerical integration, monte carlo simulation)을<br />
sections를 이용해서 동시에 작동시키도록 바꿔보자.<br />
<br />
<br />
<span style="FONT-SIZE: 130%"><strong>* 연습문제 (섹션별로 task를 할당하는 방법)</strong></span><br />
<div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds17.egloos.com/pds/200909/15/55/b0026155_4aae889f94925.png" width="500" height="314.247669774" onclick="Control.Modal.openDialog(this, event, 'http://pds17.egloos.com/pds/200909/15/55/b0026155_4aae889f94925.png');" /></div><br />
<br />
앞에서 Single threaded 버전으로 만들었던 소스코드들을 연달아 붙여두면 된다.<br />
우선 Numerical Integration 방법의 소스코드를 다시 보자.<br />
<br />
<pre>#include &lt;stdio.h&gt;<br />
#include &lt;stdlib.h&gt;<br />
int num_steps=1000000000; /* 10억번 : 너무 많으면 조금 줄이시길... */<br />
<br />
int main()<br />
{<br />
    int i;<br />
    double x, step, sum = 0.0;<br />
    step = 1.0/(double) num_steps;<br />
    for (i=0; i&lt;num_steps; i++) {<br />
        x = (i+0.5) * step;<br />
        sum += 4.0/(1.0 + x*x);<br />
    }<br />
    printf("PI = %.8f (sum = %.8f)\n", step*sum, sum);<br />
    return EXIT_SUCCESS;<br />
}</pre>이번에는 Monte Carlo simulation방법을 다시 보겠다.<br />
<br />
<pre>#include &lt;stdlib.h&gt;<br />
#include &lt;stdio.h&gt;<br />
#include &lt;time.h&gt;<br />
#include &lt;math.h&gt;<br />
#define LOOP_ITERATION 200000000<br />
int    hits;<br />
<br />
int main()<br />
{<br />
    int 	i;<br />
    double 	x, y, rns;<br />
    time_t	t_now;<br />
<br />
    printf("Loop iteration = %ld\n", (long)LOOP_ITERATION);<br />
    rns = 1.0/(double)RAND_MAX;<br />
<br />
    t_now = time(0);<br />
    srand((unsigned int)t_now);<br />
    for (i=0; i&lt;LOOP_ITERATION; i++) {<br />
        x = (double)rand() * rns;<br />
        y = (double)rand() * rns;<br />
        if (x*x + y*y &lt; 1) {<br />
            hits++;<br />
        }<br />
    }<br />
    printf("pi = %f\n", 4*(double)hits/LOOP_ITERATION);<br />
    return 0;<br />
}</pre>이제 이 2개의 소스코드를 합쳐야 한다. 주의할 점은 두번째 Monte Carlo simulation에서는 rand()함수 대신에 rand_r()을 사용하는 것을 잊지 말아야 한다. 항상 Non-multi-threaded model을 Multi-threaded model로 바꿀때는 MT-safe function이나 reentrant function으로 골라 써야 한다는 점이다.<br />
<br />
그리고 false-sharing문제를 피하기 위해서 동일 캐시 라인에 올라갈 수 있는 전역변수나 힙 메모리를 사용하면 안된다는 점이다. 왠만하면 쓰레드 로컬 변수에서 대부분 해결하도록 하자.<br />
<br />
그러면 둘을 #pragma omp sections로 합친 코드를 보겠다. 보기 좋게 출력부분의 수정을 했으나 기본 코드는 같다.<br />
<br />
<pre>#define _XOPEN_SOURCE   600<br />
#include &lt;stdlib.h&gt;<br />
#include &lt;unistd.h&gt;<br />
#include &lt;stdio.h&gt;<br />
#include &lt;time.h&gt;<br />
#include &lt;sys/times.h&gt;<br />
#include &lt;omp.h&gt;<br />
#ifndef LOOP_ITERATION<br />
#define LOOP_ITERATION 200000000<br />
#endif<br />
<br />
double  pi[2];<br />
int main()<br />
{<br />
    clock_t     start_m, end_m;<br />
    clock_t     sc_clk_tck = sysconf(_SC_CLK_TCK);<br />
    start_m = times(NULL);<br />
    printf("LOOP ITERATION = %ld\n",(long)LOOP_ITERATION);<br />
#ifdef _OPENMP<br />
    omp_set_num_threads(2);<br />
#endif<br />
#pragma omp parallel<br />
    {<br />
#pragma omp sections<br />
        {<br />
#pragma omp section <br />
            {<br />
                int     i;<br />
                double  x, step, hits;<br />
                float   t_elapsed;<br />
                clock_t start, end;<br />
                hits = 0.0;<br />
#ifdef _OPENMP<br />
                printf("[SECTION] integration method by thread(%d)\n", omp_get_thread_num());<br />
#else<br />
                printf("[SECTION] start integration method\n");<br />
#endif<br />
                start = times(NULL); /* get clock tick */<br />
                step = 1.0/(double) LOOP_ITERATION;<br />
                for (i=0; i&lt;LOOP_ITERATION; i++) {<br />
                    x = (i+0.5) * step;<br />
                    hits += 1.0/(1.0 + x*x);<br />
                }<br />
                pi[0] = 4 * hits * step;<br />
                end = times(NULL);<br />
                t_elapsed = (float) (end - start)/sc_clk_tck;<br />
#ifdef _OPENMP<br />
                printf("[SECTION] end integration method by thread(%d):elapsed time(%.02f sec)\n", <br />
                        omp_get_thread_num(), t_elapsed);<br />
#else<br />
                printf("[SECTION] end integration method: elapsed time(%.02f sec)\n",<br />
                        t_elapsed);<br />
#endif<br />
            }<br />
#pragma omp section<br />
            {<br />
                int     i, state, hits;<br />
                double  x, y, rns;<br />
                float   t_elapsed;<br />
                clock_t start, end;<br />
#ifdef _OPENMP<br />
                printf("[SECTION] monte carlo method by thread(%d)\n", omp_get_thread_num());<br />
#else<br />
                printf("[SECTION] start monte carlo method\n");<br />
#endif<br />
                start = times(NULL); /* get clock tick */<br />
                state = time(0);<br />
                rns = 1.0/(double)RAND_MAX;<br />
                hits = 0;<br />
                for (i=0; i&lt;LOOP_ITERATION; i++) {<br />
                    x = (double)rand_r((unsigned int *)&amp;state) * rns;<br />
                    y = (double)rand_r((unsigned int *)&amp;state) * rns;<br />
                    if (x*x + y*y &lt; 1) {<br />
                        hits++;<br />
                    }<br />
                }<br />
                pi[1] = (hits / LOOP_ITERATION) * 4;<br />
                end = times(NULL);<br />
                t_elapsed = (float) (end - start)/sc_clk_tck;<br />
#ifdef _OPENMP<br />
                printf("[SECTION] end monte carlo method by thread(%d): elapsed time(%.02f sec)\n",<br />
                        omp_get_thread_num(), t_elapsed);<br />
#else<br />
                printf("[SECTION] end monte carlo method: elapsed time(%.02f sec)\n",<br />
                        t_elapsed);<br />
#endif<br />
            }<br />
        }<br />
    }<br />
    end_m = times(NULL);<br />
    printf("integration PI = %.8f\n", pi[0]);<br />
    printf("monte carlo PI = %.8f\n", pi[1]);<br />
    printf("* Total elapsed time(%.02f sec)\n", (double)(end_m - start_m)/sc_clk_tck);<br />
    return EXIT_SUCCESS;<br />
}</pre>이제 실행을 해보자.<br />
<pre>$ gcc -o pi_sections_omp -fopenmp pi_sections.c<br />
$ time ./pi_sections_omp <br />
LOOP ITERATION = 200000000                                    <br />
[SECTION] integration method by thread(0)                     <br />
[SECTION] monte carlo method by thread(1)                     <br />
[SECTION] end integration method by thread(0):elapsed time(1.79 sec)<br />
[SECTION] end monte carlo method by thread(1): elapsed time(5.05 sec)<br />
integration PI = 3.14159265                                          <br />
monte carlo PI = 3.14161276                                          <br />
* Total elapsed time(5.05 sec)                                       <br />
<br />
real    0m5.052s<br />
user    0m6.858s<br />
sys     0m0.016s<br />
</pre>당연히 Numerical Integration이 더 빠르기 때문에 먼저 끝난다. 하지만 implicit barrier가 있기 때문에 대기하게 된다. Monte Carlo simulation은 더 오래 걸리기 때문에 전체 수행 시간은 Monte Carlo simulation이 끝나는 시간에 종료한다.<br />
<br />
* 오늘은 여기까지... 나머지는 다음 장에. (이것도 정말 힘들군요... 본문의 반말은 이해해주세요.)<br/><br/>tag : <a href="/tag/C언어" rel="tag">C언어</a>,&nbsp;<a href="/tag/MT-safe" rel="tag">MT-safe</a>,&nbsp;<a href="/tag/openmp" rel="tag">openmp</a>,&nbsp;<a href="/tag/쓰레드" rel="tag">쓰레드</a>,&nbsp;<a href="/tag/thread" rel="tag">thread</a>			 ]]> 
		</description>
		<category>책 강의 PDF,PPT</category>
		<category>C언어</category>
		<category>MT-safe</category>
		<category>openmp</category>
		<category>쓰레드</category>
		<category>thread</category>

		<comments>http://sunyzero.egloos.com/4229235#comments</comments>
		<pubDate>Tue, 15 Sep 2009 10:32:00 GMT</pubDate>
		<dc:creator>SY Kim</dc:creator>
	</item>
	<item>
		<title><![CDATA[ Fedora 11 업그레이드 후 한글입력기 작동 불가시 ]]> </title>
		<link>http://sunyzero.egloos.com/4229870</link>
		<guid>http://sunyzero.egloos.com/4229870</guid>
		<description>
			<![CDATA[ 
  Fedora11을 바로 설치하지 않고 업그레이드로 설치한 경우에<br>한글 입력기가 scim에서 ibus로 대체되지 않는 경우가 있다.<br><br>물론 scim을 그대로 사용해도 되지만, 이왕이면 brand new를 써보고 싶은 프로그래머들이 많을것이다.<br>그러나&nbsp;scim을 제거하고 ibus를 설치하고 나면 어찌된 노릇인지 KDE에서는 잘 작동하지만 <br>GNOME에서는 작동하지 않는 경우가 많다. 예를 들어 gnome-terminal, firefox ...같은 경우는 당연히 한글 입력이 안된다.<br><br>원인은&nbsp;ibus와&nbsp;연관된 패키지의 버전이 제대로 업데이트 되지 않아서이다.<br><br><strong>따라서 ibus (혹 ibus-gtk, ibus-hangul도 같이)가 설치되어있다면 일단 제거한뒤에<br>먼저 glib, glib2, glibmm24를 먼저 업데이트하고, 그 후에 ibus-gtk, ibus-hangul을 설치하라.</strong><br/><br/>tag : <a href="/tag/linux" rel="tag">linux</a>,&nbsp;<a href="/tag/fedora" rel="tag">fedora</a>,&nbsp;<a href="/tag/한글입력기" rel="tag">한글입력기</a>,&nbsp;<a href="/tag/ibus" rel="tag">ibus</a>,&nbsp;<a href="/tag/scim" rel="tag">scim</a>			 ]]> 
		</description>
		<category>Com. Science</category>
		<category>linux</category>
		<category>fedora</category>
		<category>한글입력기</category>
		<category>ibus</category>
		<category>scim</category>

		<comments>http://sunyzero.egloos.com/4229870#comments</comments>
		<pubDate>Tue, 08 Sep 2009 09:55:13 GMT</pubDate>
		<dc:creator>SY Kim</dc:creator>
	</item>
	<item>
		<title><![CDATA[ C언어: OpenMP를 이용한 멀티 쓰레드 프로그래밍 HOWTO #1 ]]> </title>
		<link>http://sunyzero.egloos.com/4227785</link>
		<guid>http://sunyzero.egloos.com/4227785</guid>
		<description>
			<![CDATA[ 
  <strong><span style="COLOR: #ff6600; FONT-SIZE: 130%">C언어: OpenMP를 이용한 멀티 쓰레드 프로그래밍 HOWTO #1</span></strong><br />
<br />
<strong><span style="FONT-SIZE: 130%">0. 시작하기에 앞서</span></strong><br />
병렬처리 프로그래밍을 위한 기법중 OpenMP를 이용하는 방법에 대해서 보도록 할 것이다.<br />
<br />
불과 10여년 전만 하더라도 병렬 처리를 위한 프로그래밍은 사치에 가까웠고 CPU를 여러 개 장착한 PC는 보기 힘들었다. 하지만 최근 CPU의 발달은 Hz 속도를 거의 한계에 다다르게 하였다:현재 한계는 약 3GHz 전후이다. 물론 이론상으로는 5GHz도 가능하지만 엄청난 발열과 전력 소비로 인해서 효과적이지 못하다. 그리하여 CPU 벤더들은 Hz속도의 경쟁보다는 복수개의 코어로 진화할 수 밖에 없게 되었다.<br />
<br />
그러나 기존 프로그램은 멀티 코어를 지원하도록 작성된 경우가 거의 없기에 심각한 문제로 대두되기 시작했다. 앞으로의 추세는 병렬 처리를 위한 멀티 쓰레드 프로그래밍이지만 이 분야가 결코 쉽지 않다는 점이 발목을 잡는다. 일반적인 native multi-thread programming은 고급 레벨이 아니고서는 작성하기 힘들어서 해당 분야 전공자가 아닌 응용 프로그래머는 손대기가 힘들었다. 예를 들어 물리나 수학, 화학에서 C, C++, fotran을 이용한 수치계산을 한다고 가정해보자. 연구중인 앨거리즘이나 휴리스틱 기법을 테스트하려고 하는데 더 빠른 계산을 위해서 고급 멀티 쓰레드 프로그래밍을 배우려고 한다면 얼마나 걸릴 것인가? 물론 천재적인 사람이라면 금방 가능하겠지만 일반적으로는 프로그래밍 배우는데 몇개월에서 반 년이상을 투자해야 제대로 할 수 있을 것이다 그러나 이는 배보다 배꼽이 더 큰 문제가 되어버린다.<br />
<br />
따라서 간단한 멀티 쓰레드 프로그래밍을 구현하기 위한 좀 더 쉬운 adaptive multi-thread programming 기법으로 OpenMP가 나오게 되었다. OpenMP는 뛰어난 프로그래밍 실력을 아니더라도 단 몇일만 투자해도 쉽게 멀티 쓰레드 프로그래밍을 할 수 있게 도와 준다.<br />
<hr><br />
<br />
<br />
<span style="FONT-SIZE: 130%"><strong>1. OpenMP란?</strong></span><br />
멀티 쓰레드 프로그래밍을 간단하게 하기 위해서 개발된 기법.<br />
OpenMP는 컴파일러 지시자만으로서 블록을 멀티 쓰레드로 작동하게 할 수 있다.<br />
현재 대부분의 컴파일러(e.g. gcc, Intel Compiler, Microsoft visual studio, Sun studio... 등등)는 OpenMP지시자를 지원한다.<br />
<br />
<ul><li>지원언어 : C, C++, Fortran</li><li>표준 : <a href="http://www.openmp.org/">http://www.openmp.org</a>  (현재 OpenMP 3.0-May, 2008이 나와있지만 이 문서는 2.5 spec을 기준으로 설명한다. 3.0은 나중에 따로 추리도록 하겠다.)</li></ul><br />
<br />
<strong><span style="FONT-SIZE: 130%">2. OpenMP의 시작</span></strong><br />
<br />
코드에 #pragma omp 로 시작하는 지시어를 삽입한다.<br />
<br />
<pre>#pragma omp parallel [clause[ [, ]clause] ...] new-line<br />
structured-block<br />
</pre><br />
그러면 간단한 Hello world 예제를 OpenMP 멀티 쓰레드 프로그램으로 만들어 보자. <br />
우선 아래의 프로그램은 그냥 일반 single thread 프로그램이다.<br />
<br />
<pre><code># include &lt;stdio.h&gt;<br />
int main() <br />
{<br />
    printf("Hello world\n");<br />
    return 0;<br />
}</pre>여기에 OpenMP 지시어를 추가하여 멀티 쓰레드 프로그램으로 바꾸면 아래처럼 작성된다.<br />
<pre># include &lt;stdio.h&gt;<br />
int main() <br />
{<br />
#pragma omp parallel<br />
    {<br />
        printf("Hello world\n");<br />
    }<br />
    return 0;<br />
}</code></pre>실제로 엄청 간단하지 않은가? 그러면 컴파일 해보자.&nbsp;<br />
단 주의할 점은 OpenMP를 인식할 수 있도록 컴파일러에 옵션을 넣어줘야 한다.<br />
<ul><li>OpenMP 컴파일 옵션 : <strong>-fopenmp</strong> (GCC인 경우), <strong>-openmp</strong> (Intel C Compiler인 경우)</li><li>윈도우즈용 비주얼 스튜디오는 알아서 컴파일 해준다.</li><br />
</ul><pre>$ gcc -fopenmp -o helloworld_omp helloworld.c<br />
$ ./helloworld_omp<br />
Hello world<br />
Hello world</pre>파일명이 helloworld.c라고 가정할 때 위와 같이 컴파일하면 된다.<br />
make를 쓸줄 안다면 "make CFLAGS=-fopenmp helloworld"라고 해도 된다.<br />
<br />
그리고 실행해보면 필자의 플랫폼에서는 2번 출력되었다. 하지만 3번이나 4번 출력되는 사람도 있을것이다.<br />
이는 기본적으로 OpenMP가 물리적 CPU개수만큼 쓰레드를 만들기 때문이다. 즉 필자는 듀얼코어를 쓰고 있다.<br />
<br />
그러면 쓰레드의 개수를 임의로 늘릴 수는 없는가? 가능하다. 환경변수나 omp_set_num_threads(int)함수를 이용하면 된다.<br />
그러면 임의로 쓰레드의 개수를 4개로 만들어 보겠다.<pre># include &lt;stdio.h&gt;<br />
# include &lt;omp.h&gt;<br />
int main() <br />
{<br />
    omp_set_num_threads(4);<br />
#pragma omp parallel<br />
    {<br />
        printf("Hello world\n");<br />
    }<br />
    return 0;<br />
}</pre>앞의 예제에서 달라진 것은 omp.h 헤더를 포함한 것과 병렬구간을 시작하기 전에 omp_set_num_threads(4)를 실행한 것이다.<br />
혹은 OMP_NUM_THREADS라는 환경변수를 세팅해도 된다. 본쉘이라면 "export OMP_NUM_THREADS=4"으로 명령하면 되겠다.<br />
<br />
<br />
<br />
<strong><span style="FONT-SIZE: 130%">3. OpenMP를 이용한 work-sharing 모델<br />
</span></strong>이번에는 OpenMP를 이용한 work-sharing 모델 분류를 배우도록 하겠다. 분류는 3가지 정도 된다.(OpenMP 3.0에서는 worksharing라는 지시어가 추가되었는데 fortran전용이며 이 문서는 오래전 2.5때 쓰여진 것이라 여기서는 설명하지 않는다.)<br />
<ul><li>loop construct</li><li>section construct</li><li>single construct</li></ul>loop construct는 for 루프문을 병렬처리하는 기법이고, section construct 는 블록단위로 병렬처리를 하도록 하는 방법이다. single은 병렬처리구간에서 한번만 실행되어야 하는 블록을 지정할 때 사용한다.<br />
<br />
<br />
<strong><span style="FONT-SIZE: 130%">4. Loop construct<br />
</span></strong><br />
<span style="COLOR: #3333ff"><strong>4.a Loop consturct 기본 형태</strong></span><br />
loop construct는 for 루프문을 병렬처리 한다고 했다. 기초적인 예제를 보자.<br />
<br />
<div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds17.egloos.com/pds/200909/07/55/b0026155_4aa4ca4591280.png" width="500" height="290.816326531" onclick="Control.Modal.openDialog(this, event, 'http://pds17.egloos.com/pds/200909/07/55/b0026155_4aa4ca4591280.png');" /></div>예제에서 보면 총 8번의 루프동안 2개의 쓰레드로 병렬처리하는 것을 보여주고 있다. 물론 쓰레드가 4개라면 2개씩 처리할 것이다.<br />
그런데 위에는 병렬처리 구간이 for 루프 구간과 겹치므로 #pragma omp parallel 과 #pragma omp for를 합칠 수 있다.<br />
<br />
<div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds17.egloos.com/pds/200909/07/55/b0026155_4aa4cb7eee5a0.png" width="500" height="285.445420326" onclick="Control.Modal.openDialog(this, event, 'http://pds17.egloos.com/pds/200909/07/55/b0026155_4aa4cb7eee5a0.png');" /></div>합치니까 매우 깔끔해 보인다.<br />
그런데 루프문 안에서 printf()의 출력결과는 단지&nbsp;index만 출력하고 있으므로&nbsp;어떤 쓰레드가 어떤 결과를 출력하는지<br />
알 수 있는 방법이 없다. 그래서 쓰레드 번호를 찍는 것을 추가해 보도록 하자.<br />
<br />
<div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds16.egloos.com/pds/200909/07/55/b0026155_4aa4cdaef2fd4.png" width="500" height="286.072772898" onclick="Control.Modal.openDialog(this, event, 'http://pds16.egloos.com/pds/200909/07/55/b0026155_4aa4cdaef2fd4.png');" /></div>omp_get_thread_num()은 쓰레드 번호를 출력해 주므로 각각의 쓰레드가 어떻게 출력하는지 나타난다.<br />
그러면 수정된 예제를 컴파일 후 실행해보면 결과는 어떻게 나올까? (위의 파일명은 loop_exam1.c라고 하자)<br />
<pre>$ gcc -fopenmp -o loop_exam1 loop_exam1.c<br />
$ ./loop_exam1<br />
[1-4] Hello world<br />
[1-5] Hello world<br />
[1-6] Hello world<br />
[1-7] Hello world<br />
[0-0] Hello world<br />
[0-1] Hello world<br />
[0-2] Hello world<br />
[0-3] Hello world<br />
</pre><br />
결과를 보면 0번 쓰레드가 0~3까지 처리하고, 1번 쓰레드가 4~7까지 처리하는 것으로 나온다. 위에는 이쁘지 않게 순서가 거꾸로 출력되었지만, 이 순서는 항상 같으리라고 보장할수는 없다. 따라서 출력 순서는 그냥 신경쓰지 말자.<br />
<br />
<br />
<strong><span style="COLOR: #3333ff">4.b 병렬 처리시 주의점</span></strong><br />
이번에는 예제를 하나 풀어보면서 Loop construct를 적용할 때 주의점도 보겠다. 간혹 잘못된 멀티 쓰레드 적용은 false-sharing문제나 멀티 쓰레드가 계산한 결과의 reduction처리를 빼먹어서 잘못된 값이 나올 수 있기 때문이다. <br />
<br />
풀어볼 예제는 3.141592의 숫자로 알고 있는 파이(pi) 계산을 멀티 쓰레드로 만드는 것이다. pi 계산을 선택한 이유는 쉽게 CPU를 혹사시킬 수 있는 방법이기 때문이다. 우선 Single threaded 버전을 한번 보자.<br />
<br />
<pre>#include &lt;stdio.h&gt;<br />
#include &lt;stdlib.h&gt;<br />
int num_steps=1000000000; /* 10억번 : 너무 많으면 조금 줄이시길... */<br />
<br />
int main()<br />
{<br />
    int i;<br />
    double x, step, sum = 0.0;<br />
    step = 1.0/(double) num_steps;<br />
    for (i=0; i&lt;num_steps; i++) {<br />
        x = (i+0.5) * step;<br />
        sum += 4.0/(1.0 + x*x);<br />
    }<br />
    printf("PI = %.8f (sum = %.8f)\n", step*sum, sum);<br />
    return EXIT_SUCCESS;<br />
}</pre>위의 pi 구하는 소스코드가 어떻게 나왔는지는 그냥 양념으로 알아두자.<br />
(생각없이 그냥 그런가보다 할 수도 있지만 이런 계산법을 알아두는 것도 고등학교때 추억을 떠올리는 즐거움이 있다.)<br />
<br />
우선 위의 pi를 구하는 공식은 그레고리 급수를 전개하기 전까지의 방법이다.<br />
그레고리는 계산의 편리함을 위해서 급수로 전개했지만, 컴퓨터가 있으니 그냥 적분값을 무수히 계산시키면 쉽다.<br />
<div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds16.egloos.com/pds/200909/10/55/b0026155_4aa8964dc351c.png" width="500" height="204.365079365" onclick="Control.Modal.openDialog(this, event, 'http://pds16.egloos.com/pds/200909/10/55/b0026155_4aa8964dc351c.png');" /></div><br />
이제 y=1/(1+x^2)의 0~1까지의 정적분에 4를 곱하면 pi의 값이 나온다는 것을 알았다. 이를 컴퓨터를 이용해서 계산할 때는 해당 구간을 무수히 작은 사각형으로 쪼개서 더하는 numerical integration으로 구하면 쉽다. 즉 0~1까지의 x의 값을 n개로 쪼갠 뒤에 y와 곱한 직사각형을 더할 것이다.(이때 x의 좌표값은 중간값을 취하는 Midpoint rule을 적용하므로 0.5씩 더했다.)<br />
<br />
하지만 정의역(x)의 범위인 0~1에 대해 y의 범위는 4~2가 나오지만, numerical integration을 위해서 <br />
정의역을 n개로 나누면 n이 작을수록 한 개의 직사각형의 크기는 엄청 작아진다.(물론 정밀도가 올라간다.)<br />
<br />
하지만 소수점으로 너무 작아지는 x값을 사용하면 계산에도 무리가 있으므로 직사각형의 높이(y)는 그대로 두고<br />
x만 n번을 곱한 수로 치환하면 x는 1이 된다. 그리고 n개의 직사각형을 더한 뒤 n으로 다시 나누면&nbsp;pi가 나오게 된다.<br />
<br />
이제 수학적인 부분을 다 설명했으므로 위의 pi계산 프로그램을 single thread버전으로 돌려보자.(파일명을 pi_numerical_integration.c라고 지정했다.)<br />
예제 파일 : <a href="http://pds17.egloos.com/pds/200910/21/55/pi_numerical_integration.c">pi_numerical_integration.c</a><pre>$ make pi_numerical_integration<br />
$ time ./pi_numerical_integration<br />
PI = 3.14159265 (sum = 2513274122.87223005)<br />
<br />
real    0m8.994s<br />
user    0m8.993s<br />
sys     0m0.001s<br />
</pre>time명령으로 수행 시간을 보니 약 8.994초이며 user/sys영역에서 소비한 CPU타임도 이와 같은 것을 볼때 1개의 CPU만 사용했음을 알 수 있다. 이번에는 OpenMP를 적용하도록 소스코드를 수정하겠다. 중간에 #pragma omp parallel for 구문을 넣어주었다.<pre>#include &lt;stdio.h&gt;<br />
#include &lt;stdlib.h&gt;<br />
int num_steps=1000000000; /* 10억번 : 너무 많으면 조금 줄이시길... */<br />
<br />
int main()<br />
{<br />
    int i;<br />
    double x, step, sum = 0.0;<br />
    step = 1.0/(double) num_steps;<br />
#pragma omp parallel for<br />
    for (i=0; i&lt;num_steps; i++) {<br />
        x = (i+0.5) * step;<br />
        sum += 4.0/(1.0 + x*x);<br />
    }<br />
    printf("PI = %.8f (sum = %.8f)\n", step*sum, sum);<br />
    return EXIT_SUCCESS;<br />
}</pre>이제 컴파일 후 실행해야한다. 컴파일은 make에&nbsp;CFLAGS (Compiler flags) 변수에 openmp기능을 넣고 컴파일한다. (make를 이용하는 것이 쉬워서인데 gcc 명령어를 직접 타이핑하는게 좋다면 gcc -o pi_numerical_integration -fopenmp pi_numerical_integration.c 를 모두 타이핑하면 된다.)<br />
<pre>$ make CFLAGS="-fopenmp" pi_numerical_integration<br />
$ time ./pi_numerical_integration<br />
PI = 2.26234349 (sum = 1809874795.18165779)<br />
<br />
real    0m50.394s<br />
user    1m38.686s<br />
sys     0m0.013s<br />
</pre><br />
멀티 쓰레드를 사용했는데 실제 수행 시간은 50여초나 걸렸고, CPU는 무려 1분 38초 넘게 사용되었다. 어째서 수행 시간이 더 늘어난 것일까? 더군다나 pi값도 틀리게 출력된다.<br />
<div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds15.egloos.com/pds/200911/05/55/b0026155_4af2e312d52c8.png" width="500" height="91.2305516266" onclick="Control.Modal.openDialog(this, event, 'http://pds15.egloos.com/pds/200911/05/55/b0026155_4af2e312d52c8.png');" /></div><br />
수행시간의 오버헤드는 True-sharing, <a title="" href="http://minjang.egloos.com/1848130" target="_blank">False-sharing</a> 때문에 발생한다. True-sharing은 복수개의 CPU가 공유 변수인 x, sum을 접근하려고 할 때 발생시키는 cache miss 오버헤드이며, 이는 정확한 값을 유지하려고 하는 동기화 오버헤드를 가져온다. False-sharing은 예제의 x와 sum은 연속된 공간에 있을 확률이 높기에(<span style="font-size:85%;">64 byte cache line을 사용하는 최근 Intel CPU에서는 x와 sum이 같은 cache line에 있을 가능성이 높다.</span>) x나 sum 둘 중에 하나만 update되어도 cache line단위로 업데이트 되어 다른 CPU의 cache line을 무효(invalid) 시켜버리는 문제를 가져온다. 특히 위의 예제처럼 x, sum이 종종 업데이트 되는 계산구조라면 cache miss가 비약적으로 증가하여 엄청난 페널티가 있으므로 이를 회피하도록 설계를 바꿔야만 한다. <span style="color:#0000ff;">- object(minjang.egloos.com)님의 지적</span><br />
<br />
따라서 프로그래머는 True-sharing, False sharing을 피하기 위해 멀티 쓰레드가 사용하는 메모리 공간은 서로 공유하지 않도록 설계하거나, 혹은 lock을 사용하거나, 동일 cache line에 걸칠 가능성이 있는 데이터 구조에는 padding을 넣어 설계하는 방법등이 있다. (자세한 False-sharing 문제는 OS관련 책에 나오니 그 부분을 참고)<br />
<br />
위 예제의 경우도 x와 sum을 공유하지 않도록 로컬 변수로 만들어 버리면 해결된다. 마침 OpenMP에서는 private(변수, ...)이란 지시어를 제공하고 있는데, private에 선언된 변수는 병렬처리 구간에서 같은 이름을 가지는 새로운 스택 변수로 선언되어 공유를 피할 수 있게 된다.<br />
<br />
그러면 private(x, sum)으로 선언해야 할까? 아니다. x는 private으로 선언하는 것이 맞지만 sum값은 작동 방법이 조금 다르다. sum은 각각의 쓰레드가 작동하여 따로 계산할지라도 끝에서는 각 쓰레드의 값을 합산하여 보정해야 되기 때문이다. 이를 위해서 보정하는 지시어인 reduction(...)이 제공된다. reduction(op:변수...)로 선언하며 op에 해당하는 operand로 reduction시켜준다. 또한 reduction에 선언된 변수는 자동으로 private변수가 되어 공유를 피할 수 있게 해준다. 위 예제는 덧셈이므로 op부분에 +를 넣어주면 되겠다. 플러스 외에 사용가능한 reduction operand는 다음과 같다.<br />
<div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds17.egloos.com/pds/200911/06/55/b0026155_4af2e8f7424ff.png" width="500" height="176.090468498" onclick="Control.Modal.openDialog(this, event, 'http://pds17.egloos.com/pds/200911/06/55/b0026155_4af2e8f7424ff.png');" /></div>그러면 private()과 reduction()을 적용하여 수정된 예제를 보도록 하자.<br />
예제 파일 : <a href="http://pds17.egloos.com/pds/200910/21/55/pi_numerical_integration_omp.c">pi_numerical_integration_omp.c</a><pre>#include &lt;stdio.h&gt;<br />
#include &lt;stdlib.h&gt;<br />
int num_steps=1000000000; /* 10억번 : 너무 많으면 조금 줄이시길... */<br />
<br />
int main()<br />
{<br />
    int i;<br />
    double x, step, sum = 0.0;<br />
    step = 1.0/(double) num_steps;<br />
#pragma omp parallel for private(x) reduction(+:sum)<br />
    for (i=0; i&lt;num_steps; i++) {<br />
        x = (i+0.5) * step;<br />
        sum += 4.0/(1.0 + x*x);<br />
    }<br />
    printf("PI = %.8f (sum = %.8f)\n", step*sum, sum);<br />
    return EXIT_SUCCESS;<br />
}</pre>이제 컴파일후 실행해보면 real time이 약 CPU개수만큼 줄어드는 것을 볼 수 있다. <br />
듀얼코어인 필자 시스템에서는 절반으로 줄어들었다.<br />
<br />
<br />
<br />
<strong><span style="COLOR: #3333ff">4.c 스케줄링</span></strong><br />
앞의 for문을 여러 쓰레드가 나눌때는 정확하게 1/n (n=# of threads)로 나누는 것을 볼 수 있다.<br />
이렇게 스케줄링하는 방법은 편리하긴 하지만 때에 따라서는 언밸런스를 가져올 수 있으므로 OpenMP는 각 반복작업(iteration)을 스케줄링 하는 3가지 방법을 제공한다. 이는 #pragram omp for의 지시어의 뒤에 붙여서 사용한다.<br />
<ul><li>schedule(static [, x])</li><li>schedule(dynamic [, x])</li><li>schedule(guided [, x])</li></ul><br />
<div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds15.egloos.com/pds/200911/06/55/b0026155_4af2e9fb389a9.png" width="500" height="136.482939633" onclick="Control.Modal.openDialog(this, event, 'http://pds15.egloos.com/pds/200911/06/55/b0026155_4af2e9fb389a9.png');" /></div><br />
static 스케줄링은 round-robin 방식으로 각 쓰레드에게 작업을 할당한다.<br />
뒤의 x은 한번에 할당할 chunk의 개수로 생략하면 1로 지정된다.<br />
static 스케줄은 OpenMP의 기본 스케줄 방식이며 기본값일 경우는 x는 전체 iteration을 쓰레드의 개수로 나눈 값이 지정된다.<br />
이 방식은 각 iteration의 완료 시간이 규칙적일때 모든 쓰레드들은 비슷한 시간에 종료하게 된다.<br />
만일 각 iteration의 완료 시간이 불규칙적이라면 static 스케줄링은 좋지 못한 결과를 가져올 수도 있다.<br />
<br />
dynamic 스케줄링은 작업을 빨리 마치고 idle상태인 쓰레드에게 chunk개수만큼씩을 할당한다.<br />
마찬가지로 x가 생략되면 chunk의 개수는 1로 지정된다.<br />
dynamic은 각 chunk들의 작업 완료 시간이 불규칙적일 때 매우 유용하다.<br />
왜냐하면 쓰레드 중에 작업을 빨리 끝내는 경우가 있다면 다음 작업을 빨리 할당받아 실행할 가능성이 높기 때문이다.<br />
(단 주의할 점은 너무 적은 chunk의 개수를 할당하면 스케줄링하는 오버헤드가 더 커질 수 있으니 적당한 값을 써야 한다.)<br />
<br />
guided는 dynamic과 비슷하여 idle 상태인 쓰레드에게 먼저 chunk를 할당한다.<br />
다만 다른 점은 dynamic은 chunk의 개수가 고정인데 반해, guided는 chunk의 개수를 큰 수에서 점점 작은 수로<br />
줄여나간다는 점이다. guided는 chunk를 지수적으로 감소시키되 지정된 x의 크기 이하로는 감소시키지 않는다.(x는 생략시 1이다)<br />
chunk의 크기는 다음 공식을 따른다.<br />
<div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds15.egloos.com/pds/200911/06/55/b0026155_4af2ee6f243c4.png" width="500" height="74.6173469388" onclick="Control.Modal.openDialog(this, event, 'http://pds15.egloos.com/pds/200911/06/55/b0026155_4af2ee6f243c4.png');" /></div><br />
<br />
<strong><span style="FONT-SIZE: 130%">-- 오늘은 여기까지... 다음 게시물로 --</span></strong><br />
<hr><br />
History<br />
* 2009.09.10 처음판<br />
* 2009.09.22 0번 소개글 등록, loop construct에 약간 살을 더 붙임<br />
* 2009.10.21 예제 파일을 따로 등록<br />
* 2009.11.05 reduction, schedule 일부 추가 (그림)<br />
* 2009.11.06 False-sharing부분 수정, True-sharing 추가 - object(minjang.egloos.com)님의 지적 <br/><br/>tag : <a href="/tag/C언어" rel="tag">C언어</a>,&nbsp;<a href="/tag/MT-safe" rel="tag">MT-safe</a>,&nbsp;<a href="/tag/omp" rel="tag">omp</a>,&nbsp;<a href="/tag/openmp" rel="tag">openmp</a>,&nbsp;<a href="/tag/쓰레드" rel="tag">쓰레드</a>,&nbsp;<a href="/tag/thread" rel="tag">thread</a>			 ]]> 
		</description>
		<category>책 강의 PDF,PPT</category>
		<category>C언어</category>
		<category>MT-safe</category>
		<category>omp</category>
		<category>openmp</category>
		<category>쓰레드</category>
		<category>thread</category>

		<comments>http://sunyzero.egloos.com/4227785#comments</comments>
		<pubDate>Sat, 05 Sep 2009 14:42:50 GMT</pubDate>
		<dc:creator>SY Kim</dc:creator>
	</item>
</channel>
</rss>
