<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>hyelie</title>
    <link>https://hyelie.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Tue, 30 Jun 2026 12:03:34 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>hyelie</managingEditor>
    <image>
      <title>hyelie</title>
      <url>https://tistory1.daumcdn.net/tistory/5424886/attach/c74453618fc94e7f8458be7b10a85586</url>
      <link>https://hyelie.tistory.com</link>
    </image>
    <item>
      <title>[Study] 가상 면접 사례로 배우는 대규모 시스템 설계 기초 1 - 11장 뉴스피드 시스템</title>
      <link>https://hyelie.tistory.com/entry/Study-%EA%B0%80%EC%83%81-%EB%A9%B4%EC%A0%91-%EC%82%AC%EB%A1%80%EB%A1%9C-%EB%B0%B0%EC%9A%B0%EB%8A%94-%EB%8C%80%EA%B7%9C%EB%AA%A8-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EC%84%A4%EA%B3%84-%EA%B8%B0%EC%B4%88-1</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;스터디를 진행하기로 했다. 책은 유명한 가상 면접 사례로 배우는 대규모 시스템 설계 기초 1이다. 책에는 단순하게 아키텍처 정도만 설명되어 있고 부하를 실제로 어느 정도로 받을 수 있는지 실제 코드와 아키텍처를 간단하게 구성하면서 배워보기로 했다. (다른 스터디 멤버들은 어떻게 생각할지 모르겠지만)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/hyelie/Virtual-Interview-01&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/hyelie/Virtual-Interview-01&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1753709032651&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - hyelie/Virtual-Interview-01&quot; data-og-description=&quot;Contribute to hyelie/Virtual-Interview-01 development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/hyelie/Virtual-Interview-01&quot; data-og-url=&quot;https://github.com/hyelie/Virtual-Interview-01&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dkZ77s/hyZrvFrS10/2vLz2Gh4HB3H7h7hd8ZKs1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/3r8dR/hyZqV5pmNM/yVaoFPt14ED1envcSmQVHK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/hyelie/Virtual-Interview-01&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/hyelie/Virtual-Interview-01&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dkZ77s/hyZrvFrS10/2vLz2Gh4HB3H7h7hd8ZKs1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/3r8dR/hyZqV5pmNM/yVaoFPt14ED1envcSmQVHK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - hyelie/Virtual-Interview-01&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Contribute to hyelie/Virtual-Interview-01 development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1회차&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드만 짰고, 실제로 인프라(NCP)에 올리는 건 다음 회차에 진행할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 책에서 한 것과 실 구현은 조금 다르다. 해당 내용은 &lt;a href=&quot;https://github.com/hyelie/Virtual-Interview-01?tab=readme-ov-file#-core-flows&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;GitHub&lt;/a&gt;에 올려둠.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;포스팅 생성 시
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;fan-out 및 news feed cache, post cache, user cache에 모두 값을 넣음&lt;/li&gt;
&lt;li&gt;인데... 나는 news feed cache에는 넣고 post cache, user cache에는 값을 넣지 않았다.&lt;/li&gt;
&lt;li&gt;만약 해당 글이 자주 읽히지 않는 글이라면, cache에 넣지 않는 것이 맞다.&lt;/li&gt;
&lt;li&gt;해당 글이 자주 읽힐 글이라면, 읽을 때 caching하고 다음 번에는 cache에서 가져온다.&lt;/li&gt;
&lt;li&gt;news feed cache에 넣은 이유는, 해당 cache는 간단할 뿐더러 특정 사용자가 어떤 news feed를 읽어야 하는지 알아오는 단계가 제일 병목이라 생각했기 때문. post, user는 PK id로 쉽게 찾아올 수 있다고 생각했다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;포스트 조회 시
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;news feed cache 조회, 없으면 DB에서 조회 및 caching&lt;/li&gt;
&lt;li&gt;news feed에 들어갈 post, user 각각을 cache에서 조회, 없는 것은 DB에서 조회 및 caching&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이정도이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 번에 할 일&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;infra (NCP)에 server, worker 등을 올리기&lt;/li&gt;
&lt;li&gt;RabbitMQ는 지금 1대만 쓰는데 여러 대 사용해서 locking 등 이슈 없는지 보기&lt;/li&gt;
&lt;li&gt;Redis를 cache 용도로 쓰고 있는데, lock 문제 없는지 보기&lt;/li&gt;
&lt;li&gt;monitoring system 구축하기 (각 WAS, RabbitMQ message consume하는 Worker, Redis, RabbitMQ) - CPU 사용량, memory 사용량, storage 사용량 등등 그래프로 볼 수 있게&lt;/li&gt;
&lt;li&gt;이후 jmeter같은 요청을 1초에 몇천 ~ 몇만 건 정도 보내서 부하 테스트하기&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Project/Study</category>
      <author>hyelie</author>
      <guid isPermaLink="true">https://hyelie.tistory.com/698</guid>
      <comments>https://hyelie.tistory.com/entry/Study-%EA%B0%80%EC%83%81-%EB%A9%B4%EC%A0%91-%EC%82%AC%EB%A1%80%EB%A1%9C-%EB%B0%B0%EC%9A%B0%EB%8A%94-%EB%8C%80%EA%B7%9C%EB%AA%A8-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EC%84%A4%EA%B3%84-%EA%B8%B0%EC%B4%88-1#entry698comment</comments>
      <pubDate>Mon, 28 Jul 2025 22:32:42 +0900</pubDate>
    </item>
    <item>
      <title>[Git] 환경 분리</title>
      <link>https://hyelie.tistory.com/entry/Git-%ED%99%98%EA%B2%BD-%EB%B6%84%EB%A6%AC</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Git 계정 2개 이상 사용하고, 특정 폴더에서만 다른 Git 계정 쓰는 방법&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1.&amp;nbsp;폴더(Local)&amp;nbsp;단위&amp;nbsp;계정&amp;nbsp;설정&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원하는 폴더(또는 해당 git 저장소)로 이동해서 아래 명령어를 입력하세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1753540361678&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;git config --local user.name &quot;원하는이름&quot;
git config --local user.email &quot;원하는메일@example.com&quot;

# 설정 확인
git config user.name
git config user.email&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. SSH Key 분리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;push도 별도 github 계정으로 하고 싶다면, 해당 계정용 SSH Key를 새로 만들고, 원격 repo 주소도 SSH로 맞춰줘야 함.&lt;br /&gt;&lt;br /&gt;1. SSH 키 생성 및 해당 public key르f GitHub 계정에 등록&lt;/p&gt;
&lt;pre id=&quot;code_1753540438719&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ssh-keygen -t ed25519 -C &quot;your_email@example.com&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2.&amp;nbsp;~/.ssh/config 파일 설정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래처럼 Host명을 구분해서 설정&lt;/p&gt;
&lt;pre id=&quot;code_1753540492506&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 계정 1
Host github-main
  HostName github.com
  User git
  IdentityFile ~/.ssh/id_ed25519_main
  IdentitiesOnly yes

# 계정 2
Host github-sub
  HostName github.com
  User git
  IdentityFile ~/.ssh/id_ed25519_sub
  IdentitiesOnly yes&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;이후 sub계정 사용 git folder에서 아래 명령어 쓰면 ssh-config에서 `github-sub`를 찾아 ssh 정보를 가져오고, 해당 repo에 접근 및 인증을 진행한다.&lt;/p&gt;
&lt;pre id=&quot;code_1753540576907&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;git remote set-url origin git@github-sub:원하는이름/repo.git&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>DevOps/Git</category>
      <author>hyelie</author>
      <guid isPermaLink="true">https://hyelie.tistory.com/697</guid>
      <comments>https://hyelie.tistory.com/entry/Git-%ED%99%98%EA%B2%BD-%EB%B6%84%EB%A6%AC#entry697comment</comments>
      <pubDate>Sat, 26 Jul 2025 23:36:56 +0900</pubDate>
    </item>
    <item>
      <title>Java 면접대비 질문</title>
      <link>https://hyelie.tistory.com/entry/Java-%EB%A9%B4%EC%A0%91%EB%8C%80%EB%B9%84-%EC%A7%88%EB%AC%B8</link>
      <description>&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;Data type&lt;/h3&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333;&quot; data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;종류&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;string ?&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333;&quot; data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;primitive : stack에 저장. byte, char, short, int, long, float, double, boolean&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;reference : 주소값 가리킴.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;string : reference이지만 primitive처럼 동작. immutable이기 때문&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;new로 생성: heap에 새 객체, literal : &lt;span style=&quot;background-color: #fafafa; color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;string constant pool에서 intern() method 호출&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;Pass by value&lt;/h3&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333;&quot; data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;primitive, reference, wrapper, string은 각각 어떻게 넘어가는지&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333;&quot; data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;primitive는 값 자체를 복사해 넘김&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;reference는 참조하는 주소를 복사해서 넘김&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;string을 넘긴 후 assign하면 주소가 바뀐 것이기 때문에 원래 값은 불변. immutable이기 때문.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;Identity &amp;amp; Equality &amp;amp; hashCode()&lt;/h3&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333;&quot; data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각각의 정의&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333;&quot; data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;identity : 참조 비교. 동일한 객체를 가리키는지&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;equality : logical equality. 내용이 같은지&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;override하지 않는 경우 equals() method가 작동, ==와 같게 동작. (identity 비교)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;address를 hash해 추출한 값. 사용 이유 : hash 값을 사용하느s collection에서 사용하기 때문.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1) hashCode() 비교 -&amp;gt; 이후 equals() 비교&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;객체 &amp;amp; class&lt;/h3&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333;&quot; data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체, class, instance?&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;method signature?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;overloading&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;access modifier&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333;&quot; data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;객체란 개념을 추상화하고 모델링한 요소. state와 behavior를 가짐.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;class는 instance를 만들기 위한 설계도, instance는 class라는 설계도로 만들어진 메모리에 할당된 실체.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;method signature : 이름과 parameter로 identify&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;overloading : 같은 이름의 method 여러 개 정의&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;access modifier : default, private, protected, public&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;Static &amp;amp; Final&lt;/h3&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333;&quot; data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각각의 정의,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;final 종류 및 정의&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333;&quot; data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;static : 모든 instance에서 같은 값, class가 memory에 올라갈 때 heap으로 올라감.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;final variable : 상수&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;static final : class의 유일 상수.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;final method : override 불가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;final class : extend 불가&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;Inheritance&lt;/h3&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333;&quot; data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;overriding&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다중상속&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333;&quot; data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;parent method를 재정의, dynamic binding. private override 시, 그냥 새로 정의 한 것이 됨.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다중상속 : diamond problem, A-B, A-C, B-D, C-D면 D는 B/C 중 골라 상속해야 하는데 뭘 고를지 모름.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;Polymorphism&lt;/h3&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333;&quot; data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;overloading vs overriding&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;static의 overloading vs overriding&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333;&quot; data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;overloading : 이름만 같게, static polymorphism, (정해짐)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;overriding : 부모 method를 child method에서 재정의해 dynamic binding하는 것. (dynamic polymorphism) runtime에 어떤 게 실행될지 결정됨. upcasting / downcasting&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;static은 overloading만 가능.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;abstract class, interface&lt;/h3&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333;&quot; data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추상화의 정의&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;abstract class 정의, interface 정의&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333;&quot; data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;abstraction : 불필요한 정보를 숨기고 중요한 정보만을 보여주는 것.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체에서 : 공통된 속성, 행위 추출하는 것.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;abstract class : abstract 로 정의됐거나 method 중 하나가 abstract인 것. extend한 것에서 abstract method를 구현 강제시킨다. instantiation 불가.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;interface : 모든 method가 abstract method. default, static 정도는 가질 수 있다. method 구현 강제.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- 같은 default method를 interface를 다중상속하는 경우 override 필수.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;둘 다 abstraction, upcasting으로 polymorphism 구현.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;abstract class는 상속 관계, 따라서 확장에 주로 사용.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;interface는 공통 행위 지정 시 유용.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. interface 사용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 공통 함수만 필요한 경우, static이나 default 사용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- 만약 static으로 처리 가능하면 interface&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- 불가능하면 abstract 사용 고려.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;Exception handling&lt;/h3&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333;&quot; data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;error vs exception&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처리 방식&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예외처리 방법&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333;&quot; data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;error: 치명적 오류, 수습 불가.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;exception : 프로그램 내에서 수습 가능&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;try-catch-finally : return문 직전까지 수행하다 finally문을 수행.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;try-with-resource : 괄호 안에 resource 넣음. 그러면 try 끝난 후 알아서 정리.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예외처리 방법&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- 복구 : while + try-catch 사용해 복구&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- 회피 : 위로 올려줌.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;JVM&lt;/h3&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333;&quot; data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;java virtual machine&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구성요소&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;java 실행 과정&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333;&quot; data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;class loader : loading, linking, initializing&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴파일된 class 파일을 참조되는 순간에 올려줌.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;runtime data area : thread (pc register, stac, native method stack), heap (object + static + runtime constant pool), metaspace (method)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- pc register : 실행 중인 instruction 주소값 담음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- stack : paramter, local variable, 리턴값 (method frame)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- native method stack : native code.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- heap : object, static, runtime constant pool. static이나 constant pool 많아지면서 바뀐 걸로.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;execution engine : interpreter, JIT co mpiler, GC&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 작성한 .java 파일을 java compiler가 .class 파일(byte code)로 변경한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. JVM의 class loader가 .class 파일을 JVM에 로딩한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. Execution engine이 로딩된 .class 파일을 실행한다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;Java에서 main 실행 과정&lt;/h3&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333;&quot; data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;0. App.java 파일을 compiler가 .class 파일로 변환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. JRE가 static void main() method를 찾는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. JVM이 실행된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Class Loader가 App.class 파일을 JVM에 로딩한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 추가로 App.class에서 import하는 java.lang package를 method area에 올린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. main method가 stack에 올라간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 이 때, main의 리턴 타입과 parameter 등이 frame으로 묶여 stack에 올라간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Hello World!가 출력된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 이후에는 main method가 실행 종료되었기 때문에 stack에서 해제된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. main method가 끝났기 때문에 JRE는 JVM을 종료시키고 JRE도 종료된다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;GC&lt;/h3&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333;&quot; data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구조&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;삭제 방법&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;young : eden, survivor 0, survivor 1&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;old : old&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;삭제 방법 : reachability 사용. 참조되면 살리고 아니면 냅둠&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;minor gc : eden이 가득 차면 GC 수행. 살아남은 것은 survivor 0으로 이동, survivor 0에서 산 것은 survivor 1로 이동. survivor 1에서 산 것은 old로 이동.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;major jc : old가 가득 차면 GC 수행. mark-sweep-compact. 지울 것 마킹, 삭제, memory fragmentation 막기 위한 compact.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;Generic&lt;/h3&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333;&quot; data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;type을 parameter화하는 것.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;wildcard&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;memory pollution&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유의점 : static은 generic 불가. 정의되기 전부터 사용하기 때문.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;wildcard : 아무 타입 가능. ? extend T : T를 상속하는 것, ? super T : T 상위의 것.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;?로 쓰는 경우에는 type 사용 불가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴파일 한 후에는 T로 적은 것들도 다 Object로 변경. 그래서 잘못된 메모리 참조 가능할 수도 있음. 막기 위해서는 checkedList() 사용.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;Wrapper class&lt;/h3&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333;&quot; data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정의&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특징&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;constant pool&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;primitive type을 object로 감싼 것. boxing / unboxing 일어나며 성능 떨어질 수도 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특징&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- immutable&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- wrapper class 비교는 ==가 아니라 equals() 사용.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;constant pool&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;boolean은 t, f&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;숫자는 -128 ~ 127까지 constant pool에 들어가서 string과 같은 방식으로 동작. 때문에 127 비교는 true지만 128 비교는 false로 뜸.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;String vs StringBuffer vs StringBuilder&lt;/h3&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333;&quot; data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비교&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;string : immutable, literal 사용 시 constant pool 사용. new 사용 시 heap 사용. thread-safe.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;stringbuffer : mutable. 직접 값 변경, synchronization 보장.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;stringbuilder : synchronization 보장 X&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;Java Thread&lt;/h3&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333;&quot; data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JVM은 static main() method class를 찾아 main thread를 만들고, 내부 코드 따라 thread 필요 시 thread 생성함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;java app은 모든 thread가 종료되어야 종료함. thread 끝 대기 함수는 .join()&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Thread class를 override하거나 lambda function 사용하는 방식 : start() 호출. return value X, exception X&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Callable interface를 implement하는 방식 : start() 호출. return value O, exception O&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시작 method 호출 시 ready queue에 넣음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;java thread scheduling : first come, first serve / priority가 높은 것부터 처리 / round robin (quantum 값은 JVM이 조절)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fafafa; color: #333333; text-align: start;&quot;&gt;너무 많은 thread로 성능 저하가 발생할 수 있기 때문에 thread pool에서는 thread 개수를 정해두고 ready queue에 있는 thread를 처리. 동시 처리 속도를 보장한다.&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;Collection&lt;/h3&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333;&quot; data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;List&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- arraylist : index. 조회 빠름, thread unsafe&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- linkedlist : doubly linked list&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- vector : thraed safe&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- stack : vector의 구현체&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Queue&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- priorityqueue : heap, thread unsafe&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- arraydeque : thread unsafe&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Set : 중복 허용 X&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- hashset : hash 사용, thread unsafe, hashCode() method 사용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- linkedhashset : 순서 O, thread unsafe&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- treeset : thread unsafe, rb tree&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Map : key 중복 X&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- hashmap : hashCode(), equals() 사용해 equality 판단, thread unsafe&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- linkedhashmap : 순서 O, thread unsafe&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- hashtable : thread safe&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;thread safe를 위해서는 synchroizeXXX() 쓰면 됨.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>내가 하고싶은 것!/취준</category>
      <author>hyelie</author>
      <guid isPermaLink="true">https://hyelie.tistory.com/695</guid>
      <comments>https://hyelie.tistory.com/entry/Java-%EB%A9%B4%EC%A0%91%EB%8C%80%EB%B9%84-%EC%A7%88%EB%AC%B8#entry695comment</comments>
      <pubDate>Mon, 15 Apr 2024 23:23:12 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] DTO와 Entity 간의 변환</title>
      <link>https://hyelie.tistory.com/entry/Spring-DTO%EC%99%80-Entity-%EA%B0%84%EC%9D%98-%EB%B3%80%ED%99%98</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Spring을 쓴다면 MVC 구조를 사용한다는 것을 전제로 깔고 갈 것이다. 따라서 Controller, Service, Repositoy, DB 순으로 flow가 이동하며, 이 과정에서 entity라는 객체와 DTO라는 객체를 사용한다.&amp;nbsp;정의를 먼저 살펴보자면, &lt;b&gt;entity는 DB의 row 하나와 매핑되는 객체&lt;/b&gt;인 반면, &lt;b&gt;DTO는&lt;/b&gt; Data Transfer Object, &lt;b&gt;데이터를 옮기는 데 사용하는 객체&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;DTO의 필요성&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;DTO의 필요성에 대해서는 말할 필요도 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;만약 DTO가 없다고 가정해 보자. 그러면 entity를 사용자에게 노출시켜야 하는데, entity는 DB의 모든 column에 대한 정보를 가지고 있기 때문에 이를 사용자에게 노출시키는 것은 좋지 않다. 또한 entity에 내용이 부족해 추가적인 요청을 해야 할 수도 있고(&lt;b&gt;underfetching&lt;/b&gt;), 필요없는 내용이 있어 네트워크 낭비가 일어날&amp;nbsp; 수도 있다.(&lt;b&gt;overfetching&lt;/b&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;사용자 요청이 복잡한 경우 (2개 이상 table을 조인해 리턴하는 경우), 해당 entity를 또 만들어야 한다. 사용자 요청이 entity에 영향을 끼치게 되는데, entity는 그 자체로서 이미 핵심 로직을 가지고 있다. 때문에 &lt;b&gt;entity로만 통신할 수는 없다&lt;/b&gt;.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Overfetching은 응답받은 정보에 필요없는 값이 있어 네트워크 낭비가 일어나는 것을,&lt;br /&gt;Underfecthing은 응답받은 정보가 부족해서 추가적인 요청을 해야 하는 상황을 의미한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;DTO를 사용함으로써 encapsulation을 할 수 있고, overfetching/underfetching을 막을 수 있다. validation을 사용해 입력에 대한 검증 로직을 controller와 분리할 수도 있다. 이 때, repository는 entity를 persistence context에 넣고, persistence context에서 entity를 가져오는 조작을 한다. 앞서 살펴봤듯 controller는 사용자에게 DTO를 돌려줘야 한다. &lt;b&gt;어딘가에서는 entity를 DTO로 변환해 줘야 한다&lt;/b&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;어디서 DTO와 entity를 변환해야 하는가?&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Repository에서 DTO와 entity를 변환해야 할까?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;앞서 살펴봤듯 repository는 persistence context에 관한 조작을 하기 때문에 여기에 변환까지 추가되면 repository의 일이 너무 많아지게 된다. 때문에 일반적인 경우에서는 &lt;b&gt;repository는 entity를 받아 조작&lt;/b&gt;하는 것이 좋다고 본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;예외&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;단, 몇 가지 예외가 있을 수 있다고 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;예를 들어 JPQL로 해결할 수 없는 복잡한 쿼리(inline view 같은)를 날리는 경우를 생각해 보자. 예를 들어 A join B join C에 paging도 걸고 filtering도 걸고, ... 이런 상황에 모든 정보를 가져오고 싶다고 생각해 보자. DTO를 사용하지 않았을 때 이 결과가 entity와 매핑되지 않는 경우 Object[]나 Map을 사용해야 한다. 이 경우 repository에서 Object[]로 리턴하게 되고, service도 Object[]를 받아 정보를 파싱해야 하며, 쿼리를 보아야 어떤 위치에 어느 데이터가 있는지 알 수 있다. 이런 경우는 &lt;b&gt;유지보수가 너무 어려워지고&lt;/b&gt;, &lt;b&gt;변환 코드도 중복&lt;/b&gt;되기 때문에 DTO를 사용해 리턴하는 방식이 좋은 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;또한, QueryDSL의 @QueryProtection을 사용하는 경우 DTO가 repository에서 생성되어 나가기도 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;일단 repository는 아닌 것 같다.&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그러면 남은 것은 service와 controller이다. 일단 service는 비즈니스 로직을 다루는 layer이고, controller는 클라이언트 요청을 받고 service에게 받은 처리 결과를 클라이언트에게 응답하는 역할을 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;일단 Spring을 사용하는 이유는 &quot;&lt;b&gt;유연한 확장과 유지보수의 용이성&lt;/b&gt;&quot;을 목적으로 가져가는 경우가 대부분일 것이다. 이를 위해서는&lt;b&gt; dependency를 줄이는 것&lt;/b&gt;이 제일 중요하다. dependency가 어떻게 되는지 살펴보자.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;dependency가 있는 경우, 하나를 수정하면 연관된 모든 것을 수정해야 하기 때문이다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Controller에서 변환&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;controller에서 DTO를 entity로 바꾼다고 해 보자.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;controller가 입력으로 DTO를 받으면 controller 내부에서 entity로 바꾸고 service를 호출한다.&lt;/li&gt;
&lt;li&gt;이후 service가 리턴한 entity를 DTO로 바꾸어 리턴한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Group 3391.png&quot; data-origin-width=&quot;1212&quot; data-origin-height=&quot;453&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WpHgd/btsFJsiqRiF/gGfwsNtNGlfoPnWI0jgfs1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WpHgd/btsFJsiqRiF/gGfwsNtNGlfoPnWI0jgfs1/img.png&quot; data-alt=&quot;controller에서 Entity - DTO 변환 시 dependency tree&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WpHgd/btsFJsiqRiF/gGfwsNtNGlfoPnWI0jgfs1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWpHgd%2FbtsFJsiqRiF%2FgGfwsNtNGlfoPnWI0jgfs1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;603&quot; height=&quot;225&quot; data-filename=&quot;Group 3391.png&quot; data-origin-width=&quot;1212&quot; data-origin-height=&quot;453&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;controller에서 Entity - DTO 변환 시 dependency tree&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그러면 &lt;b&gt;controller는 DTO, entity, service에 의존&lt;/b&gt;한다. &lt;b&gt;service는 entity와 repository에 의존&lt;/b&gt;하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;비즈니스 로직을 다루는 service가 특정 DTO에 의존하지 않고, entity에만 의존하기 때문에 &lt;b&gt;service에 대한 재사용성이 높다&lt;/b&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 경우, controller가 하는 역할에 비즈니스 로직이 섞일 수도 있다! 예를 들어 복잡한 통계를 응답해야 하는 상황을 가정해 보자. 여러 service로부터 entity list를 받아오고 이를 합쳐 DTO를 만들어야 한다고 치면, 이것 자체도 비즈니스 로직이 포함되어 있는 것이다. 그러면 사용자 요청을 담당하는 &lt;b&gt;controller에 추가적인 일&lt;/b&gt;이 생긴다. 또한 하나의 controller가 여러 개의 service에 의존하게 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Service에서 변환&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;service에서 DTO를 entity로 바꾼다고 해 보자.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;controller가 입력으로 받은 DTO를 그대로 service에 넘긴다.&lt;/li&gt;
&lt;li&gt;service 내부에서 DTO를 entity로 변환한 후 비즈니스 로직을 실행하고, 필요 시 repository를 호출한다.&lt;/li&gt;
&lt;li&gt;이후 service는 repository가 리턴한 entity를 DTO로 변환하고, controller에게 돌려준다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Group 3392.png&quot; data-origin-width=&quot;1212&quot; data-origin-height=&quot;453&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/btWCyn/btsFJsWZEQw/ZvjZOTG1D9H2Pr744VSBr1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btWCyn/btsFJsWZEQw/ZvjZOTG1D9H2Pr744VSBr1/img.png&quot; data-alt=&quot;service에서 Entity - DTO 변환 시 dependency tree&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btWCyn/btsFJsWZEQw/ZvjZOTG1D9H2Pr744VSBr1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbtWCyn%2FbtsFJsWZEQw%2FZvjZOTG1D9H2Pr744VSBr1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;651&quot; height=&quot;243&quot; data-filename=&quot;Group 3392.png&quot; data-origin-width=&quot;1212&quot; data-origin-height=&quot;453&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;service에서 Entity - DTO 변환 시 dependency tree&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그러면 &lt;b&gt;controller는 DTO, service에 의존&lt;/b&gt;한다. &lt;b&gt;service는 DTO, entity, service에 의존&lt;/b&gt;한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;controller는 받은 DTO를 사용자에게 바로 넘겨주기만 하면 된다. 여러 entity를 합쳐야 하는 복잡한 비즈니스 로직도 service에서 모든 것을 처리한 후 controller로 넘겨주면 된다. 요구사항이 바뀌는 경우를 생각해 보자. controller는 받은 DTO를 그대로 넘겨주기만 하면 되므로 변하지 않을 가능성이 매우 높다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;반면 모든 요청에서 다른 DTO를 사용해야 하기 때문에 &lt;b&gt;API 개수만큼&lt;/b&gt; &lt;b&gt;DTO 개수가 늘어난다&lt;/b&gt;는 단점이 있다. (같은 DTO를 사용하는 경우 overfetching이 일어날 수도 있기 때문에 나누는 것이 좋다.) 또한 service가 DTO에 의존하고 있기 때문에 해당 DTO가 아닌 경우 그 service를 쓸 수 없으므로&amp;nbsp;&lt;b&gt;service에 대한 재사용성이 매우 떨어진다&lt;/b&gt;는 단점도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;결론&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;애플리케이션에 복잡한 로직이 아예 없는 경우는 없다고 봐도 무방하므로, controller에서 entity와 DTO를 변환하는 방식은 비즈니스 로직이 controller로 넘어오게 되므로 별로인 것 같다. 그렇다고 두 번째 방법을 채택하자니 service가 DTO에 의존하기 때문에 service에 대한 재사용성이 떨어진다는 딜레마가 발생한다. 모든 경우에서 딱 좋은 압도적인 하나의 결론이 없다. 때문에 상황에 맞춰 써야 한다. 그러면 어떤 상황에서 어떤 방식을 써야 할까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;만약 복잡한 쿼리가 적거나 없는 소규모 프로젝트라면 controller에서 entity를 변환하더라도 비즈니스 로직이 controller로 오지 않는다. 그러면 controller에서 변환하는 것이 더 좋을 것이다!&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;반면 복잡한 쿼리가 많고, service를 재사용하지 않을 경우 service에서 변환하는 것이 더 좋을 것이다!&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;일반적으로는 한 종류의 controller가 하나의 service를 사용하는 경우가 대부분이기 때문에 service에 대한 재사용성을 크게 기대하지 않아도 될 것이다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;다른 방법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;진짜 silver bullet은 없나? service의 재사용성/완벽히 분리된 비즈니스 로직 두 가지를 모두 달성할 수 있는 방법은 없을까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;controller가 service에 값을 보낼 때는 DTO를 보내고, 받을 때는 entity를 쓰는 방식은? 음... 이 경우는 두 방식의 단점만 모두 가져온 것 같다. service의 재사용성은 떨어지고, entity를 합쳐야 할 때는 비즈니스 로직이 controller로 오게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그렇다면 위 방식을 flip해서 controller가 service에 값을 보낼 때는 entity로 변환해서 보내고, 받을 때는 DTO를 받고 리턴하는 형식은 어떨까? 그러면 service의 재사용성도 확보할 수 있고, 비즈니스 로직을 service에 모두 넣을 수 있게 된다! 단... controller가 입력으로 받는 형식과 출력으로 주는 형식이 다를 수 있기 때문에, 하나의 로직에서 2개의 DTO가 필요하게 된다. DTO 관리가 매우 힘들어질 것이다. controller, service가 DTO와 entity에 모두 의존하게 되므로 DTO가 entity와 비슷한 역할을 하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;DTO-entity mapper&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Group 3393.png&quot; data-origin-width=&quot;1212&quot; data-origin-height=&quot;453&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b9XRd1/btsFK75rLU2/DB9qvN3xysjm3gE5tGjhhK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b9XRd1/btsFK75rLU2/DB9qvN3xysjm3gE5tGjhhK/img.png&quot; data-alt=&quot;mapper에서 Entity - DTO 변환 시 dependency tree&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b9XRd1/btsFK75rLU2/DB9qvN3xysjm3gE5tGjhhK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb9XRd1%2FbtsFK75rLU2%2FDB9qvN3xysjm3gE5tGjhhK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;672&quot; height=&quot;251&quot; data-filename=&quot;Group 3393.png&quot; data-origin-width=&quot;1212&quot; data-origin-height=&quot;453&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;mapper에서 Entity - DTO 변환 시 dependency tree&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;controller와 service 사이에 매핑만 전문으로 다루는 class를 추가하는 방식은 어떨까? 그러면 service는 mapper와 entity에만 의존하고, controller는 mapper와 DTO에만 의존하게 된다. 일단 dependency는 꽤 좋다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;service는 entity에만 의존하므로 service에 대한 재사용성이 좋다! controller는 입력으로 받은 DTO를 mapper에 의존해 entity로 변환하고, service를 호출한다. 복잡한 비즈니스 로직도 service에서 모든 것을 처리한 후 mapper에 의존해 DTO로 바꾼다. service에서 변환하는 방식, controller에서 변환하는 방식 2개의 장점만 모았고, dependency 문제도 해결한 것 같다! entity나 DTO가 바뀌면 mapper만 바꿔주면 되므로 유지보수도 좋을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;단... mapper class가 매우 비대해질 수 있다는 단점이 있다. 변환이라는 게 말이 간단하지 요청이 조금만 복잡해져도 로직이 생기고, 모든 변환 로직이 mapper에 집중될 경우 크기에서 문제가 생길 것이다.&lt;span style=&quot;text-align: start;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;정리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;일반적인 경우 controller와 service에서만 DTO를 사용하되, repository에서 DTO를 반환할 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;변환 위치는, case by case이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;controller에서 변환: 만약 &lt;b&gt;복잡한 쿼리가 적거나 없는 프로젝트&lt;/b&gt;라면 controller에서 entity를 변환하더라도 비즈니스 로직이 controller로 오지 않는다. 그러면 &lt;b&gt;controller에서 변환&lt;/b&gt;하는 것이 더 좋을 것이다!&lt;/li&gt;
&lt;li&gt;service에서 변환: 반면 &lt;b&gt;복잡한 쿼리가 많고&lt;/b&gt;, service의 재사용을 하지 않을 것이라 예상되는 경우 &lt;b&gt;service에서 변환&lt;/b&gt;하는 것이 더 좋을 것이다!&lt;/li&gt;
&lt;li&gt;mapper 사용: 요구사항이 매우 자주 바뀌는 경우 controller와 service 사이에 &lt;b&gt;mapper&lt;/b&gt;를 두고 mapper에서 entity와 DTO를 변환하면 유지보수가 간편해진다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참고: 순환 참조&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;DTO의 위치를 설정할 때 package &lt;b&gt;dependency가 cycle을 이루지 않게&lt;/b&gt; 조심히 설정해야 한다. 아래 2가지 경우를 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Group 3392.png&quot; data-origin-width=&quot;1212&quot; data-origin-height=&quot;453&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/btWCyn/btsFJsWZEQw/ZvjZOTG1D9H2Pr744VSBr1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btWCyn/btsFJsWZEQw/ZvjZOTG1D9H2Pr744VSBr1/img.png&quot; data-alt=&quot;service에서 Entity - DTO 변환 시 dependency tree&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btWCyn/btsFJsWZEQw/ZvjZOTG1D9H2Pr744VSBr1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbtWCyn%2FbtsFJsWZEQw%2FZvjZOTG1D9H2Pr744VSBr1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;651&quot; height=&quot;243&quot; data-filename=&quot;Group 3392.png&quot; data-origin-width=&quot;1212&quot; data-origin-height=&quot;453&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;service에서 Entity - DTO 변환 시 dependency tree&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;service에서 DTO-entity 변환 시, DTO 위치를 controller package에 두었다고 하자. controller package는 service package에 의존하는데, service가 DTO에 의존하기 떄문에 controller package에 의존하게 된다! dependency가 cycle을 이루게 된다. 이렇게 두면 안 된다. DTO 위치를 잘 생각해 두어야 할 것이다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Group 3391.png&quot; data-origin-width=&quot;1212&quot; data-origin-height=&quot;453&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WpHgd/btsFJsiqRiF/gGfwsNtNGlfoPnWI0jgfs1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WpHgd/btsFJsiqRiF/gGfwsNtNGlfoPnWI0jgfs1/img.png&quot; data-alt=&quot;controller에서 Entity - DTO 변환 시 dependency tree&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WpHgd/btsFJsiqRiF/gGfwsNtNGlfoPnWI0jgfs1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWpHgd%2FbtsFJsiqRiF%2FgGfwsNtNGlfoPnWI0jgfs1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;603&quot; height=&quot;225&quot; data-filename=&quot;Group 3391.png&quot; data-origin-width=&quot;1212&quot; data-origin-height=&quot;453&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;controller에서 Entity - DTO 변환 시 dependency tree&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;controller에서 DTO-entity 변환 시, DTO 위치를 controller package 내에 두던, service 내에 두던 큰 문제가 생기지 않는다. 다만 service에서는 DTO를 사용하지 않으므로 controller package에 두는 것이 자연스럽다!&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Development/Spring</category>
      <author>hyelie</author>
      <guid isPermaLink="true">https://hyelie.tistory.com/693</guid>
      <comments>https://hyelie.tistory.com/entry/Spring-DTO%EC%99%80-Entity-%EA%B0%84%EC%9D%98-%EB%B3%80%ED%99%98#entry693comment</comments>
      <pubDate>Tue, 12 Mar 2024 23:22:04 +0900</pubDate>
    </item>
    <item>
      <title>[이종병렬컴퓨팅] Accelerators for Deep Learning</title>
      <link>https://hyelie.tistory.com/entry/%EC%9D%B4%EC%A2%85%EB%B3%91%EB%A0%AC%EC%BB%B4%ED%93%A8%ED%8C%85-Accelerators-for-Deep-Learning</link>
      <description>&lt;blockquote style=&quot;text-align: left;&quot; data-ke-style=&quot;style2&quot;&gt;이 글은 포스텍 성효진 교수님의 이종병렬컴퓨팅(CSED490C) 강의를 기반으로 재구성한 것입니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 글에서는 deep learning을 위한 가속의 motivation, domain-specific accelerator의 기본, efficiency metric 그리고 4가지 case study(Google TPU, GraphCore, DianNao series, Reconfigurable accerlerator)를 살펴본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;heterogeneity의 Motivation&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;single core CPU의 경우 moore의 법칙/dennard의 scaling이 거의 끝이 났을 정도로 발전이 끝에 다다랐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;844&quot; data-origin-height=&quot;393&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blsSCq/btsCj3r7Bje/FSKPajey74ukZIBMLJDNW0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blsSCq/btsCj3r7Bje/FSKPajey74ukZIBMLJDNW0/img.png&quot; data-alt=&quot;heterogeneity의 motivation&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blsSCq/btsCj3r7Bje/FSKPajey74ukZIBMLJDNW0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FblsSCq%2FbtsCj3r7Bje%2FFSKPajey74ukZIBMLJDNW0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;492&quot; height=&quot;229&quot; data-origin-width=&quot;844&quot; data-origin-height=&quot;393&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;heterogeneity의 motivation&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;때문에 performance와 watt를 향상시키기 위해 single core CPU를 넘어, multi core CPU, GPU, FPGA 등으로 나아가고 있다. 그러나, CPU의 경우에는 target domain이 general purpose, 범용성을 가지는 것이 목표인데, GPU는 범용으로 사용할 수 없다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Domain Specific Accelerator&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;domain specific accelerator는 그래픽, 딥러닝, 시뮬레이션 등 특정 분야에 특화된 하드웨어를 말한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;특정 분야에 특화되었기 때문에 전문화된 연산, parallelism, 효율적인 메모리 시스템, 오버헤드 감소 등으로 성능이나 소모 전력량이 훨씬 향상되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;domain specific accelerator 설계와 parallel program을 작성하는 것의 주된 차이는 &lt;b&gt;cost model&lt;/b&gt;이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;일반적으로 arithmetic이나 logical 연산은 무시할 수 있을 정도로 빠르기 때문에 memory가 dominate한다. 따라서 효율성을 최대화하기 위해서는 프로그램을 재구성해야 하며, granularity나 memory footprint에서 차이가 만들어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Acceleration의 source&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;data specialization&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;특정 domain에 특화된 hardware operator set을 제공한다.&lt;/li&gt;
&lt;li&gt;이를 통해 overhead를 줄이고 에너지를 아낄 수 있다.&lt;/li&gt;
&lt;li&gt;overhead 감소로 인해 area와 power는 memory에 의해 dominate되는데, 따라서 global memory access를 줄이는 것이 domain specific accelerator의 핵심이다. 따라서 domain specific accelerator는 algorithm과 동시에 설계되어야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;parallelism&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;parallelism을 domain에 특화시키는 방식이다. 이를 통해 PE의 synchronization과 communication이 단순화되고, 이를 통해 overhead를 줄이고 utilization을 높일 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Local and Optimized memory&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;계산은 작은 local memory에서 수행해야 한다.&lt;/li&gt;
&lt;li&gt;높은 bandwidth를 얻기 위해 global memory로의 access pattern을 최적화한다.&lt;/li&gt;
&lt;li&gt;특정 데이터 구조를 앞축해 local memory의 유효 크기와 bandwidth를 올리는 방식도 있다.&lt;/li&gt;
&lt;li&gt;memory access를 load balance해서 memory utilization을 최대화하는 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;reduce overhead&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;instruction overhead가 높기 때문에 복잡하고 전문화된 특수 instruction을 만든다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Balancing Specialization and Generality&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;일반성과 효율성은 tradeoff를 가진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;special instruction vs special engine&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;special instruction은 general purpose processor에 추가되었다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;dedicated accelerator는&amp;nbsp; on chip memory에서 data stating이나 data 이동을 최적화하는 데 효과적이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Accelerator 프로그래밍&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;domain specific accelerator는 firmware와 software 개발 인터페이스가 필요하다. API based 또는 compiler based이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;더 유연한 domain specific language를 지원하는 accelerator는 domain specific compiler에 backend를 추가한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Efficiency Metrics&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;효울적인 deep neural network를 처리하기 위해서는 다음과 같은 것들을 고려해야 한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;어떤 것을 측정하고 비교해야 하는지 (&lt;b&gt;metric&lt;/b&gt;)&lt;/li&gt;
&lt;li&gt;어떤 것이 주요 과제인지 (&lt;b&gt;challenge&lt;/b&gt;)&lt;/li&gt;
&lt;li&gt;설계 고려사항과 tradeoff (&lt;b&gt;consideration&lt;/b&gt;, &lt;b&gt;tradeoff&lt;/b&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Key Metrics&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;accuraty&lt;/b&gt; : 결과의 품질&lt;/li&gt;
&lt;li&gt;&lt;b&gt;throughput&lt;/b&gt; : 큰 데이터를 가진 것들에 대해&lt;/li&gt;
&lt;li&gt;&lt;b&gt;latency&lt;/b&gt; : interactive한 것들에 대해&lt;/li&gt;
&lt;li&gt;&lt;b&gt;energy&lt;/b&gt; and &lt;b&gt;power&lt;/b&gt; : embedded device는 한정된 배터리를 가지고 있기 때문. 또한 data center는 cooling cost가 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;hardware&lt;/b&gt; &lt;b&gt;cost&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;flexibility&lt;/b&gt; : deep neural network model의 작업 범위&lt;/li&gt;
&lt;li&gt;&lt;b&gt;scalability&lt;/b&gt; : resource 양에 따른 성능의 확장&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Key Operation&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;weighted sum을 계산하기 위한 &lt;b&gt;multiply and accumulate (MAC)&lt;/b&gt;가 중요하다. 계산의 90% 이상을 차지하기 때문.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;deep neural network SW/HW의 핵심 설계 목표&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;throughput을 증가시키고 latency를 최소화&lt;/b&gt;하는 것이다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;MAC 연산의 reduce를 줄이는 것. critical path를 줄임으로써 overhead를 줄일 수 있다.&lt;/li&gt;
&lt;li&gt;필요 없는 MAC 연산을 줄여 cycle을 아끼는 것&lt;/li&gt;
&lt;li&gt;processing element (PE)의 개수를 늘리는 것. 이를 통해 더 많은 MAC 연산을 병렬로 수행할 수 있다.&lt;/li&gt;
&lt;li&gt;PE utilization을 증가시키는 것. 가능한 한 많은 PE에 작업을 분산하고, 부하를 균형있게 유지해 utilization을 높게 유지하는 것이다. 추가로 PE에 작업을 전달하기 위한 memory bandwidth&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;낮은 latency&lt;/b&gt;는 작은 batch size를 가져야 한다는 제약이 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;전력 소비량 감소&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;에너지 소비를 dominate하는 data 이동을 줄이기&lt;/li&gt;
&lt;li&gt;MAC 연산당 소모 전력 줄이기&lt;/li&gt;
&lt;li&gt;필요 없는 MAC 연산 없애기&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;전력 소비는 열 방출에 의해 제한되어 있으며, 이는 parallel하게 수행할 수 있는 최대 MAC 연산의 개수를 한정한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Metric 측정을 위한 명세&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;accuracy&lt;/b&gt; : dataset 작업의 어려움을 고려해야 함. 어려운 작업은 더 복잡한 deep neural network model을 필요로 한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;throughput&lt;/b&gt; : utilization과 processing element의 개수, 특정 deep neural network model의 수행시간&lt;/li&gt;
&lt;li&gt;&lt;b&gt;latency&lt;/b&gt; : 평가를 위한 batch size&lt;/li&gt;
&lt;li&gt;&lt;b&gt;energy&lt;/b&gt;와 &lt;b&gt;power&lt;/b&gt; : 특정 deep neural network model을 수행할 때 전력 소모량, off chipe memory access&lt;/li&gt;
&lt;li&gt;&lt;b&gt;hardware&lt;/b&gt; &lt;b&gt;cost&lt;/b&gt; : on chip storage, processing element의 개수, chip area와 process 기술&lt;/li&gt;
&lt;li&gt;&lt;b&gt;flexibility&lt;/b&gt; : 다양한 deep neural network에 대한 성능 비교&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 때 모든 metric은 설계 tradeoff를 공정하게 비교해야 한다. 특정 metric이 생략되었을 때는 문제가 발생하기 때문&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;accuracy는 주어진 작업을 정확하게 수행하는지,&lt;/li&gt;
&lt;li&gt;latency와 throughput은 작업이 빠르고 실시간으로 수행되는지,&lt;/li&gt;
&lt;li&gt;energy와 power consumption은 기기 형태에 따라 결정되며,&lt;/li&gt;
&lt;li&gt;cost는 chip area에 의해 결정되며 각 solution에 얼마인지 결정되며&lt;/li&gt;
&lt;li&gt;flexibility는 작업 범위를 말한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;비교&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;GPU의 경우&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;721&quot; data-origin-height=&quot;385&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tX1yS/btsCk4qzp9A/hzUeC9hKMQcHGLH4v05jnK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tX1yS/btsCk4qzp9A/hzUeC9hKMQcHGLH4v05jnK/img.png&quot; data-alt=&quot;GPU의 경우&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tX1yS/btsCk4qzp9A/hzUeC9hKMQcHGLH4v05jnK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtX1yS%2FbtsCk4qzp9A%2FhzUeC9hKMQcHGLH4v05jnK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;390&quot; height=&quot;208&quot; data-origin-width=&quot;721&quot; data-origin-height=&quot;385&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;GPU의 경우&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ASIC accelerator의 경우&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;673&quot; data-origin-height=&quot;386&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJ9HGF/btsCiNpCiZ1/N4zRw42KweHMfYu5X4bK40/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJ9HGF/btsCiNpCiZ1/N4zRw42KweHMfYu5X4bK40/img.png&quot; data-alt=&quot;ASIC accelerator의 경우&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJ9HGF/btsCiNpCiZ1/N4zRw42KweHMfYu5X4bK40/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJ9HGF%2FbtsCiNpCiZ1%2FN4zRw42KweHMfYu5X4bK40%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;396&quot; height=&quot;227&quot; data-origin-width=&quot;673&quot; data-origin-height=&quot;386&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;ASIC accelerator의 경우&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;FPGA/CGRA accelerator의 경우&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;747&quot; data-origin-height=&quot;390&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sDj13/btsCfzsmm1X/B1v1WDQRKSe3BGkMRR7080/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sDj13/btsCfzsmm1X/B1v1WDQRKSe3BGkMRR7080/img.png&quot; data-alt=&quot;FPGA/CGRA accelerator의 경우&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sDj13/btsCfzsmm1X/B1v1WDQRKSe3BGkMRR7080/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsDj13%2FbtsCfzsmm1X%2FB1v1WDQRKSe3BGkMRR7080%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;414&quot; height=&quot;216&quot; data-origin-width=&quot;747&quot; data-origin-height=&quot;390&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;FPGA/CGRA accelerator의 경우&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;GraphCore의 경우&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;449&quot; data-origin-height=&quot;373&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TpAFe/btsCgqIqzsG/z3HHVRsYx5poqcLZV2HeP1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TpAFe/btsCgqIqzsG/z3HHVRsYx5poqcLZV2HeP1/img.png&quot; data-alt=&quot;GraphCore의 경우&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TpAFe/btsCgqIqzsG/z3HHVRsYx5poqcLZV2HeP1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTpAFe%2FbtsCgqIqzsG%2Fz3HHVRsYx5poqcLZV2HeP1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;303&quot; height=&quot;252&quot; data-origin-width=&quot;449&quot; data-origin-height=&quot;373&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;GraphCore의 경우&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In memory computing의 경우&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;421&quot; data-origin-height=&quot;393&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3OCHe/btsCgYdOLaw/AwglSqwSWPrVM6UO2vgqZ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3OCHe/btsCgYdOLaw/AwglSqwSWPrVM6UO2vgqZ1/img.png&quot; data-alt=&quot;In memory computing의 경우&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3OCHe/btsCgYdOLaw/AwglSqwSWPrVM6UO2vgqZ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3OCHe%2FbtsCgYdOLaw%2FAwglSqwSWPrVM6UO2vgqZ1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;275&quot; height=&quot;257&quot; data-origin-width=&quot;421&quot; data-origin-height=&quot;393&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;In memory computing의 경우&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Case study : Google TPU&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Systolic array&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;목표는 다음 요건들을 달성하는 accelerator를 만드는 것이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;간단하고 규칙적인 설계&lt;/li&gt;
&lt;li&gt;높은 concurrency&lt;/li&gt;
&lt;li&gt;균형잡힌 computation과 I/O&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;핵심 아이디어는 하나의 processing element를 processing element의 regular array로 대체하고, processing element 간의 data flow를 잘 조절하는 것이다. 이를 통해 memory에서 가져온 input data를 출력하기 전에 변환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이를 통해 memory에서 가져온 single data element에 대해 computation을 maximize한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;장단점&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;장점 : memory bandwidth를 더 쓸 수 있고, concurrency를 높인다. 또한 regular한 설계이다.&lt;/li&gt;
&lt;li&gt;단점으로는 irregular parallelism을 활용하는 데는 별로이고, specialize되었기 때문에 general하게 적용할 수 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;지난 10년간 lesson&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DNN은 memory와 compute에서 급격하게 성장했다.&lt;/li&gt;
&lt;li&gt;DNN 워크로드는 DNN의 혁신과 함께 성장했다.&lt;/li&gt;
&lt;li&gt;DNN을 compiler나 hardware만큼 최적화할 수 있다.&lt;/li&gt;
&lt;li&gt;inference slo는 batch size가 아니라 p99 latency에 의해 한정된다.&lt;/li&gt;
&lt;li&gt;production infernce는 multi tenacy이다.&lt;/li&gt;
&lt;li&gt;FLOPs가 아니라 memory의 문제다.&lt;/li&gt;
&lt;li&gt;DSA challenge : domain에 최적화하면서 flexible을 유지하기&lt;/li&gt;
&lt;li&gt;logic, wire, SRAM &amp;amp; DRAM은 불균등하게 개선된다.&lt;/li&gt;
&lt;li&gt;compiler를 최적화하고 ML 호환성을 유지해야 한다.&lt;/li&gt;
&lt;li&gt;TCO 대비 성능 대 CapEX 성능을 설계해야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Lesson 6: It's the memory&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;transistor의 수가 아니라 external memory access energy가 현대 칩의 한계를 결정한다. external memory access energy는 on chip memory access보다 100배 더 크고, arithmetic operation의 1만배 정도 더 크다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;memory access의 균형을 맞추기 위해 ALU를 추가해 FLOPs/s를 늘일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Lesson 7: DSA optimizes for domain while being flexible&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;TPU v2는 학습에 어려움이 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;더 많은 backpropagation, transpose, derivative&lt;/li&gt;
&lt;li&gt;더 많은 memory : backpropagation을 위해 data를 유지한다.&lt;/li&gt;
&lt;li&gt;더 넓은 operand : int8보다 더 큰 동적 범위가 필요하다.&lt;/li&gt;
&lt;li&gt;어려운 parallelization : scale out 대신 scale up&lt;/li&gt;
&lt;li&gt;programmability&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Lesson 8 : Unequal changes in semiconductor technology&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;logic은 무료이기 때문에 wire나 SRAM보다 더 빨리 발전한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;VLIW XLA compiler&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;TPU v2와 이후 버전은 XLA 컴파일러에 의존한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;TPU v2와 v3는 322 bit의 VLIW instruction을 생성한다. 이를 통해 8개의 작업을 수행할 수 있다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;2개의 scalar, 2개의 vector ALU, vector load와 store, 행렬 곱셈 및 전치&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;TPU v4 VLIW는 25% 더 넓다.&lt;/li&gt;
&lt;li&gt;compiler와도 호환된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;표준 VLIW compilation 기술 사용 : loop unrolling, instruction scheduling, software pipelining&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Reconfigurable Accelerators&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Field Programmable Gate Array, FPGA&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;적은 개발 비용으로 custom hardware 기능을 구현할 수 있는 유연한 platform을 제공하는 판매용 프로그래밍 장치. logic block, programmable interconnection network, programmable input/output cell을 지원한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Coarse Grained Reconfigurable Architecture, CGRA&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;domain specific flexibility
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;general purposed와 fixed function 사이에서 flexibility를 유지한다.&lt;/li&gt;
&lt;li&gt;coarse grained level에서 재구성할 수 있다.&lt;/li&gt;
&lt;li&gt;hardware를 목적 domain에 맞게 재구성한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;spatial과 temporal computation의 결합
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;spatial : 여러 processing element에서 병렬로 계산하고 데이터를 전공한다.&lt;/li&gt;
&lt;li&gt;temporal : 계산을 shared &lt;span style=&quot;text-align: left;&quot;&gt;processing element에서 실행할 section으로 분할한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;data driven execution&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;잘못된 내용이나 오탈자에 대한 지적, 질문&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;b&gt;등은 언제나 환영합니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>CS/Parallel Computing</category>
      <author>hyelie</author>
      <guid isPermaLink="true">https://hyelie.tistory.com/691</guid>
      <comments>https://hyelie.tistory.com/entry/%EC%9D%B4%EC%A2%85%EB%B3%91%EB%A0%AC%EC%BB%B4%ED%93%A8%ED%8C%85-Accelerators-for-Deep-Learning#entry691comment</comments>
      <pubDate>Wed, 20 Dec 2023 00:19:27 +0900</pubDate>
    </item>
    <item>
      <title>[이종병렬컴퓨팅] Heterogeneous Parallel Computer를 위한 기술 스택</title>
      <link>https://hyelie.tistory.com/entry/%EC%9D%B4%EC%A2%85%EB%B3%91%EB%A0%AC%EC%BB%B4%ED%93%A8%ED%8C%85-Heterogeneous-Parallel-Computer%EB%A5%BC-%EC%9C%84%ED%95%9C-%EA%B8%B0%EC%88%A0-%EC%8A%A4%ED%83%9D</link>
      <description>&lt;blockquote style=&quot;text-align: left;&quot; data-ke-style=&quot;style2&quot;&gt;이 글은 포스텍 성효진 교수님의 이종병렬컴퓨팅(CSED490C) 강의를 기반으로 재구성한 것입니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 글에서는 이종 시스템을 위한 고수준 프로그래밍을 가능하게 하는 software stack에 대해 설명한다. compiler-based, library-based, framework-based로 나눠 설명하고, OpenCL에 대한 compile/runtime 지원을 살펴본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Compiler란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;b&gt;compiler는 source code를 기계어로 번역하는 일&lt;/b&gt;을 한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;source code&lt;/b&gt;는 high level abstraction, low level detail을 숨긴다. 떄문에 알고리즘에 대한 이해가 쉽고, fine-grained performance tuning이 제한된다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;기계어&lt;/b&gt;는 코드를 짜고 유지보수하기 어렵지만, 성능을 직접적으로 제어할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;compiler는 lexical analysis, syntatic analysis, semantic analysis, IR generation, IR optimization, code generation, code optimization의 과정을 거쳐 기계어를 생성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;더 깊은 software stack에 대한 동기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;언제, 그리고 왜 abstraction layer를 사용할 수 있을까? 크게 3가지가 있다. detail을 숨기고 싶을 때는 programmability를, 최적화된 기능을 제공하고 싶을 땐 performance를, portable code를 작성하고 싶을 땐 portability이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그러나 이것은 heterogeneous system을 위한 것일까? CPU와 GPU는 서로 다른 contraint와 기능을 가지고 있기에 설계가 복잡하고, 때문에 성능을 추정하기 어렵다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;General Purpose Compiler&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;867&quot; data-origin-height=&quot;333&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ebg55r/btsB3vp5qps/kacaFwwwQJjYrgkFJt4P40/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ebg55r/btsB3vp5qps/kacaFwwwQJjYrgkFJt4P40/img.png&quot; data-alt=&quot;general compose compiler&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ebg55r/btsB3vp5qps/kacaFwwwQJjYrgkFJt4P40/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Febg55r%2FbtsB3vp5qps%2FkacaFwwwQJjYrgkFJt4P40%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;579&quot; height=&quot;222&quot; data-origin-width=&quot;867&quot; data-origin-height=&quot;333&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;general compose compiler&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Application Programming Interface: API&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;939&quot; data-origin-height=&quot;242&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/baR6Hn/btsB6kuLI6h/IGveny3TUCqRRtrx1t2RdK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/baR6Hn/btsB6kuLI6h/IGveny3TUCqRRtrx1t2RdK/img.png&quot; data-alt=&quot;API&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/baR6Hn/btsB6kuLI6h/IGveny3TUCqRRtrx1t2RdK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbaR6Hn%2FbtsB6kuLI6h%2FIGveny3TUCqRRtrx1t2RdK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;643&quot; height=&quot;166&quot; data-origin-width=&quot;939&quot; data-origin-height=&quot;242&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;API&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;software 객체 간의 상호작용을 정의하는 library 기반 interface이다. 이를 통해 기본 구현을 추상화하고, 개발자가 필요로 하는 객체나 동작만 노출시켜 programming을 간단하게 만든다. 언어에서 만든 추가적인 abstraction layer라고 생각하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Domain Specific Language&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;871&quot; data-origin-height=&quot;424&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RHwGc/btsB5VIPivS/hN3ksWWssKKm7nCVZ4Dp9K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RHwGc/btsB5VIPivS/hN3ksWWssKKm7nCVZ4Dp9K/img.png&quot; data-alt=&quot;domain specific language&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RHwGc/btsB5VIPivS/hN3ksWWssKKm7nCVZ4Dp9K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRHwGc%2FbtsB5VIPivS%2FhN3ksWWssKKm7nCVZ4Dp9K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;577&quot; height=&quot;281&quot; data-origin-width=&quot;871&quot; data-origin-height=&quot;424&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;domain specific language&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;domain specific language는 특정 domain에 대한 더 높은 abstraction을 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;맞춤형 추상화라고 불리우며, 이를 통해 domain 전문가들에 대한 접근성과 신뢰성을 향상시킬 수 있다. 코드가 더 짧아지고 compiler는 정해진 boilerplate code를 생성하기 때문이다. 또한 표현력을 일부 제한해서 domain 수준에서 유효성을 검사하고 최적화를 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Library와의 비교&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;복잡한 libaray는 사용하기 어려울 수 있다. type checking은 host language level에서만 사용할 수 있다. domain 지식을 활용하거나 여러 artifact를 생성하기 더 어렵다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Framework&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;589&quot; data-origin-height=&quot;488&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dVlmGh/btsB6kVTqLP/gpkCzoXy81G2FAc02nnjik/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dVlmGh/btsB6kVTqLP/gpkCzoXy81G2FAc02nnjik/img.png&quot; data-alt=&quot;framework&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dVlmGh/btsB6kVTqLP/gpkCzoXy81G2FAc02nnjik/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdVlmGh%2FbtsB6kVTqLP%2FgpkCzoXy81G2FAc02nnjik%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;371&quot; height=&quot;307&quot; data-origin-width=&quot;589&quot; data-origin-height=&quot;488&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;framework&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;framework는 프로그램을 구축하고 배포하는 표준 방식을 사용해 개발을 용이하게 한다. compiler, code library, toolset, API, 개발환경, 테스팅 환경, 실행환경 등이 포함된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;framework는 domain specific language 그 이상의 구현이다.&amp;nbsp;다양한 engineering 장점들을 제공한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;modularity : 다른 시스템이 작업을 수행하는 방식에 대한 자유로운 모듈&lt;/li&gt;
&lt;li&gt;extensibility : 작업의 실행과 구현이 분리된다.&lt;/li&gt;
&lt;li&gt;flexbility : 언어가 부여한 구조나 규칙이 없다.&lt;/li&gt;
&lt;li&gt;development support : 개발/디버그 환경이나 utility 등 프로그램이 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Inversion of Control: IoC&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;941&quot; data-origin-height=&quot;338&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yLLhM/btsB2PCjp6Y/z9v2KdMXQh3aRoPSkTw011/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yLLhM/btsB2PCjp6Y/z9v2KdMXQh3aRoPSkTw011/img.png&quot; data-alt=&quot;inversion of control&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yLLhM/btsB2PCjp6Y/z9v2KdMXQh3aRoPSkTw011/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyLLhM%2FbtsB2PCjp6Y%2Fz9v2KdMXQh3aRoPSkTw011%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;532&quot; height=&quot;191&quot; data-origin-width=&quot;941&quot; data-origin-height=&quot;338&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;inversion of control&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;기존 코드에서는 사용자가 모든 library를 호출한다. 그러나 framework는 framework가 사용자가 작성한 코드를 호출한다. 때문에 dependency의 책임을 higher-level code에게 전가한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;HPC를 위한 software stack&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;934&quot; data-origin-height=&quot;548&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjXl4q/btsB3sfOFDZ/s6e6rIgCI8yYYd6MEZNPi1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjXl4q/btsB3sfOFDZ/s6e6rIgCI8yYYd6MEZNPi1/img.png&quot; data-alt=&quot;heterogeneous system을 위한 programming&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjXl4q/btsB3sfOFDZ/s6e6rIgCI8yYYd6MEZNPi1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbjXl4q%2FbtsB3sfOFDZ%2Fs6e6rIgCI8yYYd6MEZNPi1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;581&quot; height=&quot;341&quot; data-origin-width=&quot;934&quot; data-origin-height=&quot;548&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;heterogeneous system을 위한 programming&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cBZZfF/btsB7jISmhu/RuHJHB84tF5gvh1i8zHmPk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cBZZfF/btsB7jISmhu/RuHJHB84tF5gvh1i8zHmPk/img.png&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;430&quot; data-is-animation=&quot;false&quot; width=&quot;698&quot; height=&quot;319&quot; data-widthpercent=&quot;44.58&quot; style=&quot;width: 44.0597%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cBZZfF/btsB7jISmhu/RuHJHB84tF5gvh1i8zHmPk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcBZZfF%2FbtsB7jISmhu%2FRuHJHB84tF5gvh1i8zHmPk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;940&quot; height=&quot;430&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cKeG9L/btsB8UonBZ9/9IwsMDtI3dZa8oasrvN7Fk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cKeG9L/btsB8UonBZ9/9IwsMDtI3dZa8oasrvN7Fk/img.png&quot; data-origin-width=&quot;1098&quot; data-origin-height=&quot;404&quot; data-is-animation=&quot;false&quot; width=&quot;710&quot; height=&quot;261&quot; style=&quot;width: 54.7776%;&quot; data-widthpercent=&quot;55.42&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cKeG9L/btsB8UonBZ9/9IwsMDtI3dZa8oasrvN7Fk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcKeG9L%2FbtsB8UonBZ9%2F9IwsMDtI3dZa8oasrvN7Fk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1098&quot; height=&quot;404&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;high level 언어 기반 프로그래밍&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;왼쪽은 host를 직접 짜는 경우이다. 이 경우 구현이 더 투명해지며, 실행 동작을 직접 관리할 수 있고 platform에 indepdent하게 동작할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;오른쪽은 framework에서 짜는 경우이다. framework는 scheduling과 mapping을 제어해 높은 runtime flexibility와 시스템 전반적인 최적화를 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;AMD ROCm&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;GPU 가속화된 HPC, 과학 계산, CAD를 위해 디자인되었다.&lt;/li&gt;
&lt;li&gt;모듈로 나누고, 최소한의 소프트웨어 개발&lt;/li&gt;
&lt;li&gt;framework, library, driver, programming model, linux kernel support로 구성되었다.&lt;/li&gt;
&lt;li&gt;성능이나 확장성에 최적화되어 있다.&lt;/li&gt;
&lt;li&gt;특히 ROCm 5.6의 경우 LLM을 위한 최적화된 library를 제공한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Intel OneAPI&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;heterogeneous processing architecture를 위한 프로그램 개발을 위한 통일되고 간소화된 프로그래밍 모델을 위한 industry&lt;/li&gt;
&lt;li&gt;Intel Xeon이나 Core processor, Intel FPGA를 목표로 한다.&lt;/li&gt;
&lt;li&gt;기존 AI나 HPC 프로그래밍 모델과 호환된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;NVICIDA CUDA와 CUDA-X&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;고성능 AI 및 HPC를 위한 library와 도구의 collection.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;GPU library&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;CUDA library&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;CUDA math library&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;exponential, logarithmic, trrigonometric, hyperbolic, vector norm 등 기본적인 수학 함수를 지원한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;cuBLAS&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;dense matrix를 위한 선형대수 subroutine 지원&lt;/li&gt;
&lt;li&gt;matrix-vector나 matrix-matrix 곱셈 지원&lt;/li&gt;
&lt;li&gt;user kernel에서 cuBLAS 호출 가능. (device API이다.)&lt;/li&gt;
&lt;li&gt;CUDA stream 지원&lt;/li&gt;
&lt;li&gt;cuBLASxt는 multiple GPU도 지원한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;cuFFT&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;1D, 2D, 3D FFT&lt;/li&gt;
&lt;li&gt;cuBLAS와 유사하게 user host code에서 호출된다.&lt;/li&gt;
&lt;li&gt;batch로 독맂벅인 변환을 수행하는 기능을 지원한다. 예를 들어 3D dataset에서 1D transform 등&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;cuTENSOR&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;tensor 선형대수 library&lt;/li&gt;
&lt;li&gt;새로운 tensor core를 활용한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;cuSPARSE&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;sparse matrix를 위한 작업 지원&lt;/li&gt;
&lt;li&gt;sparse matrix-vector와 matrix-matrix 곱셈 포함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;cuRAND&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;난수 생성&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;cuSOLVER&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;cuBLAS나 cuSPARSE library를 기반으로 한 고수준 package&lt;/li&gt;
&lt;li&gt;LAPACK dense solver를 제공하며, Intel MKL보다 3-6배 빠르다. sparse direct solver는 CPU보다 2-14배 빠르다.&lt;/li&gt;
&lt;li&gt;최신 버전은 low-precision tensor core 연산을 사용한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;CUTLASS&lt;/b&gt; (CUDA Template for Linear Algebra Subroutines)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;GEMM 행렬 곱을 구현하기 위한 CUDA C++ 템플릿 추상화 collection&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;cuDNN&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;deep neural network를 위한 library&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;nvGraph&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;그래프 분석 library&lt;/li&gt;
&lt;li&gt;page rank, single source shortest path, single source widest path 등을 지원한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;NPP&lt;/b&gt; (NVIDIA Performance Primitive)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;imaging이나 video 처리를 위한 library&lt;/li&gt;
&lt;li&gt;filtering, JPEG decoding 등을 지원한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;NCCL&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;NVIDIA GPU 및 네트워킹에 최적화된 multi GPU 및 multi node 통신을 구현한다.&lt;/li&gt;
&lt;li&gt;all-gather, all-reduce, broadcase, reduce, reduce-scatter, P2P 등 지원&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Thrust&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;C++ STL 기반 interface를 갖는 고수준 C++ template library&lt;/li&gt;
&lt;li&gt;사용자는 CUDA 코드 없이 표준 C++ 코드를 작성하지만 GPU 병렬화의 이점을 가질 수 있다.&lt;/li&gt;
&lt;li&gt;x86도 지원한다.&lt;/li&gt;
&lt;li&gt;memory 관리나 data movement를 간소화한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;AMD ROCc Library&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AMD의 ROCm 런타임 및 toolchain에서 매우 유사한 library set을 제공한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;HIP 프로그래밍 언어로 구현되어 AMD GPU에 최적화되어 있다.&lt;/li&gt;
&lt;li&gt;rocBLAS, rocFFT, rocSPARSE, RCCL 등이있다.&lt;/li&gt;
&lt;li&gt;hipBLAS나 hipSPARSE는 backend를 지원하는 marshalling library이다.&lt;/li&gt;
&lt;li&gt;rocThrust는 thrust를 위한 HIP backend이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;잘못된 내용이나 오탈자에 대한 지적, 질문&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;b&gt;등은 언제나 환영합니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>CS/Parallel Computing</category>
      <author>hyelie</author>
      <guid isPermaLink="true">https://hyelie.tistory.com/686</guid>
      <comments>https://hyelie.tistory.com/entry/%EC%9D%B4%EC%A2%85%EB%B3%91%EB%A0%AC%EC%BB%B4%ED%93%A8%ED%8C%85-Heterogeneous-Parallel-Computer%EB%A5%BC-%EC%9C%84%ED%95%9C-%EA%B8%B0%EC%88%A0-%EC%8A%A4%ED%83%9D#entry686comment</comments>
      <pubDate>Mon, 18 Dec 2023 00:16:09 +0900</pubDate>
    </item>
    <item>
      <title>[이종병렬컴퓨팅] OpenMP</title>
      <link>https://hyelie.tistory.com/entry/%EC%9D%B4%EC%A2%85%EB%B3%91%EB%A0%AC%EC%BB%B4%ED%93%A8%ED%8C%85-OpenMP</link>
      <description>&lt;blockquote style=&quot;text-align: left;&quot; data-ke-style=&quot;style2&quot;&gt;이 글은 포스텍 성효진 교수님의 이종병렬컴퓨팅(CSED490C) 강의를 기반으로 재구성한 것입니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 글에서는 OpenMP 프로그래밍 모델의 기본 개념과 pragma, 간단한 예제들을 살펴본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;OpenMP&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;OpenMP API는 pragma(컴파일러 지시문), library routine, 환경변수 등을 제공한다. 이를 통해 multithread parallel fortran이나 C/C++ 프로그램을 쓸 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;b&gt;programmability와 portability를 위한 high-level parallel structure를 제공&lt;/b&gt;한다. 때문에 low-level thread를 조작하는 것보다 parallel program을 쓰고 유지하는 것이 더 쉽다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;SMP 관행을 표준화하며, vectorization과 heterogeneous device programming도 지원한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Motivation&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;OpenMP 프로그래머는 &lt;b&gt;1) sequential 버전으로 시작&lt;/b&gt;한 다음, &lt;b&gt;2) OpenMP 지시문을 추가&lt;/b&gt;한다. 그러면 대부분의 kernel 생성, memory allocation, data 전송을 &lt;b&gt;OpenMP compiler에게 맡기게 된다&lt;/b&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;한편, OpenMP 코드는 pragma를 무시하고 non-OpenMP 컴파일러에서 컴파일할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Tradeoff&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;장점으로는 &lt;b&gt;low-level detail을 알 필요 없이&lt;/b&gt; sequential program에서 parallel version으로 빠르게 변환할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;단점은 &lt;b&gt;OpenMP 프로그램의 성능은 컴파일러의 성능에 크게 의존&lt;/b&gt;한다. 몇몇 OpenMP pragma는 컴파일러에게 hint일 수 있지만, 그렇지 않을 수도 있다. 때문에 컴파일러가 pragma에 따라 동작하지 않는 이유를 찾기 어렵다. 이러한 불확실성은 CUDA나 OpenCL 프로그램에 비하면 훨씬 덜하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;OpenMP common core&lt;/h2&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 292px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 45.9302%; height: 19px;&quot;&gt;OpenMP pragma, function, clause&lt;/td&gt;
&lt;td style=&quot;width: 54.0698%; height: 19px;&quot;&gt;개념&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 45.9302%; height: 20px;&quot;&gt;#pragma omp parallel&lt;/td&gt;
&lt;td style=&quot;width: 54.0698%; height: 20px;&quot;&gt;병렬 영역, thread team, structured block, thread 간 교차 실행&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 41px;&quot;&gt;
&lt;td style=&quot;width: 45.9302%; height: 41px;&quot;&gt;int omp_get_thread_num() int omp_get_num_threads()&lt;/td&gt;
&lt;td style=&quot;width: 54.0698%; height: 41px;&quot;&gt;병렬 영역에서 thread를 생성하고, thread ID를 사용해 여러 개의 thread를 식별하고 작업을 분할한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 41px;&quot;&gt;
&lt;td style=&quot;width: 45.9302%; height: 41px;&quot;&gt;double omp_get_wtime()&lt;/td&gt;
&lt;td style=&quot;width: 54.0698%; height: 41px;&quot;&gt;Amdahl's law를 적용한 speedup. false sharing과 기타 성능 문제&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 45.9302%; height: 20px;&quot;&gt;setenv OMP_NUM_THREADS N&lt;/td&gt;
&lt;td style=&quot;width: 54.0698%; height: 20px;&quot;&gt;내부 제어 변수로, 기본 thread 개수를 설정한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 45.9302%; height: 20px;&quot;&gt;#pragma omp barrier #pragma omp critical&lt;/td&gt;
&lt;td style=&quot;width: 54.0698%; height: 20px;&quot;&gt;synchronization과 race conditioon. 교차 실행을 revisit한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 45.9302%; height: 20px;&quot;&gt;#pragma omp for #pragma omp parallel for&lt;/td&gt;
&lt;td style=&quot;width: 54.0698%; height: 20px;&quot;&gt;workshaing, 병렬 loop, loop carried dependency&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 45.9302%; height: 20px;&quot;&gt;reduction(op:list)&lt;/td&gt;
&lt;td style=&quot;width: 54.0698%; height: 20px;&quot;&gt;thread team 사이에서 값을 reduction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 45.9302%; height: 20px;&quot;&gt;schedule(dynamic [,chunk]) schedule (static [, chunk])&lt;/td&gt;
&lt;td style=&quot;width: 54.0698%; height: 20px;&quot;&gt;loop 계획, loop overhead과 load balance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 45.9302%; height: 20px;&quot;&gt;private(list), firstprivate(list), shared(list)&lt;/td&gt;
&lt;td style=&quot;width: 54.0698%; height: 20px;&quot;&gt;data 환경&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 45.9302%; height: 17px;&quot;&gt;nowait&lt;/td&gt;
&lt;td style=&quot;width: 54.0698%; height: 17px;&quot;&gt;작업 공유 구조에서 barrier 비활성화. barrier의 high cost. flush 개념&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 45.9302%; height: 17px;&quot;&gt;#pragma omp single&lt;/td&gt;
&lt;td style=&quot;width: 54.0698%; height: 17px;&quot;&gt;single thread로 작업 공유&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 45.9302%; height: 17px;&quot;&gt;#pragma omp task #pragma omp taskwait&lt;/td&gt;
&lt;td style=&quot;width: 54.0698%; height: 17px;&quot;&gt;작업에 대한 데이터 환경을 포함한 작업&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;OpenMP Device Model&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;649&quot; data-origin-height=&quot;352&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/byARfD/btsB6LTbPSR/PNTyKrc6nFwJiIOtEvDic1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/byARfD/btsB6LTbPSR/PNTyKrc6nFwJiIOtEvDic1/img.png&quot; data-alt=&quot;OpenMP device model&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/byARfD/btsB6LTbPSR/PNTyKrc6nFwJiIOtEvDic1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbyARfD%2FbtsB6LTbPSR%2FPNTyKrc6nFwJiIOtEvDic1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;385&quot; height=&quot;209&quot; data-origin-width=&quot;649&quot; data-origin-height=&quot;352&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;OpenMP device model&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;OpenMP는 &lt;b&gt;host/device model을 사용&lt;/b&gt;한다. &lt;b&gt;host는 초기 thread가 실행을 시작하는 위치&lt;/b&gt;이며, 0개 이상의 device가 host에 연결된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;하나의 host, 여러 개의 device가 존재한다. &lt;b&gt;각 device는 1개 이상의 compute unit으로 구성&lt;/b&gt;되며, &lt;b&gt;각 compute unit은 1개 이상의 processing element로 구성&lt;/b&gt;된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;memory는 host memory와 device memory로 분할된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;520&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BRSmN/btsB2RNye9R/mfRRa0nVQjktkRADSTByrk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BRSmN/btsB2RNye9R/mfRRa0nVQjktkRADSTByrk/img.png&quot; data-alt=&quot;Host/Device Platform Model&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BRSmN/btsB2RNye9R/mfRRa0nVQjktkRADSTByrk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBRSmN%2FbtsB2RNye9R%2FmfRRa0nVQjktkRADSTByrk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;538&quot; height=&quot;350&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;520&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Host/Device Platform Model&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;target&lt;/b&gt; : device로 진입하기 위한 대상 구성&lt;/li&gt;
&lt;li&gt;&lt;b&gt;teams&lt;/b&gt; : 각 computing unit에 하나의 team thread로 이뤄진 team league를 만든다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;distribute&lt;/b&gt; : distribute clause로 team에게 loop iteration block을 할당한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;parallel&lt;/b&gt; &lt;b&gt;for&lt;/b&gt; : 각 loop iteration block을 processing element에서 실행한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;예시 : Vector Add&lt;/h4&gt;
&lt;pre id=&quot;code_1702809508898&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;omp.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#define N 1024

int main() {
    float a[N], b[N], c[N];
    // initialize a, b and c ....
    
#pragma omp target // thread는 host에서 실행된다.

    for (int i = 0; i &amp;lt; N; i++)
        c[i] += a[i] + b[i];

    // Test results, report results ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;여기서 `#pragma omp target`을 하면 thread는 host에서 실행된다. original variable i, a, b, c는 construct 초기에 device로 복사된다. target construct는 code 영역을 device로 offload한다. 이후 계산이 끝나면 i, a, b, c는 host로 돌아온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;`target` : device에서 실행 중인 초기 thread가 code block의 code를 실행한다. 즉, single thread가 loop를 sequential하게 실행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1702809660928&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;omp.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#define N 1024

int main() {
    float a[N], b[N], c[N];
    // initialize a, b and c ....
    
#pragma omp target
#pragma omp teams

    for (int i = 0; i &amp;lt; N; i++)
        c[i] += a[i] + b[i];

    // Test results, report results ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;`teams` : 같은 개수의 thread를 가진 여러 개의 thread group이 시작된다. 실행은 각 team의 master thread에 의해 계속된다. team들끼리는 synchronization이 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1702809718062&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;omp.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#define N 1024

int main() {
    float a[N], b[N], c[N];
    // initialize a, b and c ....
    
#pragma omp target
#pragma omp teams distribute

    for (int i = 0; i &amp;lt; N; i++)
        c[i] += a[i] + b[i];

    // Test results, report results ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;`distribute` : team의 master thread가 loop iteration을 분배한다. (static distribution) 정해진 실행 순서가 없고, thread team 내부에서 parallelism이나 work-sharing을 생성하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1702809787660&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;omp.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#define N 1024

int main() {
    float a[N], b[N], c[N];
    // initialize a, b and c ....
    
#pragma omp target
#pragma omp teams distribute parallel for

    for (int i = 0; i &amp;lt; N; i++)
        c[i] += a[i] + b[i];

    // Test results, report results ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;`parallel for` : loop 반복을 team 내의 thread에게 분배한다. `teams`가 없는 경우 오직 하나의 team만 존재하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Target Data Environment&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1186&quot; data-origin-height=&quot;532&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bPy7Fp/btsCc4jCSy5/kkAKJlejxgXj608whAKTp1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bPy7Fp/btsCc4jCSy5/kkAKJlejxgXj608whAKTp1/img.png&quot; data-alt=&quot;target data environment&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bPy7Fp/btsCc4jCSy5/kkAKJlejxgXj608whAKTp1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbPy7Fp%2FbtsCc4jCSy5%2FkkAKJlejxgXj608whAKTp1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;664&quot; height=&quot;298&quot; data-origin-width=&quot;1186&quot; data-origin-height=&quot;532&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;target data environment&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;host thread에서 `#pragma`로 OpenMP를 실행시키면 다음과 같은 일이 일어난다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;host thread는 task가 끝나기를 기다린다.&lt;/li&gt;
&lt;li&gt;A, B 등 원래 값들이 device로 복사된다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;target 영역에서 참조된 scalar나 static allocated array는 implicitly하게 host - device에서 복사된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;device에서 값을 parallel하게 계산한다.&lt;/li&gt;
&lt;li&gt;device에서 계산이 끝난 A, B 등 변수 값들을 host로 돌린다.&lt;/li&gt;
&lt;li&gt;host thread가 이어 실행한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Data Movemont 관리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;data movement는 `map`을 사용해 explicitly 관리할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1702823138376&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int i, a[N], b[N], c[N];
#pragma omp target map(to:a, b) map(toform:c)&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;map(to: list) : device에서 read-only data&lt;/li&gt;
&lt;li&gt;map(from: list) : device에서 write-only data. target 영역의 끝에서 list 내의 변수들이 원래 값에 복사된다.&lt;/li&gt;
&lt;li&gt;map(tofrom: list) : to와 from 둘 다 동작한다.&lt;/li&gt;
&lt;li&gt;map(alloc: list) : data가 device에서 할당되고, 초기화되지 않은 상태&lt;/li&gt;
&lt;li&gt;map(to: a[0:N]) : pointer의 경우 array notation을 사용해야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;이외 자주 사용되는 Clause&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Target과 자주 사용되는 Clause&lt;/h4&gt;
&lt;pre id=&quot;code_1702823287060&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#pragma omp target [clause[[,]clause]...]
structure-block&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;if (scalar expression) : scalar expression이 false면 target은 host에 의해 실행된다.&lt;/li&gt;
&lt;li&gt;device (integer expression) : integer expression의 값은 device를 결정한다.&lt;/li&gt;
&lt;li&gt;private(list) firstprivate(list) : list에 있는 변수와 동일한 변수를 device에 생성한다. firstprivate의 경우 host에서 원래 변수의 값이 device에서 생성된 private 변수로 복사된다.&lt;/li&gt;
&lt;li&gt;map(map-type: list[0:N]) : list의 변수가 host와 device 간에 어떻게 이동하는지 정의한다.&lt;/li&gt;
&lt;li&gt;nowait : target 작업이 연기되어 host와 target 영역이 parallel하게 실행된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Teams Distribute Parallel For과 사용되는 Clause&lt;/h4&gt;
&lt;pre id=&quot;code_1702823404607&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#pragma omp teams distribute parallel for [clause[[,]clause]...]
for-loop&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;reduction(reduction-identifier : list) : list의 변수에 대해 reduction 연산을 수행한다. reduction 변수는 map clause에도 나타나야 한다.&lt;/li&gt;
&lt;li&gt;collapse(n) : distribute 지시어가 iteration을 team에게 분배하기 전에 loop를 합친다.&lt;/li&gt;
&lt;li&gt;schedule(kind[, chunk_size]) : loop iteration을 team에게 분배하는 것을 제어한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Target Data Directive&lt;/h4&gt;
&lt;pre id=&quot;code_1702823708632&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#pragma omp target data map(to: A,B) map(from: C)
{
#pragma omp target
 // do lots of stuff with A, B, and C
//do something on the host
#pragma omp target
 // do lots of stuff with A, B, and C
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;`target data`는 target data region을 생성한다.&lt;/li&gt;
&lt;li&gt;`map`은 explicit한 data 관리를 위해 사용한다. device data 환경에 data가 지시문의 시작과 끝에 복사한다.&lt;/li&gt;
&lt;li&gt;`target data` 영역 내에서 여러 `target` 지시문이 하나의 data 영역과 작업할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Target Update Directive&lt;/h4&gt;
&lt;pre id=&quot;code_1702823722729&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#pragma omp target data map(to: A,B) map(from: C)
{
#pragma omp target
 // do lots of stuff with A, B, and C
#pragma omp target update from(A)
// do something with A on the host
#pragma omp target update to(A)
#pragma omp target
 // do lots of stuff with A, B, and C&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;`target update`를 사용해 target 영역 간의 data를 갱신할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;CUDA vs OpenMP : Vadd&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- CUDA의 경우&lt;/p&gt;
&lt;pre id=&quot;code_1702823785903&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;_global__ vadd(float *a, float *b, float *c) {
    int i = blockDim.x * blockIdx.x + threadIdx.x;
    c[i] += a[i] + b[i];
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- OpenMP의 경우&lt;/p&gt;
&lt;pre id=&quot;code_1702823817875&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include&amp;lt;omp.h&amp;gt;
#include&amp;lt;stdio.h&amp;gt;

#define N 1024

int main() {
    float a[N], b[N], c[N];
    // initialize a, b and c ....
#pragma omp target map(to:a,b) map(tofrom:c) // device로 정보 옮김
#pragma omp teams num_teams(NCU) thread_limit(NPE) // device 묘사. NCU는 CU의 개수, NPE는 CU당 PE의 개수
#pragma omp distribute // thread block을 compute unit에게 분배
    for (ib=0; ib&amp;lt;N; ib+=tbsize)
#pragma omp parallel for // 바로 아래의 for문은 thread block의 개별 thread가 실행
    for(int i=ib; i&amp;lt;ib+tbsize; i++)
        c[i] += a[i] + b[i];
    // Test results, report results ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;잘못된 내용이나 오탈자에 대한 지적, 질문&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;b&gt;등은 언제나 환영합니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>CS/Parallel Computing</category>
      <author>hyelie</author>
      <guid isPermaLink="true">https://hyelie.tistory.com/684</guid>
      <comments>https://hyelie.tistory.com/entry/%EC%9D%B4%EC%A2%85%EB%B3%91%EB%A0%AC%EC%BB%B4%ED%93%A8%ED%8C%85-OpenMP#entry684comment</comments>
      <pubDate>Sun, 17 Dec 2023 23:42:31 +0900</pubDate>
    </item>
    <item>
      <title>[이종병렬컴퓨팅] Parallel Patterns : Sparse Computation</title>
      <link>https://hyelie.tistory.com/entry/%EC%9D%B4%EC%A2%85%EB%B3%91%EB%A0%AC%EC%BB%B4%ED%93%A8%ED%8C%85-Parallel-Patterns-Sparse-Computation</link>
      <description>&lt;blockquote style=&quot;text-align: left;&quot; data-ke-style=&quot;style2&quot;&gt;이 글은 포스텍 성효진 교수님의 이종병렬컴퓨팅(CSED490C) 강의를 기반으로 재구성한 것입니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 글에서는 memory bandwidth를 절감가힉 위한 parallel sparse computation에서 input data를 압축하는 방법을 살펴본다. 이를 통해 memory의 utilization을 높일 수 있고, on-chip memory로 전송되는 data 크기를 줄일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;또한 clamping으로 variation 제한, sorting, transposition 등으로 불규칙한 데이터를 규칙적으로 만드는 방법을 살핀다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Sparse Matrix&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;자연 상태의 많은 system들은 sparse하다. 보통 &lt;b&gt;matrix의 80% 이상이 0이면 sparse matrix&lt;/b&gt;라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Sparse Matrix-Vector Multiplication, SpMV&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;876&quot; data-origin-height=&quot;412&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dKTUya/btsB80IyVwl/iph60GEykxceENex8zohHk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dKTUya/btsB80IyVwl/iph60GEykxceENex8zohHk/img.png&quot; data-alt=&quot;sparse matrix-vector multiplication&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dKTUya/btsB80IyVwl/iph60GEykxceENex8zohHk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdKTUya%2FbtsB80IyVwl%2Fiph60GEykxceENex8zohHk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;434&quot; height=&quot;204&quot; data-origin-width=&quot;876&quot; data-origin-height=&quot;412&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;sparse matrix-vector multiplication&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;position: absolute;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;sparse matrix-vector multiplication는 많은 곳에서 사용되는데, 예를 들어 계수행렬 A, vector x, y에 대해 linear system Ax = y의 역행렬을 구해야 할 때 사용된다.&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;sparse linear system 해결&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;sparse matrix vector multiplication을 기반으로 하는 iterative conjugate gradient solver가 일반적인 방법이다.&lt;/li&gt;
&lt;li&gt;mv(A)가 A와 matrix-vector 곱을 계산할 때 시간복잡도일 때, O(mv(A) * n)의 시간이 걸린다.&lt;/li&gt;
&lt;li&gt;이 경우 SpMV operation에 대해 100초 이상이 걸린다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;신경쓸 것들&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;dense matrix multiplication과 비교했을 때 &lt;b&gt;SpMV은 불규칙하고, 구조화되지 않는다&lt;/b&gt;. 또한 input data reuse가 거의 없고, compiler transformation tool로 이득을 얻기 힘들다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;때문에 성능을 높이기 위해서는 divergence나 load imbalance를 줄여 regularity를 최대화하거나, layout을 재배열해 DRAM burst utilization을 최대화하는 방법이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Sparse Matrix Format&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;831&quot; data-origin-height=&quot;485&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/biB3f7/btsB6w9jgNB/NpcRKRz0opMN05rHbm4w5k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/biB3f7/btsB6w9jgNB/NpcRKRz0opMN05rHbm4w5k/img.png&quot; data-alt=&quot;sparse matrix format&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/biB3f7/btsB6w9jgNB/NpcRKRz0opMN05rHbm4w5k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbiB3f7%2FbtsB6w9jgNB%2FNpcRKRz0opMN05rHbm4w5k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;452&quot; height=&quot;264&quot; data-origin-width=&quot;831&quot; data-origin-height=&quot;485&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;sparse matrix format&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;왼쪽으로 갈수록 구조화된 것, 오른쪽으로 갈수록 구조가 없는 것이다. 이 글에서는 &lt;b&gt;ELL&lt;/b&gt;, &lt;b&gt;CSR&lt;/b&gt;, &lt;b&gt;HYB&lt;/b&gt;, &lt;b&gt;COO&lt;/b&gt;, &lt;b&gt;JDS&lt;/b&gt; 정도만 살펴본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;간단한 Parallel SpMV&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;489&quot; data-origin-height=&quot;241&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bI2lY0/btsB4Tdg2fs/k6wi592pGsnm7Pzvm4wr90/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bI2lY0/btsB4Tdg2fs/k6wi592pGsnm7Pzvm4wr90/img.png&quot; data-alt=&quot;간단한 parallel SpMV&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bI2lY0/btsB4Tdg2fs/k6wi592pGsnm7Pzvm4wr90/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbI2lY0%2FbtsB4Tdg2fs%2Fk6wi592pGsnm7Pzvm4wr90%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;241&quot; height=&quot;119&quot; data-origin-width=&quot;489&quot; data-origin-height=&quot;241&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;간단한 parallel SpMV&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;각 thread가 하나의 row를 처리하면 된다. 이걸 이제부터 발전시켜 나갈 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Compressed Sparse Row Format : CSR&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;823&quot; data-origin-height=&quot;191&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b8413h/btsB5UJGNoD/CUBKU8TtothXDweFovOZH1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b8413h/btsB5UJGNoD/CUBKU8TtothXDweFovOZH1/img.png&quot; data-alt=&quot;compressed sparse row format&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b8413h/btsB5UJGNoD/CUBKU8TtothXDweFovOZH1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb8413h%2FbtsB5UJGNoD%2FCUBKU8TtothXDweFovOZH1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;483&quot; height=&quot;112&quot; data-origin-width=&quot;823&quot; data-origin-height=&quot;191&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;compressed sparse row format&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위 형식은 &lt;b&gt;compressed sparse row format&lt;/b&gt;이다. non-zero element에 대한 정보만 저장한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;data[]&lt;/b&gt; : 값들을 저장한다. length는 matrix에서 non-zero element의 개수와 동일하다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;col_index[]&lt;/b&gt; : column index를 저장한다. length는 non-zero element의 개수와 동일하다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;row_ptr[]&lt;/b&gt; : 각 row에 non-zero element의 개수가 몇개인지 prefix sum 형태로 저장된다. l&lt;span style=&quot;text-align: left;&quot;&gt;ength는 row + 1과 동일하다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;예시&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DHUok/btsB7e8nTO4/f8E7hLrS9UuZ6VbSI13gh0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DHUok/btsB7e8nTO4/f8E7hLrS9UuZ6VbSI13gh0/img.png&quot; data-origin-width=&quot;783&quot; data-origin-height=&quot;311&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.1303%; margin-right: 10px;&quot; data-widthpercent=&quot;49.71&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DHUok/btsB7e8nTO4/f8E7hLrS9UuZ6VbSI13gh0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDHUok%2FbtsB7e8nTO4%2Ff8E7hLrS9UuZ6VbSI13gh0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;783&quot; height=&quot;311&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b7fcqN/btsB6mzbLNW/PHM2Hn2LEtE45uA4wpy36k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b7fcqN/btsB6mzbLNW/PHM2Hn2LEtE45uA4wpy36k/img.png&quot; data-origin-width=&quot;782&quot; data-origin-height=&quot;307&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.7069%;&quot; data-widthpercent=&quot;50.29&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b7fcqN/btsB6mzbLNW/PHM2Hn2LEtE45uA4wpy36k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb7fcqN%2FbtsB6mzbLNW%2FPHM2Hn2LEtE45uA4wpy36k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;782&quot; height=&quot;307&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;CSR format example : row 0 &amp;amp; row 1&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/n0sCU/btsB6j3rDII/RzkZF2uW1CnLAKrH7fj4Gk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/n0sCU/btsB6j3rDII/RzkZF2uW1CnLAKrH7fj4Gk/img.png&quot; data-origin-width=&quot;774&quot; data-origin-height=&quot;289&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.7624%; margin-right: 10px;&quot; data-widthpercent=&quot;50.35&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/n0sCU/btsB6j3rDII/RzkZF2uW1CnLAKrH7fj4Gk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fn0sCU%2FbtsB6j3rDII%2FRzkZF2uW1CnLAKrH7fj4Gk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;774&quot; height=&quot;289&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0f5mL/btsB6nkv7E7/VY7IKemSKuDJ6kiq9kp6H0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0f5mL/btsB6nkv7E7/VY7IKemSKuDJ6kiq9kp6H0/img.png&quot; data-origin-width=&quot;795&quot; data-origin-height=&quot;301&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.0748%;&quot; data-widthpercent=&quot;49.65&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0f5mL/btsB6nkv7E7/VY7IKemSKuDJ6kiq9kp6H0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0f5mL%2FbtsB6nkv7E7%2FVY7IKemSKuDJ6kiq9kp6H0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;795&quot; height=&quot;301&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;CSR format example : row 2 &amp;amp; row 3&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위 예시의 경우, row 0에는 3, 1이, row 1에는 아무것도 없고, row 2에는 2, 4, 1이, row 3에는 1, 1이 있다.&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;row_ptr[0]은 0이고, row_ptr[1]은 row 0까지 non-zero element의 개수는 2이므로 2이다. row_ptr[2]는 row 1까지 non-zero element의 개수이므로 2이다. 같은 방식으로, row_ptr[2] = 5, row_ptr[3] = 7이다.&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이후 col_index를 보면 된다. row_ptr[1]까지 element가 2개이므로, data와 col_index의 index 0과 index 1이 row 0에 대한 정보를 가지고 있다는 것을 알 수 있다. 같은 방식으로, row_ptr[1]이 2이고 row_ptr[2]의 값이 2이므로, row 1에는 non-zero 값이 없다는 것을 알 수 있다. 똑같이 row_ptr[2]가 2, row_ptr[3]이 5이므로 row 2에는 non-zero element가 3개 있다는 것을 알 수 있고, index 2, 3, 4에 해당 element들이 저장된다.&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이게 CSR format이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Compressed Sparse Row Kernel Design&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;873&quot; data-origin-height=&quot;465&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/biAfzG/btsB7T32ym2/EDy69LXeoiUrzdT6hfm1i1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/biAfzG/btsB7T32ym2/EDy69LXeoiUrzdT6hfm1i1/img.png&quot; data-alt=&quot;compressed sparse row kernel design&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/biAfzG/btsB7T32ym2/EDy69LXeoiUrzdT6hfm1i1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbiAfzG%2FbtsB7T32ym2%2FEDy69LXeoiUrzdT6hfm1i1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;422&quot; height=&quot;225&quot; data-origin-width=&quot;873&quot; data-origin-height=&quot;465&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;compressed sparse row kernel design&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;CSR format의 경우, &lt;b&gt;row_ptr과 data를 사용해 어떤 row에 어떤 data가 있는지 알 수 있다&lt;/b&gt;. 그렇다면 &lt;b&gt;vector와 이 값을 dot product하고 더해버리면&lt;/b&gt; 해당 위치의 결과가 나오게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Parallel SpMV/CSR kernel&lt;/h3&gt;
&lt;pre id=&quot;code_1702742518437&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;__global__ void SpMV_CSR(int num_rows, float* data, int* col_index, int* row_ptr, float* x, float* y) {
    int row = blockDim.x*blockIdx.x + threadIdx.x;
 
    if (row &amp;lt; num_rows) {
        float dot = 0;
        int row_start = row_ptr[row];
        int row_end = row_ptr[row+1];
 
        for (int i = row_start; i &amp;lt; row_end; i++)
            dot += data[i] * x[col_index[i]];
        y[row] = dot;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;CSR의 단점&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;첫 번째는 &lt;b&gt;kernel의 memory access가 coalesce되지 않았다&lt;/b&gt;는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위 예시로 보자면, 첫 번째 for문에서 thread 0은 data[0], thread 1은 x, thread 2는 data[2], thread 3은 data[5]에 접근한다. 다음 for문에서 thread 0은 data[1], thread 1은 x, thread 2는 data[3], thread 4는 data[6]에 접근한다. 때문에 memory coalesce가 발생하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;두 번째는 &lt;b&gt;divergence가 발생한다&lt;/b&gt;는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;각 thread가 수행하는 for문의 반복 회수는 해당 thread가 접근하는 row의 non-zero element의 개수인데, 이는 row마다 달라질 수 있기 때문에 &lt;b&gt;반복 회수가 thread마다 매우 달라진다&lt;/b&gt;. 즉 divergence가 발생하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Regularizing SpMV with ELL(PACK) Format : ELL&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;722&quot; data-origin-height=&quot;375&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cyFtXk/btsB3sfAYP2/GfqVuJd3ZrPlyG66FoKj1K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cyFtXk/btsB3sfAYP2/GfqVuJd3ZrPlyG66FoKj1K/img.png&quot; data-alt=&quot;ELL format&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cyFtXk/btsB3sfAYP2/GfqVuJd3ZrPlyG66FoKj1K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcyFtXk%2FbtsB3sfAYP2%2FGfqVuJd3ZrPlyG66FoKj1K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;402&quot; height=&quot;209&quot; data-origin-width=&quot;722&quot; data-origin-height=&quot;375&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;ELL format&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;앞서 살펴본 CSR format의 &lt;b&gt;divergence 문제와 memory access pattern 문제를 해결하기 위해 padding과 transpose를 적용&lt;/b&gt;해 해결할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;먼저 non-zero element의 개수가 제일 많은 row를 찾는다. 이후 다른 row들은 해당 row와 크기가 동일하도록 padding(0)을 추가한다. 왼쪽 그림의 예시에서는 row 0에서는 1개, row 1에서는 3개, row 2에서는 0개, row 3에서는 1개의 padding이 추가된다. 이 때 col_index 배열 또한 같은 방식으로 padding을 적용해야 한다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;이 경우 몇몇 row만 다른 row들에 비해 훨씬 길면 padding이 비효율적으로 형성된다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이후 이렇게 만들어진 data[]를 transpose한다. 이에 맞춰 col_index[]에 있는 값들도 mapping을 유지하기 위해 같은 방식으로 transpose한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그러면 row i의 시작이 위치가 data[i]로 바뀌었기 때문에 row_ptr이 필요없어진다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ELL Kernel Design&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;506&quot; data-origin-height=&quot;523&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Ty1Ry/btsB2Rs9OPc/lEKY6T6C09QktJXuiSjijK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Ty1Ry/btsB2Rs9OPc/lEKY6T6C09QktJXuiSjijK/img.png&quot; data-alt=&quot;ELL kernel design&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Ty1Ry/btsB2Rs9OPc/lEKY6T6C09QktJXuiSjijK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTy1Ry%2FbtsB2Rs9OPc%2FlEKY6T6C09QktJXuiSjijK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;246&quot; height=&quot;254&quot; data-origin-width=&quot;506&quot; data-origin-height=&quot;523&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;ELL kernel design&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;962&quot; data-origin-height=&quot;457&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dloAdC/btsB3diIkV0/sR6rdxVCNK8p5mxGdw4JA0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dloAdC/btsB3diIkV0/sR6rdxVCNK8p5mxGdw4JA0/img.png&quot; data-alt=&quot;ELL format에서 memory coalescing&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dloAdC/btsB3diIkV0/sR6rdxVCNK8p5mxGdw4JA0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdloAdC%2FbtsB3diIkV0%2FsR6rdxVCNK8p5mxGdw4JA0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;530&quot; height=&quot;252&quot; data-origin-width=&quot;962&quot; data-origin-height=&quot;457&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;ELL format에서 memory coalescing&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Parallel SpMV/ELL Kernel&lt;/h3&gt;
&lt;pre id=&quot;code_1702743105166&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;__global__ void SpMV_ELL(int num_rows, float* data, int* col_index, int num_elem, float* x, float* y) {
    int row = blockDim.x*blockIdx.x + threadIdx.x;
 
    if (row &amp;lt; num_rows) {
        float dot = 0;
        for (int i = 0; i &amp;lt; num_elem; i++)
            dot += data[row + i*num_rows] * x[col_index[row + i*num_rows]];
        y[row] += dot;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;CSR과 비교하면 입력 parameter가 조금 달라진 것을 볼 수 있다. row_ptr이 사라지고 num_elem이 추가되었는데, 여기서 `num_elem`은 원래 matrix의 모든 row 중 non-zero element가 제일 많은 row의 element 개수이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;padding을 추가했기 때문에 &lt;b&gt;모든 row의 길이가 같아진다&lt;/b&gt;. 때문에 모든 thread들이 &lt;b&gt;for문을 같은 회수만큼 반복&lt;/b&gt;하면 된다. padding의 경우 값이 0이기 때문에 결과에 영향을 미치지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;또한 for문 내에서 각 thread가 `data[row + i*num_rows]`에 접근하기 때문에 &lt;b&gt;memory coalescing&lt;/b&gt;이 일어나므로 memory &lt;b&gt;bandwidth를 더 효율적으로 사용&lt;/b&gt;할 수 있게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;ELL format의 한계&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;예를 들어 1000 by 1000 size의 sparse matrix에서 1%의 element가 0이 아니라고 가정하자. 평균적으로는 각 row에는 10개의 non-zero element가 존재하게 되고, CSR format의 경우에는 사용하는 저장공간의 크기는 전체의 2% 정도이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그러나 row 중 하나가 non-zero element가 200고, 나머지는 매우 적은 경우, 모든 row에 size 200이 되도록 padding을 만들기 때문에 저장공간 압축 효율이 떨어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;즉, &lt;b&gt;non-zero element가 많은 특별한 한 row가 있다면&lt;/b&gt;, CSR의 경우에는 하나의 warp만 오래 실행하지만, ELL은 모든 warp가 오래 실행된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;또한 conversion에 대한 overhead도 존재한다. (큰 문제는 아니다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ELLPACK variation&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;844&quot; data-origin-height=&quot;317&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Jdp1d/btsB5E05VjN/tTkUf3xQdBv1AvckL5UqO1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Jdp1d/btsB5E05VjN/tTkUf3xQdBv1AvckL5UqO1/img.png&quot; data-alt=&quot;ELLPACK-R&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Jdp1d/btsB5E05VjN/tTkUf3xQdBv1AvckL5UqO1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJdp1d%2FbtsB5E05VjN%2FtTkUf3xQdBv1AvckL5UqO1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;471&quot; height=&quot;177&quot; data-origin-width=&quot;844&quot; data-origin-height=&quot;317&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;ELLPACK-R&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;각 row에 non-zero element의 개수를 저장하는 방식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Coordinate Format : COO&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;847&quot; data-origin-height=&quot;402&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/skIAY/btsB7n5qH18/EvIitFsPHGBcZOMi7NkcE1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/skIAY/btsB7n5qH18/EvIitFsPHGBcZOMi7NkcE1/img.png&quot; data-alt=&quot;coordinate format&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/skIAY/btsB7n5qH18/EvIitFsPHGBcZOMi7NkcE1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FskIAY%2FbtsB7n5qH18%2FEvIitFsPHGBcZOMi7NkcE1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;537&quot; height=&quot;255&quot; data-origin-width=&quot;847&quot; data-origin-height=&quot;402&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;coordinate format&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;명시적으로 &lt;b&gt;non-zero element에 대해 row/column index를 나열하는 방식&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 경우, data[i]와 row_index[i]와 col_index[i]가 같이 움직이기만 한다면, 순서를 변경해도 정보가 유지되기 때문에 &lt;b&gt;순서를 변경해도 된다&lt;/b&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Sequential SpMV/COO&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;data[i]의 element를 계산할 때는 `y[row_index[i]] += data[i] * x[col_index[i]]`만 실행해도 되고, 모든 data에 대해 이 연산을 수행하면 처리되는 순서에 상관없이 결과를 얻을 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1702743781316&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;for (int i=0; i &amp;lt; num_elem; i++) {
    y[row_index[i]] += data[i] * x[col_index[i]];
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;COO Kernel Design&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rbXmY/btsB7fzsOIM/6Vwp6MEddGn3KpU3VZ5IA1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rbXmY/btsB7fzsOIM/6Vwp6MEddGn3KpU3VZ5IA1/img.png&quot; data-origin-width=&quot;1001&quot; data-origin-height=&quot;527&quot; data-is-animation=&quot;false&quot; width=&quot;552&quot; height=&quot;291&quot; style=&quot;width: 51.818%; margin-right: 10px;&quot; data-widthpercent=&quot;52.43&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rbXmY/btsB7fzsOIM/6Vwp6MEddGn3KpU3VZ5IA1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrbXmY%2FbtsB7fzsOIM%2F6Vwp6MEddGn3KpU3VZ5IA1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1001&quot; height=&quot;527&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RpDLz/btsB3cD4IcT/q0QyTAAKMX00iyKvsJbjd0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RpDLz/btsB3cD4IcT/q0QyTAAKMX00iyKvsJbjd0/img.png&quot; data-origin-width=&quot;879&quot; data-origin-height=&quot;510&quot; data-is-animation=&quot;false&quot; style=&quot;width: 47.0192%;&quot; data-widthpercent=&quot;47.57&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RpDLz/btsB3cD4IcT/q0QyTAAKMX00iyKvsJbjd0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRpDLz%2FbtsB3cD4IcT%2Fq0QyTAAKMX00iyKvsJbjd0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;879&quot; height=&quot;510&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;COO kernel design&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;때문에 각 thread가 data의 특정 section을 담당하고 `y[row_index[i]] += data[i] * x[col_index[i]]` 연산을 수행한다. 이후 각 thread는 row_index[i]를 사용해서 output Y에 atomic operation을 사용해서 값을 누적하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Parallel SpMV/COO Kernel&lt;/h3&gt;
&lt;pre id=&quot;code_1702744342081&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;__global__ void SpMV_COO(float* data, int* col_index, int* row_index, int num_elem, float* x, float* y) {
    int i = blockDim.x*blockIdx.x + threadIdx.x;
 
    if (i &amp;lt; num_elem) {
        float dot = 0.0f;
        dot = data[i] * x[col_index[i]];
        atomicAdd(&amp;amp;y[row_index[i]], dot);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;design과 같은 방식으로 실행하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Hybrid Format&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;b&gt;ELL을 typical entry에 대해&lt;/b&gt;, &lt;b&gt;COO를 exceptional entry에 대해&lt;/b&gt;&amp;nbsp;처리하는 방법이다. ELL을 적용할 때 non-zero element가 매우 많은 일부 row에 대해서만 COO를 적용하고 나머지를 ELL을 적용하는 것이다. 이를 통해 &lt;b&gt;padding의 개수를 많이 줄일 수 있고&lt;/b&gt;, 나머지는 SpMV/COO를 사용해서 계산하는 방식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;예시&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1073&quot; data-origin-height=&quot;480&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dpTa2M/btsB3eu9ZH3/Fea5gtQUz97p5q5MTyX2q1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dpTa2M/btsB3eu9ZH3/Fea5gtQUz97p5q5MTyX2q1/img.png&quot; data-alt=&quot;예시 : Hybrid format&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dpTa2M/btsB3eu9ZH3/Fea5gtQUz97p5q5MTyX2q1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdpTa2M%2FbtsB3eu9ZH3%2FFea5gtQUz97p5q5MTyX2q1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;578&quot; height=&quot;259&quot; data-origin-width=&quot;1073&quot; data-origin-height=&quot;480&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;예시 : Hybrid format&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위 예시는 hybrid format의 예시를 보여준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;[2, 3]에 해당하는 element 하나만 COO를 적용하고, 나머지를 ELL을 적용한 모습이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;CSR Runtime&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;block performance는 non-zero element가 가장 많은 row에 depend한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Jagged Diagonal Sparse : JDS&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp;load balancing&lt;/b&gt;을 위해 JDS를 구성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;JDS Kernel Design&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eplvGQ/btsCavuQk94/5HSCnMbyedKKeeQP6yUvK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eplvGQ/btsCavuQk94/5HSCnMbyedKKeeQP6yUvK1/img.png&quot; data-origin-width=&quot;683&quot; data-origin-height=&quot;301&quot; data-is-animation=&quot;false&quot; width=&quot;465&quot; height=&quot;205&quot; style=&quot;width: 51.4646%; margin-right: 10px;&quot; data-widthpercent=&quot;52.07&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eplvGQ/btsCavuQk94/5HSCnMbyedKKeeQP6yUvK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeplvGQ%2FbtsCavuQk94%2F5HSCnMbyedKKeeQP6yUvK1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;683&quot; height=&quot;301&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kTpio/btsB83SPeZC/oMyZ86ke3KR9cpLwMOe4z0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kTpio/btsB83SPeZC/oMyZ86ke3KR9cpLwMOe4z0/img.png&quot; data-origin-width=&quot;683&quot; data-origin-height=&quot;327&quot; data-is-animation=&quot;false&quot; style=&quot;width: 47.3726%;&quot; data-widthpercent=&quot;47.93&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kTpio/btsB83SPeZC/oMyZ86ke3KR9cpLwMOe4z0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkTpio%2FbtsB83SPeZC%2FoMyZ86ke3KR9cpLwMOe4z0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;683&quot; height=&quot;327&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;JDS kernel design&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;b&gt;각 row의 non-zero element의 개수로 내림차순 정렬&lt;/b&gt;한다. 여기에 output vector를 올바르게 계산하기 위해 &lt;b&gt;original row index를 추가로&lt;/b&gt; 가지고 있어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;예시&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;726&quot; data-origin-height=&quot;405&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wslfR/btsCbb38kcX/o7eMMyBqG4aBp8QCMAMcr1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wslfR/btsCbb38kcX/o7eMMyBqG4aBp8QCMAMcr1/img.png&quot; data-alt=&quot;예시 : JDS&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wslfR/btsCbb38kcX/o7eMMyBqG4aBp8QCMAMcr1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwslfR%2FbtsCbb38kcX%2Fo7eMMyBqG4aBp8QCMAMcr1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;405&quot; height=&quot;226&quot; data-origin-width=&quot;726&quot; data-origin-height=&quot;405&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;예시 : JDS&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;CSR to JDS conversion&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bZa89C/btsB7OVUTPy/PLQ1LvAXnGSTBs4TUW73M0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bZa89C/btsB7OVUTPy/PLQ1LvAXnGSTBs4TUW73M0/img.png&quot; data-origin-width=&quot;868&quot; data-origin-height=&quot;458&quot; data-is-animation=&quot;false&quot; width=&quot;521&quot; height=&quot;275&quot; style=&quot;width: 49.8155%; margin-right: 10px;&quot; data-widthpercent=&quot;50.4&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bZa89C/btsB7OVUTPy/PLQ1LvAXnGSTBs4TUW73M0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbZa89C%2FbtsB7OVUTPy%2FPLQ1LvAXnGSTBs4TUW73M0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;868&quot; height=&quot;458&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/czzb4Z/btsB2al9HLC/gUZeh5ACUdVed7pz50MVOk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/czzb4Z/btsB2al9HLC/gUZeh5ACUdVed7pz50MVOk/img.png&quot; data-origin-width=&quot;746&quot; data-origin-height=&quot;400&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.0217%;&quot; data-widthpercent=&quot;49.6&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/czzb4Z/btsB2al9HLC/gUZeh5ACUdVed7pz50MVOk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fczzb4Z%2FbtsB2al9HLC%2FgUZeh5ACUdVed7pz50MVOk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;746&quot; height=&quot;400&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;예시 : CSR to JDS&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;b&gt;data[], col_index[]는 순서만&lt;/b&gt; 바뀌지만, &lt;b&gt;row_ptr[]도 정렬 결과에 따라&lt;/b&gt; 바뀌어야 하고, &lt;b&gt;original row index를 추가적으로 관리&lt;/b&gt;해 주어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;CSR과 같은 방식으로 해석하면 되지만, original row index를 나타내는 `jds_row_index`만 추가되었다고 보면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Parallel SpMV/JDS Kernel&lt;/h3&gt;
&lt;pre id=&quot;code_1702744646580&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;__global__ void SpMV_JDS(int num_rows, float *data,
                         int *col_index, int *jds_row_ptr, int jds_row_index,
                         float *x, float *y) {
    int row = blockDim.x * blockIdx.x + threadIdx.x;
    if (row &amp;lt; num_rows) {
        float dot = 0;
        int row_start = jds_row_ptr[row];
        int row_end = jds_row_ptr[row + 1];
        for (int elem = row_start; elem &amp;lt; row_end; elem++) {
            dot += data[elem] * x[col_index[elem]];
        }
        y[jds_row_index[row]] = dot;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;CSR kernel과 대부분이 같지만, `y[jds_row_index[row]] = dot` 부분만 바뀌었다. &lt;b&gt;original row index에 해당하는 위치에 값을 더해주어야 하기 때문&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;JDS vs CSR control divergence&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;JDS kernel의 for문에서 thread는 &lt;b&gt;여전히 iteration 회수가 다르다&lt;/b&gt;. 그러나 sort했기 때문에 인접한 thread들의 iteration 회수가 비슷하다. 때문에 CSR보다는 thread utilization이 좀 더 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;JDS vs CSR memory divergence&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그러나 여전히 &lt;b&gt;memory coalesce가 발생하지 않는다는 문제&lt;/b&gt;가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;JDS with Transposition&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;706&quot; data-origin-height=&quot;444&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7nF1S/btsB7UBTAFY/qRqDhdOLKrN9BaeqVU5FL0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7nF1S/btsB7UBTAFY/qRqDhdOLKrN9BaeqVU5FL0/img.png&quot; data-alt=&quot;JDS transposoe&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7nF1S/btsB7UBTAFY/qRqDhdOLKrN9BaeqVU5FL0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7nF1S%2FbtsB7UBTAFY%2FqRqDhdOLKrN9BaeqVU5FL0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;413&quot; height=&quot;260&quot; data-origin-width=&quot;706&quot; data-origin-height=&quot;444&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;JDS transposoe&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;CSR 대신 ELL format을 적용했을 때를 생각해 보면, ELL은 transpose했기 때문에 memory coalesce를 할 수 있었다. 같은 방식으로 &lt;b&gt;JDS도 transpose해서 memory coalesce&lt;/b&gt;를 노려볼 수 있을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;예시 : JDS transposition&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wslfR/btsCbb38kcX/o7eMMyBqG4aBp8QCMAMcr1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wslfR/btsCbb38kcX/o7eMMyBqG4aBp8QCMAMcr1/img.png&quot; width=&quot;405&quot; height=&quot;226&quot; data-origin-width=&quot;726&quot; data-origin-height=&quot;405&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.0205%; margin-right: 10px;&quot; data-widthpercent=&quot;49.6&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wslfR/btsCbb38kcX/o7eMMyBqG4aBp8QCMAMcr1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwslfR%2FbtsCbb38kcX%2Fo7eMMyBqG4aBp8QCMAMcr1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;726&quot; height=&quot;405&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b5im4R/btsCbfrVRoT/ZIJT3Tj5tkvJT60P1kDTEk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b5im4R/btsCbfrVRoT/ZIJT3Tj5tkvJT60P1kDTEk/img.png&quot; data-origin-width=&quot;705&quot; data-origin-height=&quot;387&quot; data-is-animation=&quot;false&quot; width=&quot;457&quot; height=&quot;251&quot; data-widthpercent=&quot;50.4&quot; style=&quot;width: 49.8167%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b5im4R/btsCbfrVRoT/ZIJT3Tj5tkvJT60P1kDTEk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb5im4R%2FbtsCbfrVRoT%2FZIJT3Tj5tkvJT60P1kDTEk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;705&quot; height=&quot;387&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;예시 : JDS-transposed&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;transpose 이후 JDS format&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;919&quot; data-origin-height=&quot;504&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tx8mx/btsB83kZNYk/dkwP0VeJ2f04GLKTpOKxG1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tx8mx/btsB83kZNYk/dkwP0VeJ2f04GLKTpOKxG1/img.png&quot; data-alt=&quot;transposed JDS format&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tx8mx/btsB83kZNYk/dkwP0VeJ2f04GLKTpOKxG1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Ftx8mx%2FbtsB83kZNYk%2FdkwP0VeJ2f04GLKTpOKxG1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;556&quot; height=&quot;305&quot; data-origin-width=&quot;919&quot; data-origin-height=&quot;504&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;transposed JDS format&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;transpose하기 때문에 data[]와 col_index[]를 수정하고, jds_row_ptr 대신 &lt;b&gt;jds_t_col_ptr를 사용&lt;/b&gt;해야 한다. jds_row_index는 original row index를 나타내는 것이므로 그대로 남겨둬야 할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;JDS : memory coalescing&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgxmB5/btsB7jBRpfj/kLRkw68IeZe5Y4kKew370k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgxmB5/btsB7jBRpfj/kLRkw68IeZe5Y4kKew370k/img.png&quot; data-origin-width=&quot;735&quot; data-origin-height=&quot;452&quot; data-is-animation=&quot;false&quot; width=&quot;408&quot; height=&quot;251&quot; style=&quot;width: 49.4396%; margin-right: 10px;&quot; data-widthpercent=&quot;50.02&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgxmB5/btsB7jBRpfj/kLRkw68IeZe5Y4kKew370k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbgxmB5%2FbtsB7jBRpfj%2FkLRkw68IeZe5Y4kKew370k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;735&quot; height=&quot;452&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vBh79/btsB3uEsZmp/RNwYz94lQw1whBDC4zWurK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vBh79/btsB3uEsZmp/RNwYz94lQw1whBDC4zWurK/img.png&quot; data-origin-width=&quot;736&quot; data-origin-height=&quot;453&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.3976%;&quot; data-widthpercent=&quot;49.98&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vBh79/btsB3uEsZmp/RNwYz94lQw1whBDC4zWurK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvBh79%2FbtsB3uEsZmp%2FRNwYz94lQw1whBDC4zWurK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;736&quot; height=&quot;453&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;transposed JDS memory coalescing&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;transpose하면 ELL과 같은 이유로 memory coalesce가 일어나기 때문에 더 효율적이게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;언제 뭘 써야 할까?&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;무작위한 경우 : ELL&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;510&quot; data-origin-height=&quot;527&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/r55OQ/btsB4RNdPPm/7l4YsDRZOH1vOc7IXk0VBK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/r55OQ/btsB4RNdPPm/7l4YsDRZOH1vOc7IXk0VBK/img.png&quot; data-alt=&quot;무작위한 경우&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/r55OQ/btsB4RNdPPm/7l4YsDRZOH1vOc7IXk0VBK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fr55OQ%2FbtsB4RNdPPm%2F7l4YsDRZOH1vOc7IXk0VBK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;221&quot; height=&quot;228&quot; data-origin-width=&quot;510&quot; data-origin-height=&quot;527&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;무작위한 경우&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;ELL이 제일 좋을 것이다. random하기 때문에 &lt;b&gt;non-zero element도 균일하게 분포&lt;/b&gt;할 것이기 때문에 padding으로 인한 space overhead가 적을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;row의 편차가 큰 경우 : ELL/COO&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;518&quot; data-origin-height=&quot;517&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eey2ll/btsCawgeBaJ/b3SJawbl099qZGO84fxQO0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eey2ll/btsCawgeBaJ/b3SJawbl099qZGO84fxQO0/img.png&quot; data-alt=&quot;row의 편차가 큰 경우&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eey2ll/btsCawgeBaJ/b3SJawbl099qZGO84fxQO0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Feey2ll%2FbtsCawgeBaJ%2Fb3SJawbl099qZGO84fxQO0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;282&quot; height=&quot;281&quot; data-origin-width=&quot;518&quot; data-origin-height=&quot;517&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;row의 편차가 큰 경우&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;ELL/COO &lt;b&gt;hybrid&lt;/b&gt; 방식이 제일 효율적일 것이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;대부분 ELL 방식이 효율적이기 때문이다. &lt;b&gt;특히 긴 row들에 대해서는 COO로 처리&lt;/b&gt;하고, &lt;b&gt;나머지는 ELL 방식&lt;/b&gt;으로 처리하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;매우 sparse한 경우 : COO&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;510&quot; data-origin-height=&quot;512&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cKdYve/btsB3dCYYxO/nNVZo2HmXWSt3VQUzM5jTk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cKdYve/btsB3dCYYxO/nNVZo2HmXWSt3VQUzM5jTk/img.png&quot; data-alt=&quot;매우 sparse한 경우&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cKdYve/btsB3dCYYxO/nNVZo2HmXWSt3VQUzM5jTk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcKdYve%2FbtsB3dCYYxO%2FnNVZo2HmXWSt3VQUzM5jTk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;267&quot; height=&quot;268&quot; data-origin-width=&quot;510&quot; data-origin-height=&quot;512&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;매우 sparse한 경우&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;COO가 제일 좋은 방식일 것이다. &lt;b&gt;space overhead가 제일 적기 때문&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;삼각형인 경우 : JDS&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;519&quot; data-origin-height=&quot;527&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JQMFF/btsB5Yk3Ilb/ZzzFWyHKKH9Xe53GO1EBMk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JQMFF/btsB5Yk3Ilb/ZzzFWyHKKH9Xe53GO1EBMk/img.png&quot; data-alt=&quot;삼각형인 경우&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JQMFF/btsB5Yk3Ilb/ZzzFWyHKKH9Xe53GO1EBMk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJQMFF%2FbtsB5Yk3Ilb%2FZzzFWyHKKH9Xe53GO1EBMk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;254&quot; height=&quot;258&quot; data-origin-width=&quot;519&quot; data-origin-height=&quot;527&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;삼각형인 경우&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;JDS가 제일 좋을 것이다. &lt;b&gt;sparsity structure의 장점을 가져올 수 있기 때문&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;banded matrix : ELL&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;513&quot; data-origin-height=&quot;509&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HmMgw/btsB5ALbBak/AD27G5QTayDhUDzoIOw4Z1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HmMgw/btsB5ALbBak/AD27G5QTayDhUDzoIOw4Z1/img.png&quot; data-alt=&quot;banded matrix인 경우&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HmMgw/btsB5ALbBak/AD27G5QTayDhUDzoIOw4Z1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHmMgw%2FbtsB5ALbBak%2FAD27G5QTayDhUDzoIOw4Z1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;246&quot; height=&quot;244&quot; data-origin-width=&quot;513&quot; data-origin-height=&quot;509&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;banded matrix인 경우&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;ELL이 제일 효율적일 것이다. &lt;b&gt;각 row의 편차가 적기 때문&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;이외 다른 format들&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Diagonal(DIA) : strictly banded/diagonal인 경우 좋다.&amp;nbsp;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;dense diagonal vector의 sparse set만 저장하고, 각 diagonal에 대해 main diagonal과의 offset을 저장하는 방식이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Packet(PTK) : row/col 재배열로 diagonal submatrix 구성
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인접한 row access가 인접한 element에 접근하기 때문에 cache performance가 좋다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Dictionary of Keys(DOK) : data의 row/col mapping 저장
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;sparse matrix를 만들거나 query할 때 좋다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Compressed Sparse Column(CSC)&lt;/li&gt;
&lt;li&gt;Blocked CSR : block-sparse matrix에 유용하다.&lt;/li&gt;
&lt;li&gt;이외, 이것들의 조합&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Appendix: 고급 알고리즘을 위한 sparse matrix&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;graph를 sparse adjacency matrix로 표현하기도 한다.&lt;/li&gt;
&lt;li&gt;binning technique(압축)는 data compaction을 위해 sparse matrix로 표현하기도 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;결론&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SpMV는 어렵다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;다른 표현 방식&lt;/b&gt;은 &lt;b&gt;다른 storage requirement&lt;/b&gt;를 가진다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;storage requirement는&lt;/b&gt; &lt;b&gt;sparsity pattern에 따라 다르다&lt;/b&gt;.&lt;/li&gt;
&lt;li&gt;regularity와 efficiency는 tradeoff가 있다.&lt;/li&gt;
&lt;li&gt;몇몇 표현 방식은 제일 좋은 처리 방식이 있다.&lt;/li&gt;
&lt;li&gt;높은 compute-to-communication ratio를 얻기 힘들다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;잘못된 내용이나 오탈자에 대한 지적, 질문&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;b&gt;등은 언제나 환영합니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>CS/Parallel Computing</category>
      <author>hyelie</author>
      <guid isPermaLink="true">https://hyelie.tistory.com/683</guid>
      <comments>https://hyelie.tistory.com/entry/%EC%9D%B4%EC%A2%85%EB%B3%91%EB%A0%AC%EC%BB%B4%ED%93%A8%ED%8C%85-Parallel-Patterns-Sparse-Computation#entry683comment</comments>
      <pubDate>Sun, 17 Dec 2023 02:29:38 +0900</pubDate>
    </item>
    <item>
      <title>[이종병렬컴퓨팅] Parallel Patterns : Scan</title>
      <link>https://hyelie.tistory.com/entry/%EC%9D%B4%EC%A2%85%EB%B3%91%EB%A0%AC%EC%BB%B4%ED%93%A8%ED%8C%85-Parallel-Patterns-Reduction-1</link>
      <description>&lt;blockquote style=&quot;text-align: left;&quot; data-ke-style=&quot;style2&quot;&gt;이 글은 포스텍 성효진 교수님의 이종병렬컴퓨팅(CSED490C) 강의를 기반으로 재구성한 것입니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 글에서는 parallel scan (prefix sum)과 koggle-stone algorithm(work-inefficient)와 brent-kung(work-efficient) algorithm을 살펴본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Scan&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Inclusive Scan&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;어떤 binary associative operator &amp;oplus;와 array [x$_0$, x$_1$, ... , x$_{n-1}$]에 대해, [x$_0$, x$_0$ &amp;oplus; x$_1$, ... , (x$_0$ &amp;oplus; x$_1$ &amp;oplus; ... &amp;oplus; x$_{n-1}$]을 리턴하는 것이 inclusive scan이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;scan은 radix sort, quick sort, 등등 다양한 병렬 알고리즘에 사용되며, &lt;span style=&quot;text-align: start;&quot;&gt;그냥 prefix sum이라고 생각하면 된다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;일반적으로 parallel한 버전을 만들 때, 각 thread는 출력 값을 어디에 쓸 지 알아야 한다. scan의 경우 각 thread는 다른 thread가 쓰는 값에 의존하기 때문에 이를 고려해서 효율적인 병렬 알고리즘을 만들어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;exclusive scan&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;어떤 binary associative operator &amp;oplus;와 array [x$_0$, x$_1$, ... , x$_{n-1}$]에 대해, [, x$_0$, ... , (x$_0$ &amp;oplus; x$_1$ &amp;oplus; ... &amp;oplus; x$_{n-2}$]을 리턴하는 것이 exclusive scan이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;inclusive scan과 조금 다르다. inclusive scan은 i번째 결과값이 i번째 element의 연산을 포함하는데, exclusive scan은 i번째 결과값이 i번째 element와의 연산을 포함하지 않는다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예를 들어 inclusive scan은 처음 값이 0이 아니라 arr[0]인데, exclusive scan은 0부터 시작한다. 끝 값도 조금 다르다. inclusive scan의 arr[n-1]은 모든 배열의 합인데, exclusive scan의 arr[n-1]은 모든 배열의 합 - arr[n-1]이다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;exclusive scan을 사용하는 이유는 할당된 buffer의 시작 주소를 찾을 때와 같은 상황에서 사용한다. inclusive scan이나 exclusive scan은 변환하기가 매우 쉽다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Inclusive Sequential Scan&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;input [x0, x1, x2, ...]에 대해 output [y0, y1, y2, ...]를 계산한다고 했을 때,&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;y$_0$ = x$_0$&lt;/li&gt;
&lt;li&gt;y$_1$ = x$_0$ + x$_1$&lt;/li&gt;
&lt;li&gt;y$_2$ = x$_0$ + x$_1$ + x$_2$&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;li&gt;즉, y$_i$ = y$_{i-1}$ + x$_i$&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;따라서 prefix sum의 sequential 버전은 다음과 같다. 이 경우 시간복잡도는 O(n)이다.&lt;/p&gt;
&lt;pre id=&quot;code_1702733209531&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;y[0] = x[0]
for(int i = 1; i &amp;lt; len; i++){
    y[i] = y[i-1] + x[i];
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Parallel Inclusive Scan&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;제일 쉬운 버전은, y element 하나를 계산하기 위해 필요한 모든 x 값들을 다 더하면 된다. 성능을 신경쓰지 않는다면 병렬 구성 자체는 쉽다. 그러나 이는 O(n$^2$)의 연산이 필요하기 때문에 다른 방법을 살펴본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Kogge-Stone Parallel Scan Algortihm&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;각 output element를 이전 element의 reduction으로 계산하는 방식이다. 이 때 이전에 계산했던 reduction partial sum은 output element를 계산할 때 사용된다. kogge-stone tree를 기반으로 한 계산 방식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;global memory에서 size n의 배열 T를 shared memory로 load한다. 이 때 n은 2의 k승이라고 하자.&lt;/li&gt;
&lt;li&gt;pass를 logn번 반복하며, 각 pass에서 &lt;b&gt;stride를 1부터 n/2까지 증가&lt;/b&gt;시킨다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 때 각 pass에서는 &lt;b&gt;stride부터 n-1개의 thread가 active&lt;/b&gt;하다.&amp;nbsp;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예를 들면 pass 0에서는 stride가 1이다. 1부터 n-1까지의 thread가 active하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;이후 &lt;b&gt;간격이 stride인 pair element를 더한다&lt;/b&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;shared memory의 결과로부터 global memory로 값을 쓴다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;643&quot; data-origin-height=&quot;475&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bDayZA/btsB7nEi0b2/NfaIHNTYpTJUPwAxUK0cF1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bDayZA/btsB7nEi0b2/NfaIHNTYpTJUPwAxUK0cF1/img.png&quot; data-alt=&quot;kogge-stone parallel scan algorithm&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bDayZA/btsB7nEi0b2/NfaIHNTYpTJUPwAxUK0cF1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbDayZA%2FbtsB7nEi0b2%2FNfaIHNTYpTJUPwAxUK0cF1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;399&quot; height=&quot;295&quot; data-origin-width=&quot;643&quot; data-origin-height=&quot;475&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;kogge-stone parallel scan algorithm&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위 예시는 size 8일 때 3번의 pass를 보여준다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;pass 1에서는 stride가 1이므로 인접한 값을 더한다.&lt;/li&gt;
&lt;li&gt;pass 2에서는 stride가 2이므로 2칸 옆에 있는 값을 더한다.&lt;/li&gt;
&lt;li&gt;pass 3에서는 stirde가 4이므로 4칸 옆에 있는 값을 더한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;예시를 잘 보면 알겠지만, 최종 결과물에서 i번째 값은 0 ~ i번째 값이 모두 더해지는 것을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;CUDA kernel&lt;/h4&gt;
&lt;pre id=&quot;code_1702733761896&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;__global__ void koggeStoneScan(float* X, float* Y, int n) {
    __shared__ float XY[SECTION_SIZE];
    int i = blockIdx.x*blockDim.x + threadIdx.x;
    
    XY[threadIdx.x] = (i &amp;lt; n) ? X[i] : 0;

    float temp = 0.f;
    for (unsigned int stride = 1; stride &amp;lt; blockDim.x; stride *= 2) {
        __syncthreads(); // __syncthread();
        if (threadIdx.x &amp;gt;= stride) {
            temp = XY[threadIdx.x] + XY[threadIdx.x - stride];
        }
        __syncthreads(); // __syncthread();
        if (threadIdx.x &amp;gt;= stride)
            XY[threadIdx.x] = temp;
    }
    
    if (i &amp;lt; n)
        Y[i] = XY[threadIdx.x];
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 코드에서 temp 값을 사용하는 이유는, &lt;b&gt;shared memory에 바로 값을 쓰면 input 값이 바뀌기 때문&lt;/b&gt;이다. 때문에 temp에 값을 쓰고 이후에 다시 shared memory에 값을 쓴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;`&lt;/span&gt;__syncthread()`가 2군데에 있는데, 2번째에 있는 `__syncthread()`를 삭제하면 다른 thread가 같은 위치에 대해 동시에 쓸 수 있기 때문에 필요하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Double Buffering&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위 코드에서는 2개의 bariier를 사용했는데, 이를 해결하기 위해 double buffering을 사용한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;T0와 T1의 사본을 사용한다.&lt;/li&gt;
&lt;li&gt;T0을 input으로, T1을 output으로 사용한다.&lt;/li&gt;
&lt;li&gt;이후 각 pass에서 input/output의 역할을 바꾼다. 예를 들어 iteration 0에서는 T0가 input, T1이 output, iteration 2에서는 T1이 input, T0가 output이 되는 방식이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;일반적으로 2개의 pointer를 사용해 source와 destination을 swap()하는 방식으로 사용한다. 이를 통해 위 코드에서 2번째 barrier를 없앨 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Double-Buffered Kogge-Stone Parallel Scan&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dPGSCn/btsB6l74PlB/f5PP714C8Of9fg1c6wAg80/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dPGSCn/btsB6l74PlB/f5PP714C8Of9fg1c6wAg80/img.png&quot; data-origin-width=&quot;821&quot; data-origin-height=&quot;179&quot; data-is-animation=&quot;false&quot; style=&quot;width: 72.5756%; margin-right: 10px;&quot; data-widthpercent=&quot;73.43&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dPGSCn/btsB6l74PlB/f5PP714C8Of9fg1c6wAg80/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdPGSCn%2FbtsB6l74PlB%2Ff5PP714C8Of9fg1c6wAg80%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;821&quot; height=&quot;179&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Y5gQC/btsB2s8e2yg/mQUWIxXqwKMlEeHKxbAHm0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Y5gQC/btsB2s8e2yg/mQUWIxXqwKMlEeHKxbAHm0/img.png&quot; data-origin-width=&quot;790&quot; data-origin-height=&quot;476&quot; data-is-animation=&quot;false&quot; style=&quot;width: 26.2616%;&quot; data-widthpercent=&quot;26.57&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Y5gQC/btsB2s8e2yg/mQUWIxXqwKMlEeHKxbAHm0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FY5gQC%2FbtsB2s8e2yg%2FmQUWIxXqwKMlEeHKxbAHm0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;790&quot; height=&quot;476&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;double buffered kogge stone parallel scan&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이렇게 하면 오직 하나의 barrier만 사용하고, destination에 값을 쓰면 source의 값이 바뀌지 않음을 보장할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;효율 분석&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 scan 방식은 &lt;b&gt;logn번의 iteration&lt;/b&gt;을 수행한다. 각 iteration은 n-1번, n-2번, n-4번, ... n-n/2번의 add 연산을 수행한다. 따라서 &lt;b&gt;총 add operation의 개수&lt;/b&gt;는 n * logn - (n-1) = &lt;b&gt;O(nlogn)&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;따라서, 이 방식은 &lt;b&gt;work-inefficient한 방식&lt;/b&gt;이다. sequential 방식이 O(n)임을 생각해 보면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;따라서, 낮은 work efficiency로 인해 resource가 가득 찼을 때 parallel 알고리즘이 sequential보다 더 느릴 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Brent-Kung Algorithm&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 방식은 work-efficient이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp;balanced tree 방식의 parallel algorithm pattern을 사용&lt;/b&gt;해 효율을 올린다. input data에 대해 balanced binary tree를 만들고, root부터 tree를 sweeping한다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;이 때 tree는 실제 data structure가 아니라 각 pass에서 thread가 작업을 결정할 때 사용하는 개념이다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;scan의 경우, &lt;b&gt;1) leaf에서 root까지 내려가면서 tree의 internel node들의 partial sum을 구성&lt;/b&gt;한다. 그러면 root는 모든 leaf node들의 합을 가지게 된다. &lt;b&gt;2) 이후 root부터 leaf까지 올라가면서 계산해 둔 partial sum을 사용해 계산하지 않은 scan 값을 계산&lt;/b&gt;하는 방식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;864&quot; data-origin-height=&quot;558&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cx4ehk/btsB8R5YN46/05hVV1mX4IiussMg8qvLKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cx4ehk/btsB8R5YN46/05hVV1mX4IiussMg8qvLKK/img.png&quot; data-alt=&quot;brent kung parallel scan - reduction step&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cx4ehk/btsB8R5YN46/05hVV1mX4IiussMg8qvLKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcx4ehk%2FbtsB8R5YN46%2F05hVV1mX4IiussMg8qvLKK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;640&quot; height=&quot;413&quot; data-origin-width=&quot;864&quot; data-origin-height=&quot;558&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;brent kung parallel scan - reduction step&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위 그림처럼 leaf부터 root까지 올라가면서 internal node들의 partial sum을 계산한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위 예시에서는 x7이 모든 값의 합을 가지고 있게 된다. 반면 다른 값들은 아직 완벽한 값을 가지고 있지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bOWTrh/btsB82zBIGV/TR6gh3oUip5YyZgbBADacK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bOWTrh/btsB82zBIGV/TR6gh3oUip5YyZgbBADacK/img.png&quot; data-origin-width=&quot;740&quot; data-origin-height=&quot;532&quot; data-is-animation=&quot;false&quot; width=&quot;491&quot; height=&quot;353&quot; style=&quot;width: 49.2391%; margin-right: 10px;&quot; data-widthpercent=&quot;49.82&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bOWTrh/btsB82zBIGV/TR6gh3oUip5YyZgbBADacK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbOWTrh%2FbtsB82zBIGV%2FTR6gh3oUip5YyZgbBADacK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;740&quot; height=&quot;532&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IQVBu/btsB7lfpxOC/v8uJThEsrVSN1fPQ7HDcc1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IQVBu/btsB7lfpxOC/v8uJThEsrVSN1fPQ7HDcc1/img.png&quot; data-origin-width=&quot;751&quot; data-origin-height=&quot;536&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.5981%;&quot; data-widthpercent=&quot;50.18&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IQVBu/btsB7lfpxOC/v8uJThEsrVSN1fPQ7HDcc1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIQVBu%2FbtsB7lfpxOC%2Fv8uJThEsrVSN1fPQ7HDcc1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;751&quot; height=&quot;536&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;brent kung parallel scan - post scan step&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;post scan step에서는 계산된 값들의 일부를 이용해 아직 계산되지 않은 위치의 값을 쉽게 계산할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;예를 들어 왼쪽 그림에서 x0부터 x5까지 값을 구하고 싶다면 x0부터 x3까지의 합이 들어 있는 x3과, x4부터 x5까지의 합이 들어 있는 x5를 더하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;같은 방식으로 오른쪽 그림에서 x0부터 x2까지의 합을 구하고 싶다면 x0부터 x1까지의 합이 들어 있는 x1과 x2를 더하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;737&quot; data-origin-height=&quot;541&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4RxUv/btsB8UPaEyA/KwmxQqWSicEUrF3VQtjn10/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4RxUv/btsB8UPaEyA/KwmxQqWSicEUrF3VQtjn10/img.png&quot; data-alt=&quot;brent kung parallel scan - 요약&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4RxUv/btsB8UPaEyA/KwmxQqWSicEUrF3VQtjn10/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4RxUv%2FbtsB8UPaEyA%2FKwmxQqWSicEUrF3VQtjn10%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;586&quot; height=&quot;430&quot; data-origin-width=&quot;737&quot; data-origin-height=&quot;541&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;brent kung parallel scan - 요약&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;즉 위와 같은 방식으로 1) reduction을 통해 partial sum을 구하고 2) 이 값들을 사용해 다시 prefix sum을 계산한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;CUDA Kernel&lt;/h4&gt;
&lt;pre id=&quot;code_1702734708140&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;__global__
void brentKungScan(float* X, float* Y, int n) {
    __shared__ float XY[SECTION_SIZE];
    int i = 2*blockIdx.x*blockDim.x + threadIdx.x;
    if (i &amp;lt; n)
        XY[threadIdx.x] = X[i];
    if (i + blockDim.x &amp;lt; n)
        XY[threadIdx.x + blockDim.x] = X[i + blockDim.x];
    
    // reduction phase
    for (unsigned int stride = 1; stride &amp;lt;= blockDim.x; stride *= 2) {
        __syncthreads();
        int index = ((threadIdx.x + 1) * stride * 2) - 1;
        if (index &amp;lt; SECTION_SIZE) {
            XY[index] += XY[index - stride];
        }
    }
 
    // post scan phase
    for (unsigned int stride = SECTION_SIZE/4; stride &amp;gt; 0; stride /= 2) {
        __syncthreads();
        int index = ((threadIdx.x + 1) * stride * 2) - 1;
        if (index + stride &amp;lt; SECTION_SIZE) {
            XY[index + stride] += XY[index];
        }
    }
 
    __syncthreads();
    if (i &amp;lt; n)
        Y[i] = XY[threadIdx.x];
    if (i + blockDim.x &amp;lt; n)
        Y[i + blockDim.x] = XY[threadIdx.x + blockDim.x];
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;예시를 보자. 예를 들어 input size가 8일 때, reduction step에서, stride가 1이면 threadIdx.x + 1은 1, 2, 3, 4, 5, 6, 7, 8이 된다. 그러면 index는 1, 3, 5, 7, 9, 11, 13, 15가 된다. `XY[index] += XY[index - stride]`를 하므로 reduction이 올바르게 계산된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;한편, 이 때&lt;b&gt; post scan step에서 bank conflict가 발생&lt;/b&gt;할 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;효율 분석&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;parallel inclusive scan은 &lt;b&gt;2 * logn번의 pass&lt;/b&gt;를 실행한다. logn번은 reduction에서, logn번은 post scan에서 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;각 iteratioon에서는 n/2, n/4, ... , 1번, 그리고 1, 2, ... , n/4, n/2번의 add operation을 수행한다. 따라서 &lt;b&gt;add operation은 총 2(n-1)번&lt;/b&gt; 수행하므로 O(n)이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;parallel 버전에서 추가된 add operation의 회수는 sequential 버전의 2배 이하이다. 이 경우, parallel하게 계산하는 경우 2배로 늘어난 연산으로 인한 overhead는 쉽게 극복할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Kogge-Stone vs Brent-Kung&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;b&gt;brent-kung은&lt;/b&gt; kogge-stone과 비교했을 때, &lt;b&gt;절반의 thread&lt;/b&gt;를 사용한다. brent-kung의 경우 각 thread는 2개의 element를 shared memory로 load하기 때문이며, reduction이기 때문에 필요한 thread의 개수가 훨씬 적다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;b&gt;brent-kung은&lt;/b&gt; kogge-stone과 비교했을 때 &lt;b&gt;pass의 개수가 2배&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;즉, brent-kung의 경우 1/2배의 thread, 반면 2배의 pass -&amp;gt; n/2 * 2logn = nlogn이므로, GPU에서 효율성은 비슷하다. 그러나 pass의 개수가 더 많은데, 각 pass의 실행은 이전 pass의 결과에 dependent하기 때문에 더 많은 barrier가 필요하므로, 더 많은 synchronization overhead를 발생시킨다. 때문에 GPU의 block 내부의 parallel scan은 kogge-stone이 더 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;일반화&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;매우 큰 input에서 hierarchical parallel scan&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;775&quot; data-origin-height=&quot;552&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kP471/btsB5CPLkJf/NSVoHwK1l9e7AmvVES0EJK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kP471/btsB5CPLkJf/NSVoHwK1l9e7AmvVES0EJK/img.png&quot; data-alt=&quot;매우 큰 input에서 hierarchical parallel scan&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kP471/btsB5CPLkJf/NSVoHwK1l9e7AmvVES0EJK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkP471%2FbtsB5CPLkJf%2FNSVoHwK1l9e7AmvVES0EJK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;529&quot; height=&quot;377&quot; data-origin-width=&quot;775&quot; data-origin-height=&quot;552&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;매우 큰 input에서 hierarchical parallel scan&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;매우 큰 reduction에 대해, 각각을 section으로 나누고, section의 결과를 다시 reduction 했던 것처럼, scan 또한 같은 방식으로 진행한다. section의 결과를 auxiliary array에 넣고, 그 array를 다시 scan한다. 이후 최종 결과를 얻을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;global memory content 사용하기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;하나의 thread block에 속한 shared memory나 register 값은 다른 thread block에 보이지 않는다. 때문에 data를 visible하게 만들기 위해서는 data가 global memory에 쓰여야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그러나, global memory에 써진 값들은 memory fense로 인해 보이지 않는데, kernel 실행이 종료되었을 때 fense가 사라진다.&amp;nbsp;따라서 &lt;b&gt;한 kernel의 실행이 끝났을 때 다른 kernel을 실행&lt;/b&gt;해야 한다. 그래야만 종료된 kernel이 global memory에 쓴 값이 다른 thread block에서 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;임의 길이 input에 대해 작업하기&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;`2 * blockDim.x`개의 element를 처리할 수 있는 scan kernel을 만든다. kogge-stone의 경우, 각 section이 blockDim.x개의 element가 하나의 block에 할당되게 만든다.&lt;/li&gt;
&lt;li&gt;각 block은 sum[blockIdx.x]에 값을 쓴다.&lt;/li&gt;
&lt;li&gt;sum array에 대해 parallel scan을 다시 실행한다. 만약 block size보다 sum 배열의 크기가 훨씬 크다면 sum을 나눠야 할 것이다.&lt;/li&gt;
&lt;li&gt;scan된 sum 배열의 값을 해당 section의 element에 더한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;CUDA kernel : exclusive scan&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;kogge-stone kernel의 경우,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;b&gt;thread block 0에서 thread 0&lt;/b&gt;은 shared memory에 값을 올릴 때 `&lt;span style=&quot;text-align: start;&quot;&gt;arr[0]`이 아니라 `&lt;b&gt;0`을&lt;/b&gt;&lt;span&gt;&lt;b&gt; 올리게&lt;/b&gt; 한다. 다른 모든 thread들은 `X[threadIdx.x - 1]`을 `XY[threadIdx.x]`에 쓴다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;다른 모든 thread block들은 &lt;b&gt;`X[(blockIdx.x * blockDim.x) + threadIdx.x - 1]`을 `XY[threadIdx.x]`에 쓴다.&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;brent-kung의 경우도 매우 유사하지만, 각 thread가 2개의 element를 load할 수 있어야 한다. &lt;b&gt;제일 앞의 0이 load&lt;/b&gt;되어야 하고, &lt;b&gt;다른 모든 element들은 단 한 칸만 shift&lt;/b&gt;되어야 한다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;잘못된 내용이나 오탈자에 대한 지적, 질문&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;b&gt;등은 언제나 환영합니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>CS/Parallel Computing</category>
      <author>hyelie</author>
      <guid isPermaLink="true">https://hyelie.tistory.com/682</guid>
      <comments>https://hyelie.tistory.com/entry/%EC%9D%B4%EC%A2%85%EB%B3%91%EB%A0%AC%EC%BB%B4%ED%93%A8%ED%8C%85-Parallel-Patterns-Reduction-1#entry682comment</comments>
      <pubDate>Sun, 17 Dec 2023 00:14:43 +0900</pubDate>
    </item>
    <item>
      <title>[이종병렬컴퓨팅] Parallel Patterns : Reduction</title>
      <link>https://hyelie.tistory.com/entry/%EC%9D%B4%EC%A2%85%EB%B3%91%EB%A0%AC%EC%BB%B4%ED%93%A8%ED%8C%85-Parallel-Patterns-Reduction</link>
      <description>&lt;blockquote style=&quot;text-align: left;&quot; data-ke-style=&quot;style2&quot;&gt;이 글은 포스텍 성효진 교수님의 이종병렬컴퓨팅(CSED490C) 강의를 기반으로 재구성한 것입니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 글에서는 parallel reduction pattern을 살펴본다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;parallel reduction pattern은 제일 많이 사용되는 패턴 중 하나이다. 추가로, control divergence와 thread utilization 등의 작업 효율성, shared memory bank conflict 등의 resource 효율성을 살펴본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Reduction&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Reduction은 &lt;b&gt;input value를 하나의 값으로 요약하는 연산&lt;/b&gt;이다. 예를 들어 max(), min(), sum() 등등이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;기본적으로 &lt;b&gt;associative&lt;/b&gt;(결합), &lt;b&gt;commutative&lt;/b&gt;(교환)여야 하며, 잘 정의된&lt;b&gt; identity value&lt;/b&gt;가 있어야 한다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;identity value는 항등원을 의미한다. sum의 경우 0, product의 경우 1, 등등이다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Partition and Summarize&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;큰 input data set을 처리할 때 자주 사용되는 전략이다. 순서는 다음과 같다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;data set의 element를 처리할 때 특별한 순서가 없으므로, input data set을 작은 chunk로 분할한다.&lt;/li&gt;
&lt;li&gt;이후 각 thread가 chunk를 담당한다.&lt;/li&gt;
&lt;li&gt;각 chunk로부터 reduction 결과를 tree를 만들어 최종 결과를 만든다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Reduction은 다른 기술들도 가능하게 만든다.&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;몇몇 연산의 경우, 병렬 변환 이후 reduction이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;예를 들어 지난 포스팅에서 살펴봤던 histogram의 privatization의 경우, 각 thread block에서 private copy를 만들었다. 이후 private copy를 사용해 final copy를 만들 때 reduction tree를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Sequential Reduction&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;sequential reduction은 다음과 같이 실행한다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;reduction operation의 초기값을 identity value로 설정한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예를 들어 max의 경우 제일 작은 값을(INT_MIN), min의 경우 제일 큰 값을(INT_MAX), sum의 경우 0, product의 경우 1로 설정하는 것이 그것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;input을 순회하면서 n개의 입력에 대해 n번 reduction 연산을 수행한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 경우 각 element는 오직 1번만 방문하므로 &lt;b&gt;O(n) 알고리즘&lt;/b&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Parallel Reduction Tree&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;598&quot; data-origin-height=&quot;480&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bKg7ig/btsB6xtodBs/K4gdMl7TOli5ThKjJwJRuK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bKg7ig/btsB6xtodBs/K4gdMl7TOli5ThKjJwJRuK/img.png&quot; data-alt=&quot;parallel reduction tree&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bKg7ig/btsB6xtodBs/K4gdMl7TOli5ThKjJwJRuK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbKg7ig%2FbtsB6xtodBs%2FK4gdMl7TOli5ThKjJwJRuK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;370&quot; height=&quot;297&quot; data-origin-width=&quot;598&quot; data-origin-height=&quot;480&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;parallel reduction tree&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;반면 parallel reduction tree는 각 &lt;b&gt;reduction의 결과를 tree로 만들어 합치는 방법&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Reduction Tree 알고리즘 분석&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;n개의 입력 값이 있을 때, binary reduction을 사용하는 reduction tree의 경우, 첫 번째 pass에서는 n/2번, 두 번째 pass에서는 n/4번, ... 마지막 pass에서는 1번의 연산을 수행한다. 즉 (1/2 + 1/4 + 1/8 + ... + 1/n) * n = (1 - 1/n) * n = &lt;b&gt;n - 1번의 연산을 수행&lt;/b&gt;하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;b&gt;pass는 총 logn개&lt;/b&gt;가 있게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;따라서 &lt;b&gt;평균 parallelism은 $\frac{n-1}{logn}$&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;예를 들어 n이 1,000,000일 때 20개의 pass가 생긴다. 이 때 평균 parallelism은 50,000인 반면 첫 번째 pass에서는 500,000번의 연산이 필요하다. 이처럼 이 방식은 &lt;b&gt;work-efficient한 방식&lt;/b&gt;이지 &lt;b&gt;resource-efficient한 방식이 아니다&lt;/b&gt;.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;GPU에서 Parallel Reduction&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;GPU에서 parallel reduction은 다음과 같은 과정을 거쳐 수행된다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;각 thread block에서 tree-based 알고리즘을 사용한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 때 각 thread block에서는 shared memory를 사용해 in-place reduction을 수행한다고 가정하자. (모든 thread는 병렬로 수행되기 때문) 즉, global memory에 있는 값을 shared memory에 load하고, thread block에서 reduction을 수행한다고 가정하자는 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;그러면 original data set은 global memory에 존재하게 되고, thread block의 shared memory는 일부 값만 가진다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예를 들어 이 때 sum()을 실행한다고 했을 때, 각 pass에서 partial sum을 계산하고, 그 결과는 index 0에 저장될 것이다.(이 값은 최종 결과값에 반영될 것이다) 이를 통해 global memory access 회수를 줄일 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;매우 큰 값을 처리하기 위해서는 여러 개의 thread block을 사용해야 한다. 이는 GPU를 BUSY한 상태로 유지하는 것에서도 의미가 있게 되고, 각 thread block은 input data set의 일부 값을 처리하게 된다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Reduction #1 : Interleaved Addressing&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;제일 기본적인 버전으로, element to thread direct mapping라고도 불린다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;774&quot; data-origin-height=&quot;391&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bFUXvl/btsB7j9ukCH/gOCa7YILlli50ESuj2Dk2k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bFUXvl/btsB7j9ukCH/gOCa7YILlli50ESuj2Dk2k/img.png&quot; data-alt=&quot;interleaved addressing #1&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bFUXvl/btsB7j9ukCH/gOCa7YILlli50ESuj2Dk2k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbFUXvl%2FbtsB7j9ukCH%2FgOCa7YILlli50ESuj2Dk2k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;659&quot; height=&quot;333&quot; data-origin-width=&quot;774&quot; data-origin-height=&quot;391&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;interleaved addressing #1&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;다음과 같은 단계로 실행된다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;각 thread는 shared memory에 값을 할당한다. (모든 thread가 shared memory에 data를 올리는 과정에 참여한다.)&lt;/li&gt;
&lt;li&gt;각 pass에서 2개의 값을 더하며, pass마다 stride를 2배로 늘린다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;첫 번째 iteration에서는 홀수 thread들이 자신에 해당하는 값과 인접한 값에 대해 reduction을 수행한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;동시에 각 pass마다 thread 개수를 절반으로 감소시켜가면서 재귀적으로 반복한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;즉, &lt;b&gt;n개의 element를 처리하기 위해 logn의 pass와 n개의 thread가 필요&lt;/b&gt;하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;예를 들어 pass 1에서는 0 2 4 6 ...의 thread만, pass 2에서는 0 4 8 12, ...의 thread만, pass 3에서는 0 8 16, ...의 thread만 계산에 참가한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;CUDA Kernel&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각 thread는 자신이 담당하는 부분에 대한 책임을 가진다.&lt;/li&gt;
&lt;li&gt;각 pass가 끝나면 절반의 thread는 더 이상 필요가 없어진다.&lt;/li&gt;
&lt;li&gt;각 pass에서, 2개의 값을 더하는데, 하나는 그 thread가 생성한 값이고, 다른 하나는 다른 thread가 처리한 값인데, 이 때 다른 하나의 입력값이 점점 더 멀리서 온다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;pass 0에서는 바로 옆에서 오지만 pass 1에서는 2칸, pass 2에서는 4칸, pass 3에서는 8칸, ... 이렇게 입력값이 멀리서 온다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1702656783661&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;__global__ reduction(int *input, int *results) {
    unsigned int t = threadIdx.x;
    unsigned int i = blockIdx.x * blockDim.x + threaddIdx.x;

    __shared__ float partialSum[BLOCK_SIZE]; // BLOCK_SIZE == blockDim.x
    partialSum[t] = input[i]; // 각 thread가 하나의 element를 shared memory에 load한다.

    for (unsigned int stride = 1; stride &amp;lt;= blockDim.x; stride *= 2) {
        __syncthreads();
        if (t % (2 * stride) == 0){
            partialSum[t] += partialSum[t + stride];
        }
    }

    // 여기에 __syncthread()를 추가하지 않는 이유?
    // thread 0이 결과를 적는다.
    if (t == 0) {
        // note that the result is per-block, not per-thread
        results[blockIdx.x] = partialSum[0];
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;각 thread block는 blockDim.x개의 input element를 처리&lt;/b&gt;하고,&amp;nbsp;&lt;b&gt;각 thread는 1개의 element를 shared memory에&lt;/b&gt; 올린다.&lt;/li&gt;
&lt;li&gt;for문 내의 `__syncthread()`가 필요한 이유는 &lt;b&gt;이전 pass의 partial sum이 모두 계산되어 있어야 하기 때문&lt;/b&gt;이다.&lt;/li&gt;
&lt;li&gt;마지막 loop가 끝난 후 `__syncthread()`를 추가하지 않는 이유는, `if(t == 0)`으로 왔다는 것은 &lt;b&gt;이미 partialSum을 모두 계산한 것이기 때문&lt;/b&gt;이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그리고 위 코드의 제일 마지막에 있는 `results[blockIdx.x] = partialSum[0]`의 경우, 모든 input에 대한 결과값이 아니라 &lt;b&gt;해당 thread block의 결과&lt;/b&gt;이다. 최종 결과값을 얻기 위해서는 reduction이 다시 한 번 수행되어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Observation&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;각 iteration에서, &lt;b&gt;각각의 warp에 대해 2개의 control flow가 순차적으로 발생&lt;/b&gt;한다. 하나는 for문 내의 if문에 걸려서 addition을 수행하는 thread들, 나머지는 그렇지 않은 thread들이다. 그렇지만 아무것도 하지 않는 thread들도 여전히 resource를 소모하고 있다. (&lt;b&gt;idling&lt;/b&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;첫 번째 pass가 끝나면 절반의 thread가 필요없게 된다. 즉, 모든 홀수 index를 가지는 thread는 첫 번째 pass가 끝난 이후 필요없게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;예를 들어 n이 1024인 경우, 5번째 pass가 끝나면 전체 warp의 각 block은 for문 내의 if문을 실패하게 되어 resource utilization이 별로다. 몇몇 warp는 살아있을 수 있지만, 해당 warp에서 오직 하나의 thread만 if문을 성공하므로 divergence가 발생한다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;예를 들어 thread block size가 32인 경우, 5번째 pass 이후에는 active thread의 간격이 64가 되는데, 그러면 thread block 0의 첫 번째 thread는 active, thread block 1의 모든 thread는 inactive, 이후 thread block 2의 첫 번째 thread가 active, ... 이렇게 반복된다. 때문에 warp의 일부 thread block의 모든 thread는 idling하게 된다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Reduction #2 : Interleaved Addressing 2&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;818&quot; data-origin-height=&quot;435&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OJ5Uv/btsB5CPw28s/gFkNypocCK1apk4QrNlUd0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OJ5Uv/btsB5CPw28s/gFkNypocCK1apk4QrNlUd0/img.png&quot; data-alt=&quot;interleaved addressing #2&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OJ5Uv/btsB5CPw28s/gFkNypocCK1apk4QrNlUd0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOJ5Uv%2FbtsB5CPw28s%2FgFkNypocCK1apk4QrNlUd0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;589&quot; height=&quot;313&quot; data-origin-width=&quot;818&quot; data-origin-height=&quot;435&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;interleaved addressing #2&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 방식은 n개의 element에 대해 &lt;b&gt;logn개의 pass&lt;/b&gt;를 사용하되, &lt;b&gt;n/2개의 thread만 사용&lt;/b&gt;하는 방식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;b&gt;각 thread block은 2 * blockDim.x개의 input element&lt;/b&gt;를 처리하고, &lt;b&gt;2개의 element를 shared memory에 load&lt;/b&gt;한다. 위 그림에는 바로 옆에 있는 값을 shared memory로 load하는 것으로 보이는데, 실제로는 thread 하나는 threadIdx.x와 threadIdx.x + BLOCK_SIZE 2개의 값을 shared memory로 load하는 방식이다. 이렇게 하면 memory coalescing을 통해 조금이나마 더 효율적으로 작동한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그 결과 &lt;b&gt;divergence를 조금 줄일 수 있다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;CUDA Kernel&lt;/h4&gt;
&lt;pre id=&quot;code_1702657246895&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;__global__ reduction(int *input, int *results) {
    __shared__ float partialSum[2 * BLOCK_SIZE]; // BLOCK_SIZE == blockDim.x
    
    // load two elements
    unsigned in t = threadIdx.x;
    unsigned int start = (2 * BLOCK_SIZE) * blockIdx.x;
    partialSum[t] = input[start + t];
    partialSum[BLOCK_SIZE + t] = input[start + BLOCK_SIZE + t];

    for (unsigned int stride = 1; stride &amp;lt;= blockDim.x; stride *= 2) {
        __syncthreads();
        if (t % stride == 0){
            partialSum[2 * t] += partialSum[2 * t + stride];
        }
    }

    // thread 0이 결과를 적는다.
    if (t == 0)
    {
        // note that the result is per-block, not per-thread
        results[blockIdx.x] = partialSum[0];
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;#1과의 차이점은 for문 내의 if문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;한편 이 경우 &lt;b&gt;2-way bank collision&lt;/b&gt;이 발생한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Reduction #3 : Non-Divergent Reduction&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;739&quot; data-origin-height=&quot;344&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rWXbp/btsB2RNbrYD/TK8poiKdrq5PZcUb9pOLV1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rWXbp/btsB2RNbrYD/TK8poiKdrq5PZcUb9pOLV1/img.png&quot; data-alt=&quot;interleaved addressing 3&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rWXbp/btsB2RNbrYD/TK8poiKdrq5PZcUb9pOLV1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrWXbp%2FbtsB2RNbrYD%2FTK8poiKdrq5PZcUb9pOLV1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;647&quot; height=&quot;301&quot; data-origin-width=&quot;739&quot; data-origin-height=&quot;344&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;interleaved addressing 3&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;#1 버전과 달라진 점은 &lt;b&gt;thread id가 순서대로&lt;/b&gt; 줄어든다는 것이다. 이를 통해 하나의 warp에 있는 thread를 계속 살릴 수 있게 되므로 &lt;b&gt;divergent가 줄어든다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;CUDA Kernel&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;reduction #1에서 inner loop의 divergent branch를 없애고, &lt;b&gt;strided index와 non-divergent branch&lt;/b&gt;를 사용한다.&lt;/p&gt;
&lt;pre id=&quot;code_1702657411888&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;for (unsigned int stride = 1; stride &amp;lt;= blockDim.x; stride *= 2) {
    __syncthreads();
    int index = t * (2 * stride);
    if (index &amp;lt; blockDim.x) {
        partialSum[index] += partialSum[index + stride];
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;단, 이 경우에는 shared memory &lt;b&gt;bank conflict&lt;/b&gt;가 발생한다. 첫 번째는 2-way, 두 번째는 4-way, ... 이렇게 &lt;b&gt;bank conflict가 계속 늘어난다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;thread index의 사용이 문제다&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;divergence를 없애기 위해 index를 바꿔서 쓸 수도 있다. 물론 이 경우는 commutative나 associative여야만 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;#3 버전의 경우에는 active thread가 연속적이지만 접근하는 data가 멀리 있기 때문에 bank conflict가 발생한다는 점도 문제였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;더 좋은 전략은 항상 &lt;b&gt;partialSum[] 배열의 첫 번째 위치로만 partial sum을 합치는 것&lt;/b&gt;이다. 이를 통해 active thread를 연속적으로 유지할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Reduction #4 : Sequential Addressing&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;730&quot; data-origin-height=&quot;416&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wt6dt/btsB7iipt1H/2y1xNmtgZFlb2IU8UNXqpk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wt6dt/btsB7iipt1H/2y1xNmtgZFlb2IU8UNXqpk/img.png&quot; data-alt=&quot;sequential addressing&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wt6dt/btsB7iipt1H/2y1xNmtgZFlb2IU8UNXqpk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fwt6dt%2FbtsB7iipt1H%2F2y1xNmtgZFlb2IU8UNXqpk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;609&quot; height=&quot;347&quot; data-origin-width=&quot;730&quot; data-origin-height=&quot;416&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;sequential addressing&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;#3까지의 구현은 stride가 점점 더 커지는 방식이었다. 그러나 &lt;b&gt;sequential addressing의 경우에는 stride가 점점 더 줄어든다&lt;/b&gt;. 이것이 더 좋은 이유는 &lt;b&gt;bank conflict가 없어지기 때문&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;734&quot; data-origin-height=&quot;438&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oF33Y/btsB7We74b6/vbQLl6vIK78Kt3ceJPUt8k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oF33Y/btsB7We74b6/vbQLl6vIK78Kt3ceJPUt8k/img.png&quot; data-alt=&quot;16개의 thread가 있을 때 예시&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oF33Y/btsB7We74b6/vbQLl6vIK78Kt3ceJPUt8k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoF33Y%2FbtsB7We74b6%2FvbQLl6vIK78Kt3ceJPUt8k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;571&quot; height=&quot;341&quot; data-origin-width=&quot;734&quot; data-origin-height=&quot;438&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;16개의 thread가 있을 때 예시&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;CUDA Kernel&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그러면 코드는 아래와 같다. 바뀐 점은 stride의 초기값, 바뀌는 값, 그리고 partialSum의 index이다.&lt;/p&gt;
&lt;pre id=&quot;code_1702657753039&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// before : #1 interleaved addressing
for (unsigned int stride = 1; stride &amp;lt;= blockDim.x; stride *= 2) {
    __syncthreads();
    if (t % (2 * stride) == 0){
        partialSum[t] += partialSum[t + stride];
    }
}

// before : #3 non-divergent reduction
for (unsigned int stride = 1; stride &amp;lt;= blockDim.x; stride *= 2) {
    __syncthreads();
    int index = t * (2 * stride);
    if (index &amp;lt; blockDim.x) {
        partialSum[index] += partialSum[index + stride];
    }
}

// after : #4 sequential addressing
for (unsigned int stride = BLOCK_SIZE / 2; stride &amp;gt;= 1; stride /= 2) {
    __syncthreads();
    if (t &amp;lt; stride){
        partialSum[t] += partialSum[t + stride];
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;before의 경우에는 bank conflict가 발생하지만 after의 경우에는 그렇지 않는다. 그리고 stride가 *2에서 /2로 바뀌었다. 또한 if문의 condition이 달라진다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;분석&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;예를 들어 thread block size가 32일 때 1024개의 thread가 있는 thread block의 경우, 각 thread block은 1024개의 element를 shared memory에 올린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 때 처음 5개 group에 대해서는 divergence가 존재하지 않는다! 512, 256, 128, 64, 32개의 &lt;b&gt;연속된 32개의 thread들이 각 pass에서 계속 active하기 때문에&lt;/b&gt;, warp 내의 모든 thread가 active거나 모두 inactive이기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;반면, 이후 16, 8, 4, 2, 1 5개 step에 대해서는 divergence가 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;다시 Global하게 돌아가면 : Segmented Reduction&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;787&quot; data-origin-height=&quot;366&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AQQn3/btsB2R7v4fd/YC4SYf8fiAiaUkRs3eJRU0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AQQn3/btsB2R7v4fd/YC4SYf8fiAiaUkRs3eJRU0/img.png&quot; data-alt=&quot;segmented reduction&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AQQn3/btsB2R7v4fd/YC4SYf8fiAiaUkRs3eJRU0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAQQn3%2FbtsB2R7v4fd%2FYC4SYf8fiAiaUkRs3eJRU0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;639&quot; height=&quot;297&quot; data-origin-width=&quot;787&quot; data-origin-height=&quot;366&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;segmented reduction&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;kernel 실행이 끝났을 때, 각 thread block의 오직 단 하나의 thread, thread 0만이 partialSum[0]에 있는 값을 global result의 blockIdx.x에 작성한다. &lt;span style=&quot;text-align: start;&quot;&gt;이후 이&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;global result에 다시 한 번 reduction을 진행&lt;/b&gt;해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;만약 input vector size가 매우 큰 경우, host는 반복적으로 kernel code를 실행시켜야 한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;global memory에 저장된 처음의 reduction 결과도 여전히 클 때, 그 reduction 결과에 대해 반복적으로 reduction을 진행한다는 말이다.&lt;/li&gt;
&lt;li&gt;또는, global sum을 만들기 위해서는 atomic operation을 사용할 수도 있다. 단 이 경우에는 reduction 결과가 atomicAdd를 사용해도 될 만큼 충분히 작아야 할 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;만약 input vector size가 별로 크지 않은 경우, data를 kernel로 전송하고 다시 불러오는 간단한 과정만 거쳐도 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Parallel Algorithm 분석&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Parallel Algorithm Overhead&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;parallel하게 reduction을 실행했을 때 overhead는 1) &lt;b&gt;shared memory에 값을 올리는 것&lt;/b&gt;과 2) reduction을 실행하는 &lt;b&gt;for문에 존재하는 barrier&lt;/b&gt;(__syncthread())에 대한 overhead가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;또한, asymtotic하게 연산의 개수는 O(n)이지만, 각 연산은 주소 계산이나 중간 결과값 조작 등 복잡한 로직을 포함하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이러한 점 때문에 만약 parallel code가 single thread hardware에서 실행된다면 sequential algorithm보다 훨씬 느릴 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Parallel Reduction Complexity&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;b&gt;logn번의 parallel pass&lt;/b&gt;에 대해, &lt;b&gt;각 pass S는 $\frac{n}{2^S}$번의 독립적인 연산을 수행&lt;/b&gt;한다. 즉, step complexity(pass의 개수)는 O(logn)이다. 그리고 &lt;b&gt;각 pass에서는 O(n)의 연산이 수행&lt;/b&gt;된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;물리적으로 병렬로 동작하는 P개의 thread에 대해, time complexity는 O($\frac{n}{P}$ + logn)이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;$\frac{n}{P}$ 부분은 전체 작업량을 processor의 개수로 나눈 부분이다.&lt;/li&gt;
&lt;li&gt;logn 부분은 step complexity 부분이며, 해당 부분은 parallelize될 수 없으므로 logn이다.&lt;/li&gt;
&lt;li&gt;한편, thread block에서 n = P이므로 O(logn)이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;parallel algorithm의 cost는 &lt;b&gt;processor의 개수 * time complexity&lt;/b&gt;이다. 즉 O(n) thread * O(logn) time complexity = &lt;b&gt;O(nlogn)&lt;/b&gt;이다. 즉, &lt;b&gt;cost-inefficient&lt;/b&gt;이다. (sequential의 경우 O(n)라는 것을 기억하자.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;즉, sequential한 버전보다 parallelize에 추가적인 resource가 필요해 더 많은 resource를 사용하고, 더 많은 시간이 걸린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Algorithm Cascading&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;b&gt;sequential과 parallel을 결합&lt;/b&gt;할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;지금까지 살폈던예시에서는 각 thread가 binary reduction을 수행하는 예시를 보았지만, 더 효율적인 병렬화를 위해 &lt;b&gt;각 thread가 2개 이상의 element를&lt;/b&gt; shared memory에 load하고 합칠 수 있다. 같은 방식으로 binary reduction 대신 n-way reduction을 수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이것이 cascading인데, 각 thread는 sequential하게 실행하는 element의 개수가 늘어나므로 thread 자체의 실행 시간은 더 길어지지만 &lt;b&gt;pass의 개수가 줄어든다&lt;/b&gt;. 전체적으로 시간이 훨씬 더 많이 줄어드므로, 더 효율적이게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;단계로 표현하면 다음과 같다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;각 thread는 sequential하게 O(logn)개의 element를 합친다.&lt;/li&gt;
&lt;li&gt;그러면 O($\frac{n}{logn}$)개의 thread를 할당했을 때, O($\frac{n}{logn}$)개의 thread는 O(logn) pass에 대해 parallelize하게 실행된다.&lt;/li&gt;
&lt;li&gt;따라서 cost = &lt;span style=&quot;letter-spacing: 0px;&quot;&gt; O($\frac{n}{logn}$ * logn) = &lt;b&gt;O(n)&lt;/b&gt;, 즉 &lt;b&gt;cost-efficient&lt;/b&gt;하다.&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;thread 개수가 $\frac{n}{logn}$개, 각 thread는 logn개만큼 연산을 수행하기 때문.&lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 경우 parallelism과 overhead이 균형을 이루며, 실제로는 상당한 속도 향상이 이뤄진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;추가적인 최적화 #5 : Unroll Loops&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;reduction은 낮은 arithmetic intensity를 가지고 있으므로, bandwidth를 포화할 수 있다.(계산에 필요한 연산량보다 I/O를 더 많이 쓴다는 뜻이다) 따라서 bottleneck은 instruction overhead일 가능성이 높다. 즉 address array arithmetic과 loop overhead이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그러면 &lt;b&gt;loop를 없애면 된다&lt;/b&gt;!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;reduction이 진행될 때마다 active thread의 개수가 감소한다. 특히 stride &amp;lt;= 32일 때는 오직 하나의 warp의 thread 중 몇 개만 active하다. 이 때&amp;nbsp;instruction은 warp 내에서 SIMD 방식으로 동기화되고, lock-step에서 실행된다.&amp;nbsp;즉, &lt;b&gt;stride &amp;lt;= 32일 때 warp끼리 barrier를 사용할 필요가 없고, &lt;/b&gt;같은 이유로 `if(t &amp;lt; stride)`&lt;b&gt;도 필요없다&lt;/b&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그러면 &lt;b&gt;마지막 6개의 iteration(32, 16, 8, 4, 2, 1)에 대해서는 loop를 풀면 더 효율적&lt;/b&gt;일 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;CUDA Kernel&lt;/h4&gt;
&lt;pre id=&quot;code_1702658748219&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// for stride &amp;gt; 32
for (unsigned int stride = BLOCK_SIZE / 2; stride &amp;gt; 32; stride /= 2) {
    __syncthreads();
    if (t &amp;lt; stride)
        partialSum[t] += partialSum[t + stride];
}

// loop unrolling for stride &amp;lt;= 32
if (t &amp;lt; 32) {
    partialSum[t] += partialSum[t + 32];
    partialSum[t] += partialSum[t + 16];
    partialSum[t] += partialSum[t + 8];
    partialSum[t] += partialSum[t + 4];
    partialSum[t] += partialSum[t + 2];
    partialSum[t] += partialSum[t + 1];
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 방식은 마지막 warp 뿐만 아니라 모든 warp에서 필요 없는 작업을 없앤다. unrolling하지 않는 경우, 모든 warp에서 for loop와 if문을 반복적으로 실행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;실행 시간 분석&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;923&quot; data-origin-height=&quot;425&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dcqMb3/btsB18PaHSj/NXOj8WNfSD6lG0HE2cWKE0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dcqMb3/btsB18PaHSj/NXOj8WNfSD6lG0HE2cWKE0/img.png&quot; data-alt=&quot;실행 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dcqMb3/btsB18PaHSj/NXOj8WNfSD6lG0HE2cWKE0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdcqMb3%2FbtsB18PaHSj%2FNXOj8WNfSD6lG0HE2cWKE0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;667&quot; height=&quot;307&quot; data-origin-width=&quot;923&quot; data-origin-height=&quot;425&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;실행 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;각 버전에 대해 실행 결과는 위와 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;kernel 1 : #1 버전&lt;/li&gt;
&lt;li&gt;kernel 2 : #3 버전&lt;/li&gt;
&lt;li&gt;kernel 3 : #4 버전&lt;/li&gt;
&lt;li&gt;kernel 4 : 이 글에서 다루지 않음&lt;/li&gt;
&lt;li&gt;kernel 5 : #5 버전&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;잘못된 내용이나 오탈자에 대한 지적, 질문&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;b&gt;등은 언제나 환영합니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>CS/Parallel Computing</category>
      <author>hyelie</author>
      <guid isPermaLink="true">https://hyelie.tistory.com/681</guid>
      <comments>https://hyelie.tistory.com/entry/%EC%9D%B4%EC%A2%85%EB%B3%91%EB%A0%AC%EC%BB%B4%ED%93%A8%ED%8C%85-Parallel-Patterns-Reduction#entry681comment</comments>
      <pubDate>Sat, 16 Dec 2023 03:35:08 +0900</pubDate>
    </item>
    <item>
      <title>[이종병렬컴퓨팅] Parallel Patterns : Histogram</title>
      <link>https://hyelie.tistory.com/entry/%EC%9D%B4%EC%A2%85%EB%B3%91%EB%A0%AC%EC%BB%B4%ED%93%A8%ED%8C%85-Parallel-Patterns-Histogram</link>
      <description>&lt;blockquote style=&quot;text-align: left;&quot; data-ke-style=&quot;style2&quot;&gt;이 글은 포스텍 성효진 교수님의 이종병렬컴퓨팅(CSED490C) 강의를 기반으로 재구성한 것입니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글에서는 다음과 같은 내용들을 살펴본다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;parallel한 histogram 계산 패턴&lt;/li&gt;
&lt;li&gt;privatization&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Histogram&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;histogram은 큰 data set에서 특징과 패턴을 추출하는 방법으로, 기본적으로는 dataset의 &lt;b&gt;각 bin 요소에 대해 count를 증가&lt;/b&gt;하는 방법이다. 제일 &lt;b&gt;기본적인 병렬 알고리즘&lt;/b&gt;은 아래와 같다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;input을 section으로 나누기&lt;/li&gt;
&lt;li&gt;각 thread는 하나의 section을 담당한다.&lt;/li&gt;
&lt;li&gt;각 thread는 section에서 순회한다.&lt;/li&gt;
&lt;li&gt;각 letter에 대해 bin counter를 증가시킨다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;효율적인 memory 접근을 위한 partitioning 방법&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;section을 나누는 방법이 memory access 효율에 영향을 미친다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wTiak/btsB6oi9g90/VrauOdaFGzsVarhCfmkTV1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wTiak/btsB6oi9g90/VrauOdaFGzsVarhCfmkTV1/img.png&quot; data-origin-width=&quot;666&quot; data-origin-height=&quot;202&quot; data-is-animation=&quot;false&quot; style=&quot;width: 47.3101%; margin-right: 10px;&quot; data-widthpercent=&quot;47.87&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wTiak/btsB6oi9g90/VrauOdaFGzsVarhCfmkTV1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwTiak%2FbtsB6oi9g90%2FVrauOdaFGzsVarhCfmkTV1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;666&quot; height=&quot;202&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cQzUeX/btsB5X0pJWH/EG6KXvkaJaUKMQmp0bRQsK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cQzUeX/btsB5X0pJWH/EG6KXvkaJaUKMQmp0bRQsK/img.png&quot; data-origin-width=&quot;553&quot; data-origin-height=&quot;154&quot; data-is-animation=&quot;false&quot; style=&quot;width: 51.5271%;&quot; data-widthpercent=&quot;52.13&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cQzUeX/btsB5X0pJWH/EG6KXvkaJaUKMQmp0bRQsK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcQzUeX%2FbtsB5X0pJWH%2FEG6KXvkaJaUKMQmp0bRQsK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;553&quot; height=&quot;154&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;partitining 방법 2가지&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위 그림에서 각 숫자는 thread가 어떤 element에 어떤 thread가 접근하는지를 나타낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;왼쪽 그림은 &lt;b&gt;sectioned partitioning&lt;/b&gt;인데, 이 경우 각 thread의 memory access가 coalesce되지 않기 때문에 효율적이지 않다. 각 thread의 첫 실행에서 thread 1은 index 0에, thread 1은 index 5에, thread 2는 index 9에, ... 이런 방식으로 접근하기 때문에 memory access가 coalesce된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;오른쪽 그림은 &lt;b&gt;interleaved partitioning&lt;/b&gt;인데, 이 경우 모든 thread가 연속된 section의 element에 접근하기 때문에 &lt;b&gt;memory access가 coalesce&lt;/b&gt;되어 &lt;b&gt;더 효율적&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;예시 : sectioned partitioning&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ogJfQ/btsB6grRr5H/n7cFIjCaZUUnVhyP6gFUx1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ogJfQ/btsB6grRr5H/n7cFIjCaZUUnVhyP6gFUx1/img.png&quot; style=&quot;width: 49.3862%; margin-right: 10px;&quot; width=&quot;434&quot; height=&quot;218&quot; data-widthpercent=&quot;49.97&quot; data-is-animation=&quot;false&quot; data-origin-height=&quot;446&quot; data-origin-width=&quot;887&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ogJfQ/btsB6grRr5H/n7cFIjCaZUUnVhyP6gFUx1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FogJfQ%2FbtsB6grRr5H%2Fn7cFIjCaZUUnVhyP6gFUx1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;887&quot; height=&quot;446&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bmPj8e/btsB1Ib5SbM/kGQKO9pdqEZWUGCNyv8cAk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bmPj8e/btsB1Ib5SbM/kGQKO9pdqEZWUGCNyv8cAk/img.png&quot; style=&quot;width: 49.451%;&quot; data-widthpercent=&quot;50.03&quot; data-is-animation=&quot;false&quot; data-origin-height=&quot;465&quot; data-origin-width=&quot;926&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bmPj8e/btsB1Ib5SbM/kGQKO9pdqEZWUGCNyv8cAk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbmPj8e%2FbtsB1Ib5SbM%2FkGQKO9pdqEZWUGCNyv8cAk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;926&quot; height=&quot;465&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;sectioned partitioning 예시&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위 그림은 `PROGRAMMING MASSIVEL`이라는 글자를 5개씩 section으로 나누고, 4개의 thread가 parallel하게 histogram을 count하는 것을 보여준다.&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;왼쪽 / 오른쪽 그림에서 각 thread는 각 section의 첫 번째 글자에 해당하는 bin counter를 1 증가시킨다.&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;오른쪽 그림은 2개의 thread가 같은 bin counter에 접근할 때 발생하는 문제를 보여준다. 이 경우, 두 thread가 접근하는 bin counter 모두 정상적으로 증가해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;예시 : interleaved partitioning&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PP2ek/btsB7U2GJeU/plSkmVm6hSYlwjKWv7Edt1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PP2ek/btsB7U2GJeU/plSkmVm6hSYlwjKWv7Edt1/img.png&quot; data-origin-width=&quot;847&quot; data-origin-height=&quot;416&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.8893%; margin-right: 10px;&quot; data-widthpercent=&quot;50.48&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PP2ek/btsB7U2GJeU/plSkmVm6hSYlwjKWv7Edt1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPP2ek%2FbtsB7U2GJeU%2FplSkmVm6hSYlwjKWv7Edt1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;847&quot; height=&quot;416&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bMEHWY/btsB4UXfdSY/FhodLT8LSvvpLAgxWB4TL0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bMEHWY/btsB4UXfdSY/FhodLT8LSvvpLAgxWB4TL0/img.png&quot; data-origin-width=&quot;847&quot; data-origin-height=&quot;424&quot; data-is-animation=&quot;false&quot; style=&quot;width: 48.948%;&quot; data-widthpercent=&quot;49.52&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bMEHWY/btsB4UXfdSY/FhodLT8LSvvpLAgxWB4TL0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbMEHWY%2FbtsB4UXfdSY%2FFhodLT8LSvvpLAgxWB4TL0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;847&quot; height=&quot;424&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;interleaved partitioning 예시&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위 그림은 interleaved partitioning 예시이다. sectioned partitioning과 다르게 하나의 iteration에서 모든 thread가 비슷한 memory에 있는 값을 참조하기 때문에 memory coalescing이 일어나며, 따라서 memory bandwidth를 더 효율적으로 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;마찬가지로 오른쪽 그림의 경우 2개의 thread가 같은 bin counter에 접근할 때 문제점을 보여준다. 이를 해결하기 위해 read-modify-write operation을 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Atomic Operation (Read-Modify-Write)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;동일한 memory 위치에 대해 2개 이상의 thread가 접근할 때 &lt;b&gt;data race가 발생&lt;/b&gt;한다. 이 경우 값을 쓰는 과정이 non-deterministic하기 때문에 결과가 어떻게 될지 보장할 수 없다. 이를 막기 위해 `atomicAdd()`를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이를 적용한 CUDA histogram 코드는 다음과 같다. (interleaved partitioning 버전이다)&lt;/p&gt;
&lt;pre id=&quot;code_1702651572004&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;__global__ void histo_kernel(unsigned char *buffer, long size, unsigned int *histo) {
    int i = blockIdx.x * blockDim.x + threadIdx.x;
    int stride = blockDim.x; // stride는 thread의 총 개수이다.
    
    // 모든 thread는 blockDim.x * gridDim.x개의 연속적인 element를 처리한다.
    while (i &amp;lt; size) {
        atomicAdd(&amp;amp;(histo[buffer[i]]), 1);
        i += stride;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Atomic Operation 성능&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;830&quot; data-origin-height=&quot;254&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c71Vbo/btsB2R7u7sG/AxVd9k9ptgteyzAFwfWFIK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c71Vbo/btsB2R7u7sG/AxVd9k9ptgteyzAFwfWFIK/img.png&quot; data-alt=&quot;atomic operation 성능&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c71Vbo/btsB2R7u7sG/AxVd9k9ptgteyzAFwfWFIK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc71Vbo%2FbtsB2R7u7sG%2FAxVd9k9ptgteyzAFwfWFIK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;640&quot; height=&quot;196&quot; data-origin-width=&quot;830&quot; data-origin-height=&quot;254&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;atomic operation 성능&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;DRAM에서 atomic operation의 경우, 각 read-modify-write 연산은 &lt;b&gt;2번의 full memory access delay(read latency, write latency)&lt;/b&gt;를 가진다. 이는 하나의 thread가 atomic하게 특정 위치에 값을 썼을 때 다른 모든 thread들이 해당 위치의 변경된 값을 확인할 수 있어야 하기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;또한 &lt;b&gt;동일한 memory location에 대한 모든 atomic operation은 serialize&lt;/b&gt;된다. 하나의 thread가 atomic operation으로 하나의 memory에 접근하고 있을 때, 다른 thread는 해당 위치에 접근할 수 없다는 말이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Latency가 Throughput을 결정한다.&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;같은 DRAM 위치에 대한 atomic operation throughput은 프로그램이 atomic operation을 실행하는 속도와 동일하다. 따라서 특정 위치에 대한 atomic operation의 비율은 read-modify-write 연산의 latency에 의해 한정된다. 일반적으로 global memory의 경우 1000 cycle보다 더 크다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;즉, 많은 thread가 같은 memory location에 대해 atomic operation을 수행해 contention이 발생한다면, memory throughput은 최대 bandwidth의 1/1000배 이하로 감소한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Hardware Improvement&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;L2 cache에서 atomic operation 수행&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;537&quot; data-origin-height=&quot;220&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bOD3cF/btsB7ScGuj1/2PtdtkJYtLy1KFQUkEvg7k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bOD3cF/btsB7ScGuj1/2PtdtkJYtLy1KFQUkEvg7k/img.png&quot; data-alt=&quot;hardware improvement : L2 cache&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bOD3cF/btsB7ScGuj1/2PtdtkJYtLy1KFQUkEvg7k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbOD3cF%2FbtsB7ScGuj1%2F2PtdtkJYtLy1KFQUkEvg7k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;537&quot; height=&quot;220&quot; data-origin-width=&quot;537&quot; data-origin-height=&quot;220&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;hardware improvement : L2 cache&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;atomic operation을 L2 cache에서 수행하는 것이다. 이 경우 latency가 더 줄어들게 된다. 그렇지만 여전히 serialize된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Shared Memory에서 atomic operation 수행&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;469&quot; data-origin-height=&quot;232&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bUUxZn/btsB6l7MRst/jyDkdtMN5CZ427nPMZK77k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bUUxZn/btsB6l7MRst/jyDkdtMN5CZ427nPMZK77k/img.png&quot; data-alt=&quot;hardware improvement : shared memory&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bUUxZn/btsB6l7MRst/jyDkdtMN5CZ427nPMZK77k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbUUxZn%2FbtsB6l7MRst%2FjyDkdtMN5CZ427nPMZK77k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;469&quot; height=&quot;232&quot; data-origin-width=&quot;469&quot; data-origin-height=&quot;232&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;hardware improvement : shared memory&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;atomic operation을 shared memory에서 수행하면 latency가 매우 줄어든다. 반면 각 work group에 private하게 만들어지므로 코드를 좀 더 짜야 한다는 단점이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Privatization&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;993&quot; data-origin-height=&quot;520&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bqw0O9/btsB7PUzWTl/E3urDNv36WgUd0yTPKqvzk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bqw0O9/btsB7PUzWTl/E3urDNv36WgUd0yTPKqvzk/img.png&quot; data-alt=&quot;privatization&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bqw0O9/btsB7PUzWTl/E3urDNv36WgUd0yTPKqvzk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbqw0O9%2FbtsB7PUzWTl%2FE3urDNv36WgUd0yTPKqvzk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;701&quot; height=&quot;367&quot; data-origin-width=&quot;993&quot; data-origin-height=&quot;520&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;privatization&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;여러 개의 thread block이 하나의 memory에 접근하는 경우, contention과 serialization이 계속 발생한다. 이를 막기 위해 &lt;b&gt;1) 각 thread block은 private copy를 가지고, 2) 이들을 통합해 final copy을 만드는 것&lt;/b&gt;이 &lt;b&gt;privatization&lt;/b&gt;이다. 이를 통해 contention과 serialization을 줄일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;장단점&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;overhead : privatization의 경우 추가적인 overhead는 private copy를 만드는 것, 그리고 private copy를 합쳐 final copy를 만드는 것이 overhead이다.&lt;/li&gt;
&lt;li&gt;장점 : final copy의 접근에 대한 contention과 serialization을 훨씬 줄일 수 있기 때문에 전체적인 성능은 약 10배 이상 향상된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Histogram의 Shared Memory Atomic Operation&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;하나의 thread block에는 여러 개의 thread가 있고, shared memory는 이들 사이에서 공유되는 memory이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 때 shared memory에서 atomic operation을 수행할 때 DRAM보다 100배, L2 cache보다는 약 10배 정도 더 높은 throughput을 뽑아낼 수 있다. 또한 shared memory variable에 접근할 수 있는 것은 같은 thread block의 thread뿐이기 때문 에 contention도 더 적다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Privatization과 Shared Memory Atomic&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;shared memory에서 atomic operation을 적용해 privatization을 구현한 CUDA histogram 코드는 다음과 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1702652323294&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;__global__ void histo_kernel(unsigned char *buffer, long size, unsigned int *histo) {
    int tid = threadIdx.x;
    __shared__ unsigned int histo_private[256]; // number of bins = 256
    if (tid &amp;lt; 256)
        histo_private[tid] = 0;
    __syncthreads(); // 초기화가 끝날 때까지 대기한다.

    int i = blockIdx.x * blockDim.x + threadIdx.x;
    int stride = blockDim.x; // stirde는 thread의 개수
    while (i &amp;lt; size) {
        atomicAdd ( &amp;amp;(private_histo[buffer[i]), 1); // shared memory에 접근
        i += stride;
    }
    __syncthreads(); // thread block의 모든 thread의 작업이 끝날 때까지 대기한다.

    // 이후 final copy에 추가한다.
    if (tid &amp;lt; 256){
        atomicAdd( &amp;amp;(histo[tid]), private_histo[tid] );
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Privatization에 대한 추가 정보&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;privatization은 병렬화하기 위해 자주 사용되는 강력한 기술이다.&lt;/li&gt;
&lt;li&gt;이 때 privatization을 적용하기 위해, operation은 &lt;b&gt;associative&lt;/b&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;(결합)하고 &lt;/span&gt;&lt;b&gt;commutative&lt;/b&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;(교환) 가능해야 한다. 그래야만 private copy를 합쳐도 결과가 동일하기 때문이다.&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;histogram add operation의 경우 associative &amp;amp; commutative하기 때문에 privatization을 적용할 수 있다. 만약 그렇지 않은 연산에 대해서는 privatization을 적용할 수 없다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;또한, shared memory의 크기는 작기 때문에 private histogram의 크기는 작아야만 한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;만약 histogram이 privatize하기에 너무 크다면, output histogram을 부분적으로 privatize하고, range test를 사용해 global/shared memory로 이동하면 된다.&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;shared atomics는, 일반적으로 global atomics보다 2배 이상 더 빠르다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;잘못된 내용이나 오탈자에 대한 지적, 질문&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;b&gt;등은 언제나 환영합니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>CS/Parallel Computing</category>
      <author>hyelie</author>
      <guid isPermaLink="true">https://hyelie.tistory.com/680</guid>
      <comments>https://hyelie.tistory.com/entry/%EC%9D%B4%EC%A2%85%EB%B3%91%EB%A0%AC%EC%BB%B4%ED%93%A8%ED%8C%85-Parallel-Patterns-Histogram#entry680comment</comments>
      <pubDate>Sat, 16 Dec 2023 00:47:23 +0900</pubDate>
    </item>
    <item>
      <title>[RAFT] RAFT consensus algorithm specification</title>
      <link>https://hyelie.tistory.com/entry/RAFT-RAFT-consensus-algorithm-specification</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;중간고사가 끝난 이후부터는 RAFT 알고리즘을 maude를 사용해 formal modeling을 진행할 것입니다. 그에 앞서 어떠한 specification을 모델링할지 결정해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;따라서, 이 글에서는 RAFT consensus algorithm에 대해 간단히 소개하고 modeling하고자 하는 specification을 작성하고자 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;RAFT consensus algorithm&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;RAFT consensus algorithm은 모든 node가 동일한 상태를 유지하며, tolerance를 보장하기 위해 고안된 알고리즘이다. 때문에 일부 node에 문제가 생겨도 전체 system이 잘 동작해야만 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;구성&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;580&quot; data-origin-height=&quot;248&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b0mBXm/btsyWh9rJ1B/f4nfQHf8oe9t7gO0Ggmuz1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b0mBXm/btsyWh9rJ1B/f4nfQHf8oe9t7gO0Ggmuz1/img.png&quot; data-alt=&quot;state의 변화. 출처 : In Search of an Understandable Consensus Algorithm&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b0mBXm/btsyWh9rJ1B/f4nfQHf8oe9t7gO0Ggmuz1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb0mBXm%2FbtsyWh9rJ1B%2Ff4nfQHf8oe9t7gO0Ggmuz1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;580&quot; height=&quot;248&quot; data-origin-width=&quot;580&quot; data-origin-height=&quot;248&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;state의 변화. 출처 : In Search of an Understandable Consensus Algorithm&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;모든 node는 아래 3가지 상태 중 한 가지를 가진다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;cluster란 여러 subsystem이 연결되어 하나의 system처럼 동작하는 것을 말한다.&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;leader &lt;/b&gt;&lt;span style=&quot;text-align: left;&quot;&gt;: cluster를 대표하는 상태로, client가 cluster로 보낸 &lt;b&gt;모든&lt;/b&gt; 메시지의 수신, 전송, 응답을 모두 맡는다. &lt;span style=&quot;text-align: left;&quot;&gt;주기적으로&lt;span&gt; &lt;span style=&quot;text-align: left;&quot;&gt;모든 follower에게&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;heartbeat를 보낸다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;follower&lt;/b&gt;&lt;span style=&quot;text-align: left;&quot;&gt;&lt;b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;: leader가 존재할 경우, 모든 node는 이 상태를 가진다. Leader로부터 받은 메시지를 처리한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;candidate&lt;/b&gt; : leader가 존재하지 않는 경우, 새 leader가 되기 전 상태이다. heartbeat를 받지 못한 follower가 candidate가 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;RAFT consensus algorithm은 cluster의 모든 node가 최신 상태로 동기화하는 방식으로 replicated state machine을 구현한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Flow&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;아래와 같은 과정을 통해 cluster 전체가 최신 상태로 동기화된다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;client가 leader에게 특정 명령을 보낸다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;좀 더 구체적으로는 client가 leader가 아닌 상태라면, 해당 요청을 leader로 redirect한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;leader가 모든 메시지에 대한 log를 생성해 local에 저장한 후, 모든 follower에게 복제해 전달한다. 해당 메시지를 수신한 follower는 leader에게 응답을 보낸다.&lt;/li&gt;
&lt;li&gt;leader가 수신한 `정상 응답 수`가 전체 노드 개수 중 과반수 이상이라면, log를 통해 cluster의 모든 node가 해당 명령을 수행할 때까지 log를 재전송하며 동시에 client에게 명령 수행 결과를 리턴한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;때문에 cluster의 모든 node가 같은 명령이 수행되었음을 보장할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;문제가 발생해 명령을 처리하지 못한 follower들은 복구된 후 cluster와 연결되었을 때 leader로부터 그간의 log를 모두 받아 다시 수행한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Leader Election Flow&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;leader는 다수결을 통해 선출된다. flow는 아래와 같다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;각 node의 election timeout 내에 heartbeat가 도착하지 않았다면 아래 단계를 진행한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;leader는 heartbeat를 모든 node에게 전송한다. 만약 election timeout 내에 leader로부터 heartbeat가 오지 않은 경우 leader에 문제가 생겼다고 간주하고, candidate로 상태를 바꾼다.&lt;span style=&quot;text-align: left;&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;text-align: left;&quot;&gt;election timeout이 끝난 follower node들은 candidate로 바뀌고, 새로운 term `newTerm`이 시작된다. 해당 node는 자신에게 한 표를 행사한 후 다른 node들에게 `투표 요청 메시지`를 보낸다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;text-align: left;&quot;&gt;이 메시지를 받은 node가 `newTerm`에서 투표한 적이 없다면 해당 `투표 요청 메시지`를 보낸 candidate node에게 `투표 응답 메시지`를 보낸 후 자신의 election timeout을 초기화한다.&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;text-align: left;&quot;&gt;때문에 현재 투표 중인 candidate 이외에 다른 candidate의 생성을 막을 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;text-align: left;&quot;&gt;전체 node 중 과반수에 해당하는 `투표 응답 메시지`를 받은 node는 `newTerm`의 새로운 leader가 된다. 다른 candidate들은 follwer가 된다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;여기서 사용된 용어는 아래와 같은 의미를 가지고 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;term&lt;/b&gt; : 새로운 election이 시작된 시점부터 끝날 때 까지의 시간을 식별하는 값이다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;election timeout&lt;/b&gt; : follower node가 candidate node로 될 때까지 기다리는 시간이다. 이 값은 모든 node별로 다른 값이 주어진다. 또한 &lt;b&gt;매 term마다 모든 node들의 election timeout은 무작위로 재조정&lt;/b&gt;된다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;heartbeat&lt;/b&gt; : leader가 모든 follower에게 주기적으로 보내는 메시지이다. leader의 상태 확인을 위해서만 사용한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Leader에 문제가 생긴 경우&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위 flow에 따르면, heartbeat를 수신하지 못한 채 election timeout이 끝난 node들은 candidate가 된다. 이후 `투표 요청 메시지`를 모든 node에게 보낸다고 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;만약 leader가 해당 메시지를 수신한 경우, `투표 요청 메시지` 내에 있는 `term 번호`를 확인하고, 자신이 가지고 있는 것 보다 크다면 follower로 전환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;과반을 얻지 못한 경우 - 동점자가 만들어진 경우&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;과반을 얻지 못한 경우, 해당 term을 즉시 종료하고 새로운 term을 시작함과 동시에 재선거를 시작한다. 동점자가 나타나는 현상을 막기 위해 &lt;b&gt;모든 term에서 모든 node들의 election timeout은 다르게 재조정&lt;/b&gt;된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;과반을 얻지 못한 경우 - 과반을 얻을 수 없는 경우&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;문제가 발생한 node가 너무 많다면 어떠한 경우에도 과반수 이상의 표를 얻을 수 없다. RAFT consensus algorithm의 경우 client의 모든 명령이 leader를 통해 수신되는데, leader가 만들어질 수 없는 이 경우 cluster 전체가 마비된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Quorum&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;투표에서 leader를 선출하기 위해 &lt;b&gt;과반수&lt;/b&gt;를, 즉 n이 cluster의 node 개수일 때 $\frac{n+1}{2}$ 개의 표를 얻어야만 한다. 이 개념은 cluster가 제대로 동작하기 위해 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;바로 앞에서 살펴 본 [과반을 얻을 수 없는 경우]가 발생하지 않기 위해서는 최대 $\frac{n-1}{2}$개의 node가 투표를 하지 않아도 된다. 만약 문제가 발생한 node가 $\frac{n-1}{2}$보다 많다면 과반수의 득표를 얻을 수 없기 때문에 cluster의 기능이 멈춘다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Specification&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;RAFT의 safety rule&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;election safety : 한 term에서 최대 하나의 leader가 선출된다.&lt;/li&gt;
&lt;li&gt;log matching : 두 log의 index와 term이 동일한 경우 해당 index까지의 모든 log entry는 동일하다.&lt;/li&gt;
&lt;li&gt;leader completness : 모든 leader는 commit된 log를 가진다.&lt;/li&gt;
&lt;li&gt;state machine safety : 과반수 이하의 node가 offline이더라도 잘 동작해야 한다.&lt;/li&gt;
&lt;li&gt;join consensus : 새로운 node가 추가되더라도 오직 단 하나의 leader만 존재할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Log&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;log matching을 비교할 수 있어야 한다.&lt;/li&gt;
&lt;li&gt;index, term, 변경 명령 3가지를 가지고 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;각 Node&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;state를 가진다. state는 크게 4가지, follower, leader, candidate, offline이 있다.&lt;/li&gt;
&lt;li&gt;모든 node는 현재 term과 각자의 log를 가지고 있다.&lt;/li&gt;
&lt;li&gt;모든 node는 현재의 leader node와 다른 모든 node들을 알고 있다.&lt;/li&gt;
&lt;li&gt;모든 node는 offline으로 변할 수 있다.&lt;/li&gt;
&lt;li&gt;state의 변화는 아래 diagram을 따른다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;580&quot; data-origin-height=&quot;248&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b0mBXm/btsyWh9rJ1B/f4nfQHf8oe9t7gO0Ggmuz1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b0mBXm/btsyWh9rJ1B/f4nfQHf8oe9t7gO0Ggmuz1/img.png&quot; data-alt=&quot;state의 변화. 출처 : In Search of an Understandable Consensus Algorithm&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b0mBXm/btsyWh9rJ1B/f4nfQHf8oe9t7gO0Ggmuz1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb0mBXm%2FbtsyWh9rJ1B%2Ff4nfQHf8oe9t7gO0Ggmuz1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;580&quot; height=&quot;248&quot; data-origin-width=&quot;580&quot; data-origin-height=&quot;248&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;state의 변화. 출처 : In Search of an Understandable Consensus Algorithm&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Follower&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;leader node를 알고 있어야 한다.&lt;/li&gt;
&lt;li&gt;RequestVote를 받은 경우 본인의 log index와 term을 바탕으로 true로 응답한다.&lt;/li&gt;
&lt;li&gt;leader로부터 heartbeat를 받지 못한 경우 candidate로 변화한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Leader&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다른 모든 node에게 multicast를 보낼 수 있다.&lt;/li&gt;
&lt;li&gt;주기적으로 heartbeat 메시지를 multicast한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Candidate&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다른 모든 node로 RequestVote 메시지를 multicast할 수 있다.&lt;/li&gt;
&lt;li&gt;leader로부터 AppendEntry 메시지를 받은 경우 follower로 변화한다.&lt;/li&gt;
&lt;li&gt;다른 node로부터 ResponseVote 메시지를 받을 수 있다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;과반수 이상의 투표를 받은 경우 leader로 변화한다. leader가 된 경우 모든 follower에게 AppendEntry 메시지를 보내며 이를 통해 log matching을 보장한다.&lt;/li&gt;
&lt;li&gt;과반수 미만의 투표를 받은 경우 follower로 변화한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Offline&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;state가 offline인 node는 그 어떤 message도 보낼 수 없고, 받을 수 없다.&lt;/li&gt;
&lt;li&gt;offline에서 online으로 바뀔 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Message&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;type이 있다. logUpdaterequest, appendEntryRequest, appendEntryResponse,commit, voteRequest, voteResponse가 있다.&lt;/li&gt;
&lt;li&gt;logUpdateRequest : leader node가 log update를 요청하는 메시지이다.&lt;/li&gt;
&lt;li&gt;appendEntryRequest : follower node에게 보내는 메시지이다.&lt;/li&gt;
&lt;li&gt;appendEntryResponse : follower node가 응답하는 메시지이다.&lt;/li&gt;
&lt;li&gt;commit : log commit message&lt;/li&gt;
&lt;li&gt;voteRequest : 투표를 요청하는 메시지&lt;/li&gt;
&lt;li&gt;voteResponse : 투표 응답 메시지&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Project/Model Checking</category>
      <author>hyelie</author>
      <guid isPermaLink="true">https://hyelie.tistory.com/679</guid>
      <comments>https://hyelie.tistory.com/entry/RAFT-RAFT-consensus-algorithm-specification#entry679comment</comments>
      <pubDate>Tue, 31 Oct 2023 01:01:47 +0900</pubDate>
    </item>
    <item>
      <title>Spring 면접대비 질문</title>
      <link>https://hyelie.tistory.com/entry/Spring-%EB%A9%B4%EC%A0%91%EB%8C%80%EB%B9%84-%EC%A7%88%EB%AC%B8-1</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;Spring&lt;/h3&gt;
&lt;div data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정의&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뭐 지원하는지&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IoC(bean)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DI&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AOP&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;등의 특징을 가지는 프레임워크&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Bean&lt;/h3&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정의&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;spring에서 plain old java object - 그냥 객체 - 를 bean이라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IoC Container가 관리 및 생성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@Component를 사용한 class들만 bean으로 정의된다. 이 bean들은 기본적으로 singleton이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IoC Container가 DI해주기도 한다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;IoC&lt;/h3&gt;
&lt;div data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정의&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제어 역전 - 프로그램 제어권이 programmer가 아니라 framework인 spring에 있는 것. 개발자는 framework의 형식에 맞춰 개발하게 된다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;AOP&lt;/h3&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정의&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;aspect oriented programming&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공통 관심사를 분리해 모듈화하는 것. 인증/로깅 등에 사용.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;어떤 로직에 대해 핵심 관점과 부가 기능으로 나누고, 부가 기능을 모듈화하는 것. 예를 들어 logging 등.&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Filter vs Interceptor&lt;/h3&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;filter는 dispatcher servlet에 들어가기 전에 요청을 가로챔. spring 범위의 밖임. 따라서 business logic과는 관계 없으므로 인증, xss, 인코딩 변환 등을 사용.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;interceptor는 dispatcher servlet에 들어간 후의 요청을 가로챔. 때문에 로그인, 권한 체크 등을 함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(나올 때는 반대)&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;DI&lt;/h3&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정의&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;spring IoC Container&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;종류 3가지&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;dependency injection&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fafafa; color: #333333; text-align: start;&quot;&gt;필요한 객체를 직접 생성하는 것이 아니라 외부에서 주입하는 것.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 bean들의 dependency는 IoC Container가 설정해줌.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;등록한 bean들끼리 dependency를 넣어준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 bean의 과정 : container 생성 - bean 생성 - dependency inject - 초기화 - 사용 - 소멸 전 callback - 종료 / container가 관리.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;구성 요소를 변경하더라도, 다른 부분의 코드를 변경하지 않는 것. DI를 쓰면 dependency가 있는 object를 다른 코드로 쉽게 바꿀 수 있다. 따라서 dependency가 느슨해지기에 유지보수를 쉽게 할 수 있게 된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;dependency injection의 방법은 contructor, setter, field injection 3가지가 있다. @autowired를 넣는 위치가 constructor는 costructor에 넣는 방식, setter는 setter method를 사용하는 방식, field는 field에 넣는 방식이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;일반적으로는 constructor injection을 사용한다고 한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;final로 선언해 immutable 보장 가능&lt;/li&gt;
&lt;li&gt;circular dependency 컴파일 시점에 확인 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;field injection의 경우 실행 시점에 circular dependency 확인 가능.&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Dispatcher Servlet 흐름&lt;/h3&gt;
&lt;div data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;672&quot; data-origin-height=&quot;412&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAvjk2/btsGMHzlkZu/PPxE7Yu80f8e8C4ypp9040/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAvjk2/btsGMHzlkZu/PPxE7Yu80f8e8C4ypp9040/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAvjk2/btsGMHzlkZu/PPxE7Yu80f8e8C4ypp9040/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAvjk2%2FbtsGMHzlkZu%2FPPxE7Yu80f8e8C4ypp9040%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;672&quot; height=&quot;412&quot; data-origin-width=&quot;672&quot; data-origin-height=&quot;412&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;DispatcherServlet이 client request를 받음&lt;/li&gt;
&lt;li&gt;HandlerMapping이 request url에 해당하는 controller 찾음&lt;/li&gt;
&lt;li&gt;HandlerAdapter에 처리 요청을 보냄&lt;/li&gt;
&lt;li&gt;controller가 로직을 처리하고 결과를 HandlerAdapter로 줌&lt;/li&gt;
&lt;li&gt;DispatcherServlet은 결과를 ViewResolver로 보내고, ViewResolver는 jsp파일의 경로를 찾는다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Persistence Context&lt;/h3&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정의&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장점&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;entity를 저장하는 환경.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DB의 캐시 역할을 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쓰기 지연 : write한 것들이 DB에 각각 들어가지 않고 모아서 넣어주기 때문에 내부적인 최적화가 이루어진다. (캐시, 쓰기 지연 등)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 백락으로, persistence에서 DB로 commit 보내는 시점에 스냅샷과 비교해서 필요한 만큼만 sql을 보낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지연 로딩 : 필요할 때 해당 data를 가져온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쓰기 지연 : 영속된 상태에서는 entity가 DB에 반영되었을 수도 있고 그렇지 않을 수도 있다. 내부적으로 값을 가지고 있기 때문. persistence context가 flusth하면 DB에 작업 내용을 반영함.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;비영속 : 객체는 생성됨, persistent context에 속하지 않음&lt;/li&gt;
&lt;li&gt;영속 : persistence ontext가 entity를 관리.&lt;/li&gt;
&lt;li&gt;준영속 : persistence context에서 관리되던 것이 더이상 관리되지 않는 상태. 이 상태에서는 변경사항은 DB에 반영됨.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지연 로딩 떄문에 N+1 문제가 생길 수 있다. join한 것들을 가져올 때 N개를 N번의 쿼리로 가져오는 경우가 그렇다. 해결하기 위해 fetch join을 쓴다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;패키지 구성&lt;/h3&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333;&quot; data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;종류&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333;&quot; data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;계층형&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도메인형 : &lt;span style=&quot;background-color: #fafafa; color: #333333; text-align: start;&quot;&gt;도메인형 구조를 사용하면 코드의 응집도가 높고, 도메인 그 자체의 흐름을 이해하기 쉽습니다.&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Spring Security&lt;/h3&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;680&quot; data-origin-height=&quot;516&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/coJBMn/btsGMAgelxd/tzAlxcSxFjyeSb2uBOQwHk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/coJBMn/btsGMAgelxd/tzAlxcSxFjyeSb2uBOQwHk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/coJBMn/btsGMAgelxd/tzAlxcSxFjyeSb2uBOQwHk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcoJBMn%2FbtsGMAgelxd%2FtzAlxcSxFjyeSb2uBOQwHk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;523&quot; height=&quot;397&quot; data-origin-width=&quot;680&quot; data-origin-height=&quot;516&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;filter에서 처리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. filter가 요청을 가로채면 정보를 토대로 인증용 객체 Token object를 생성.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 이후 authentication manager의 인증 method를 호출한다. 해당 method 내부에서는 개발자가 구현한 UserDetailsService method 내부에서 검증을 진행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. UserDetailService는 UserDetail이라는 객체를 반환하고, securitycontext에 userdetail 정보가 들어간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 내부적으로 filter에서 걸리고, 올바르지 않으면 return 올바르면 구현한 부분에서 userDetail을 securitycontext에 넣어준다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;DAO, DTO, BO, VO&lt;/h3&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;dao : data access object, DB 데이터 접근/조작을 위해 사용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;dto : data transfer object, layer 간에 데이터 교환을 위해 사용 (getter, setter만 사용)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;vo : value object, 값을 나타내는 객체&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;Spring vs Spring Boot&lt;/h3&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333;&quot; data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;spring 같은 경우는 설정을 많이 해 줘야 한다. bean이 어떤 package에 있고 어떤 이름을 가지는지 application.xml에 직접 등록을 해 줘야 한다. web.xml에 각 library에 대한 dependency도 직접 등록을 해 줘야 했다. 서버도 톰캣 따로 띄워 줘야 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;spring boot는 이러한 과정이 매우 간소화되었다. auto config 과정 (component scan 등)을 통해 bean 등록 자동화, 라이브러리도 가독성 좋게 관리, 내장 서버 등이 있다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;Spring 장점 &amp;amp; thread 동작&lt;/h3&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333;&quot; data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;spring&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- spring은 CPU 작업이 많은 경우가 좋다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #fafafa; color: #333333; text-align: start;&quot;&gt;연산이 많은 경우 thread를 사용해 명시적으로 처리할 수 있기 때문에 효율적.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- type-safe하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 실행에 오래 걸린다. (JVM, GC)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 플랫폼 독립. (jvm 위에서 돌 수 있음)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- thread 생성 위해서는 개발자의 관리가 필요함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;spring은 thread pool을 사용해 thread를 관리함. 내부적으로 몇 개의 thread를 미리 생성해 둠. 이후 필요한 작업에 할당했다가 돌려 받음. (thread를 생성/삭제하는 게 OS, JVM에 로드를 많이 주고, 무한히 생성할 수도 있기 때문.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 초기에 정해진 크기만큼 thread 생성함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 사용자 요청이 들어오면 queue에 담아두고, idle 상태(놀고 있는) thread가 있으면 queue에서 꺼내서 작업을 thread에 할당함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- idle인 thread가 없다면 작업은 queue에서 대기, 만약 queue가 가득 차면 thread 새로 생성.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- task 완료 시 thread는 idle 상태로 돌아가고, queue가 비고 thread가 초기 개수보다 더 많다면 destroy.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;=&amp;gt; 미리 만들어 놓고, 필요한 작업에 할당했다가 돌려받음.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;JPA &amp;amp; Transactional &amp;amp; Test annotation&lt;/h3&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333;&quot; data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;@transational&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;해당 메소드가 transation이 되게 보장해줌. 여러 DB 쿼리가 있으면 이것들을 transaction으로 묶음. 하나라도 문제 발생 시 롤백. 종료 시 commit().&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fafafa; color: #333333; text-align: start;&quot;&gt;여러 개의 transactional이 있는 경우, 격리 수준을 사용해서 해당 리소스에 접근. 순서는 jvm 스케쥴링에 따름.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;rollbackfor option : 기본적으로 unchecked exception만 롤백하기 때문에 exception도 롤백하게 지정&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;readonly option:&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;(일반적으로) entity가 영속성에 영속될 때, 해당 entity의 상태를 snapshot으로 남긴다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #fafafa; color: #333333; text-align: start;&quot;&gt;snapshot과 entity 상태를 비교해 변경된 내용만 update query를 모아 DB로 날린다.(이게 dirty checking이다!)&amp;nbsp;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #fafafa; color: #333333; text-align: start;&quot;&gt;transactional이 붙은 method는 트랜잭션 commit 시 DB에 flush함.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;readonly true : dirty checking 발생 X. read 한 이후 DB로 flush하지 않는다. (snapshot을 찍지 않기 때문에 변경사항 감지 X. 때문에 메모리 절약도 가능) 때문에 변경사항 반영 안 되는 것으로 알고 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;---&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;slice test : 특정 계층만 처리 가능. @springboottest : 전체, @webmvctest: controller, 등&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;@test&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;junit에서 test annotation 다 모아서 테스트 돌려줌.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;@springboottest&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;spring에서 bean 등록한 것들 &quot;다&quot; 모아서 injection해줌.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;테스트 코드에서 @transactional 쓰면 쿼리 날린 것 다 롤백해 줌. (안붙이면 롤백안됨)&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;bean 등록한거에서 가져오고 싶으면 @autowired 쓰면 됨&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;@webmvctest&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;controller 관련만 로드함. @mockbean 만들고 리턴값 정의해서 써야 함.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>내가 하고싶은 것!/취준</category>
      <author>hyelie</author>
      <guid isPermaLink="true">https://hyelie.tistory.com/678</guid>
      <comments>https://hyelie.tistory.com/entry/Spring-%EB%A9%B4%EC%A0%91%EB%8C%80%EB%B9%84-%EC%A7%88%EB%AC%B8-1#entry678comment</comments>
      <pubDate>Sun, 29 Oct 2023 23:05:09 +0900</pubDate>
    </item>
    <item>
      <title>[이종병렬컴퓨팅] Parallel Patterns : Convolution</title>
      <link>https://hyelie.tistory.com/entry/%EC%9D%B4%EC%A2%85%EB%B3%91%EB%A0%AC%EC%BB%B4%ED%93%A8%ED%8C%85-Parallel-Patterns-Convolution</link>
      <description>&lt;blockquote style=&quot;text-align: left;&quot; data-ke-style=&quot;style2&quot;&gt;이 글은 포스텍 성효진 교수님의 이종병렬컴퓨팅(CSED490C) 강의를 기반으로 재구성한 것입니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 글에서는 다음과 같은 내용을 다룬다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;convolution과 tiled convolution : 1D/2D convolution과 tiled convolution (input tiling, output tiling)&lt;/li&gt;
&lt;li&gt;2D convolution kernel을 작성하는 방법 : boundary condition 처리&lt;/li&gt;
&lt;li&gt;tiled parallel convolution 알고리즘의 cost와 장점&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Convolution&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;b&gt;인접한 input data element의 weighted sum 연산&lt;/b&gt;.&amp;nbsp;이 때 weighted sum 연산에 사용하는 weight를 &lt;b&gt;input mask array&lt;/b&gt; 또는 convolution kernel 또는 filter라고 부른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;예시 : 1D Convolution&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;663&quot; data-origin-height=&quot;230&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ntmHl/btsziQp2A3G/HstLK7Qjjx7r9oHwlJTHCK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ntmHl/btsziQp2A3G/HstLK7Qjjx7r9oHwlJTHCK/img.png&quot; data-alt=&quot;1D convolution 예시&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ntmHl/btsziQp2A3G/HstLK7Qjjx7r9oHwlJTHCK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FntmHl%2FbtsziQp2A3G%2FHstLK7Qjjx7r9oHwlJTHCK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;663&quot; height=&quot;230&quot; data-origin-width=&quot;663&quot; data-origin-height=&quot;230&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;1D convolution 예시&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위 예시는 input arrya가 N, convolution mask가 M일 때 P[2]를 계산하는 방법을 나타낸다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;convolution mask size가 5이므로, N의 P[2]를 포함해 주변의 5개 element를 가져온다.&lt;/li&gt;
&lt;li&gt;두 vector를 내적한다.&lt;/li&gt;
&lt;li&gt;내적한 결과의 합을 P[2]에 넣는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;수식으로 표현하면, P[2] = N[0] * M[0] + N[1] * M[1] + N[2] * M[2] + N[3] * M[3] + N[4] * M[4]이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Boundary Condition&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;616&quot; data-origin-height=&quot;224&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blYn0f/btszmwDfvYX/unFdR4IpZLdMbiDnFv5nNk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blYn0f/btszmwDfvYX/unFdR4IpZLdMbiDnFv5nNk/img.png&quot; data-alt=&quot;1D convolution boundary condition&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blYn0f/btszmwDfvYX/unFdR4IpZLdMbiDnFv5nNk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FblYn0f%2FbtszmwDfvYX%2FunFdR4IpZLdMbiDnFv5nNk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;616&quot; height=&quot;224&quot; data-origin-width=&quot;616&quot; data-origin-height=&quot;224&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;1D convolution boundary condition&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그러나 위 그림처럼 input array의 끝에 가까운 경우 convolution mask의 크기와 맞지 않은 boundary condition이 생긴다. 이 경우, input array의 범위를 벗어나는 것을 ghost element라고 한다. ghost element는 applicatoin마다 처리하는 방식이 다르다. 0이 들어갈 수도 있고, boundary에 있는 값을 복사하기도 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위 예시는 ghost element에 0을 넣는 경우이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;코드 : 1D convolution&lt;/h4&gt;
&lt;pre id=&quot;code_1698426823817&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;__global__ void convolution1D_basic(float* N, float* M,
                                    float* P, int Kernel_Width, int Width)
{
    int i = blockIdx.x*blockDim.x + threadIdx.x;
 
    float Pvalue = 0.f;
    int N_start_point = i - (Kernel_Width/2);
    for (int j = 0; j &amp;lt; Kernel_Width; j++) {
        if (N_start_point + j &amp;gt;= 0 &amp;amp;&amp;amp; N_start_point + j &amp;lt; Width)
            Pvalue += N[N_start_point + j] * M[j];
    }
 
    if (i &amp;lt; Width)
        P[i] = Pvalue;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;1D convolution을 수행하는 kernel code는 위와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;9번줄의 if문은 boundary condition을 확인하는 부분이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2D Convolution&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 2D의 경우를 보자. 사실상 1D와 동일하다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;560&quot; data-origin-height=&quot;361&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2BADw/btszkRg5S7M/LfhrDNt7JbQw8Pad7uUxp1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2BADw/btszkRg5S7M/LfhrDNt7JbQw8Pad7uUxp1/img.png&quot; data-alt=&quot;2D convolution 예시&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2BADw/btszkRg5S7M/LfhrDNt7JbQw8Pad7uUxp1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2BADw%2FbtszkRg5S7M%2FLfhrDNt7JbQw8Pad7uUxp1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;560&quot; height=&quot;361&quot; data-origin-width=&quot;560&quot; data-origin-height=&quot;361&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;2D convolution 예시&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위 예시는 2D convolution 예시이다. 1D와 동일한 방식으로, 특정 element를 convolution하기 위해 해당 element 주변의 element와 convolution mask를 내적하고 합계를 결과에 넣는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;예시 : 2D Convolution Boundary Check&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;610&quot; data-origin-height=&quot;362&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/p556F/btszjB60emx/ojafO261FlrZxoDfTG2fmK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/p556F/btszjB60emx/ojafO261FlrZxoDfTG2fmK/img.png&quot; data-alt=&quot;2D convolution boundary condition&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/p556F/btszjB60emx/ojafO261FlrZxoDfTG2fmK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fp556F%2FbtszjB60emx%2FojafO261FlrZxoDfTG2fmK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;610&quot; height=&quot;362&quot; data-origin-width=&quot;610&quot; data-origin-height=&quot;362&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;2D convolution boundary condition&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;2D의 경우 ghost cell 처리하는 방식은 1D와 동일하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Convolution Mask의 Access Pattern&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;convolution mask는 다음과 같은 특징이 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;크기는 일반적으로 작다.&lt;/li&gt;
&lt;li&gt;kernel 실행 중에 바뀌지 않는다.&lt;/li&gt;
&lt;li&gt;모든 output을 계산하기 위해 필요하며, 접근하는 순서가 동일하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;따라서 &lt;b&gt;convolution mask는 constant memory에 넣기 좋다&lt;/b&gt;!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;372&quot; data-origin-height=&quot;409&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mkq6O/btszltfQAkj/i2s1TCCzFNxVmri1kUPfd0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mkq6O/btszltfQAkj/i2s1TCCzFNxVmri1kUPfd0/img.png&quot; data-alt=&quot;CUDA memory hierarchy&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mkq6O/btszltfQAkj/i2s1TCCzFNxVmri1kUPfd0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fmkq6O%2FbtszltfQAkj%2Fi2s1TCCzFNxVmri1kUPfd0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;372&quot; height=&quot;409&quot; data-origin-width=&quot;372&quot; data-origin-height=&quot;409&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;CUDA memory hierarchy&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;각 thread는 grid별 &lt;b&gt;constant memory&lt;/b&gt;나 texture memory에 &lt;b&gt;100 cycle만에 read only&lt;/b&gt;할 수 있으며 &lt;b&gt;cache hit인 경우 10 cycle만에 read only&lt;/b&gt;할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Constant Memory&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;constant memory는 `cudaMemcpyToSymbol()`을 사용해 device memory에 복사한다. device로 옮겨지는 다른 변수와 같은 방식이다. `cudaMemcpyToSymbol()`는 해당 변수가 수정되지 않으며, read only로 안전하게 caching된다는 것을 알려준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1698429570140&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#define MAX_MASK_WIDTH 10

// global variable, outside any kernel/function
__constant__ float M[MAX_MASK_WIDTH];

// allocate N, P, initialize M_h and N, copy N to N_d
&amp;hellip;
cudaMemcpyToSymbol(M, M_h, MASK_WIDTH*sizeof(float));

// Mask is not given as an argument
convolution_1D_basic_kernel&amp;lt;&amp;lt;&amp;lt;dimGrid, dimBlock&amp;gt;&amp;gt;&amp;gt; (N_d, P_d, MASK_WIDTH, Width);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Constant Cache&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;constant cache는&lt;b&gt; kernel 실행 중에 수정되지 않는 constant data를 위한 특별한 cache&lt;/b&gt;이다. `__constant__`로 선언하며, L1 cache와 비슷한 throughput으로 constant cache에 접근할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;또한, constant cache는 read only이므로 warp 내의 같은 주소에 접근할 때 동시에 제공된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;추가적으로 read only이므로 coherence에 대해 신경쓰지 않아도 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1D Tiled Convolution&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;395&quot; data-origin-height=&quot;272&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dPVoDr/btszi8D6E43/ZvrplnYQzmcf06VdZCImh1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dPVoDr/btszi8D6E43/ZvrplnYQzmcf06VdZCImh1/img.png&quot; data-alt=&quot;tiling&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dPVoDr/btszi8D6E43/ZvrplnYQzmcf06VdZCImh1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdPVoDr%2Fbtszi8D6E43%2FZvrplnYQzmcf06VdZCImh1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;395&quot; height=&quot;272&quot; data-origin-width=&quot;395&quot; data-origin-height=&quot;272&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;tiling&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;인접한 output element는 같은 input element를 공유한다. 따라서 모든 thread의 input element를 shared memory에 올려 global memory access를 줄일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위 예시에서 mask_width가 5일 때 N[2]는 P[0], P[1], P[2], P[3], P[4]에서 사용하는 변수를 사용한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;644&quot; data-origin-height=&quot;242&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bp2Itq/btszkntLp8z/iwJ7Dx8BOV8YtDMo35kKU0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bp2Itq/btszkntLp8z/iwJ7Dx8BOV8YtDMo35kKU0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bp2Itq/btszkntLp8z/iwJ7Dx8BOV8YtDMo35kKU0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbp2Itq%2FbtszkntLp8z%2FiwJ7Dx8BOV8YtDMo35kKU0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;644&quot; height=&quot;242&quot; data-origin-width=&quot;644&quot; data-origin-height=&quot;242&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;각 group이 T개의 output element를 계산한다고 하자.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;T개의 output element를 계산하기 위해 T + mask_width - 1개의 input element가 필요하다.&lt;/li&gt;
&lt;li&gt;일반적으로 T는 mask_width보다 훨씬 크다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Tiling Option&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;566&quot; data-origin-height=&quot;93&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bCoaK0/btszkPwL2iI/mkQmgKgXtQASdktLmoVYv1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bCoaK0/btszkPwL2iI/mkQmgKgXtQASdktLmoVYv1/img.png&quot; data-alt=&quot;output tile에 thread block 크기를 맞추는 방법&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bCoaK0/btszkPwL2iI/mkQmgKgXtQASdktLmoVYv1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbCoaK0%2FbtszkPwL2iI%2FmkQmgKgXtQASdktLmoVYv1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;566&quot; height=&quot;93&quot; data-origin-width=&quot;566&quot; data-origin-height=&quot;93&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;output tile에 thread block 크기를 맞추는 방법&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;686&quot; data-origin-height=&quot;109&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dx4S7r/btszjCdPF6O/xmzBQVr7zzYLosFnqvNge0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dx4S7r/btszjCdPF6O/xmzBQVr7zzYLosFnqvNge0/img.png&quot; data-alt=&quot;input tile 크기에 thread block 크기를 맞추는 방법&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dx4S7r/btszjCdPF6O/xmzBQVr7zzYLosFnqvNge0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdx4S7r%2FbtszjCdPF6O%2FxmzBQVr7zzYLosFnqvNge0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;686&quot; height=&quot;109&quot; data-origin-width=&quot;686&quot; data-origin-height=&quot;109&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;input tile 크기에 thread block 크기를 맞추는 방법&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;tiling하는 방법은 크게 2가지가 있다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;output tile에 thread block 크기를 맞추는 방법
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모든 thread가 output element 계산에 참가한다. 위 예시에서는 blockDim.x가 4이다.&lt;/li&gt;
&lt;li&gt;몇몇 thread는 input element를 shared memory에 올려야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;input tile에 thread block 크기를 맞추는 방법&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;몇몇 thread는 output element 계산에 참가하지 않는다. 위 예시에서는 blockDim.x가 8이다.&lt;/li&gt;
&lt;li&gt;각 thread는 1개의 input element를 shared memory에 올린다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;여기서는 &lt;b&gt;두 번째 방법&lt;/b&gt;을 살펴본다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Input/Output Data와 Thread의 매핑&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;559&quot; data-origin-height=&quot;227&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdc6Qh/btszhthplbT/VvqASpJQPUR7RtefWpJKJ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdc6Qh/btszhthplbT/VvqASpJQPUR7RtefWpJKJ1/img.png&quot; data-alt=&quot;input/output data와 thread 매핑&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdc6Qh/btszhthplbT/VvqASpJQPUR7RtefWpJKJ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbdc6Qh%2FbtszhthplbT%2FVvqASpJQPUR7RtefWpJKJ1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;559&quot; height=&quot;227&quot; data-origin-width=&quot;559&quot; data-origin-height=&quot;227&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;input/output data와 thread 매핑&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;N이 input, P가 output이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각 thread에서 &lt;b&gt;index_input = index_output - (mask_width/2)&lt;/b&gt;이다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;tile_width은 tile의 output element 개수&lt;/b&gt;. 위 예시에서는 4.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;thread block size은 tile_width + (mask_width - 1)&lt;/b&gt;. 위 예시에서는 8.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;코드&lt;/h4&gt;
&lt;pre id=&quot;code_1698430603987&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;float output = 0.0f;
__shared__ float Ns[tile_width + (mask_width &amp;ndash; 1)];
int tx = threadIdx.x;

int index_output = blockIdx.x * blockDim.x + threadIdx.x;
int index_input = index_output &amp;ndash; (mask_width &amp;ndash; 2);

if (index_input &amp;gt;= 0 &amp;amp;&amp;amp; index_input &amp;lt; width)) {
    Ns[threadIdx.x] = N[index_input];
}
else {
    Ns[threadIdx.x] = 0.0f;
}

if (tx &amp;lt; tile_width) {
    output = 0.0f;
    for (int j=0 ; j &amp;lt; mask_width; j++) {
        output += M[j] * Ns[tx + j];
    }
    P[index_output] = output;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위에서 언급했듯 input tile에 thread block의 크기를 맞추는 방법이기에 각 thread는 하나의 input element를 shared memory에 올려야 한다. 위 코드의 8번줄부터 13번줄까지 shared memory에 input memory를 올리는 모습이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;15번줄부터는 0부터 (tile_width-1)까지의 thread만 계산에 참가하는 것을 보여준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;해석&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;463&quot; data-origin-height=&quot;86&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bEqTYK/btszlHSzcrY/aMaZVCB2P5teSNxLefkRDK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bEqTYK/btszlHSzcrY/aMaZVCB2P5teSNxLefkRDK/img.png&quot; data-alt=&quot;shared memory access&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bEqTYK/btszlHSzcrY/aMaZVCB2P5teSNxLefkRDK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbEqTYK%2FbtszlHSzcrY%2FaMaZVCB2P5teSNxLefkRDK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;463&quot; height=&quot;86&quot; data-origin-width=&quot;463&quot; data-origin-height=&quot;86&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;shared memory access&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;437&quot; data-origin-height=&quot;273&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IC2Le/btszk8iGWfX/Aoim9SO3wWj8dvgUSElI11/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IC2Le/btszk8iGWfX/Aoim9SO3wWj8dvgUSElI11/img.png&quot; data-alt=&quot;thread당 shared memory access&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IC2Le/btszk8iGWfX/Aoim9SO3wWj8dvgUSElI11/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIC2Le%2Fbtszk8iGWfX%2FAoim9SO3wWj8dvgUSElI11%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;437&quot; height=&quot;273&quot; data-origin-width=&quot;437&quot; data-origin-height=&quot;273&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;thread당 shared memory access&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;mask_width가 5인 상황이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;NS[0]은 thread 1에 의해 사용된다.&lt;/li&gt;
&lt;li&gt;NS[1]은 thread 1, 2에 의해 사용된다.&lt;/li&gt;
&lt;li&gt;NS[2]은 thread 1, 2, 3에 의해 사용된다.&lt;/li&gt;
&lt;li&gt;NS[3]은 thread 1, 2, 3, 4에 의해 사용된다.&lt;/li&gt;
&lt;li&gt;NS[4]은 thread 1, 2, 3, 4에 의해 사용된다.&lt;/li&gt;
&lt;li&gt;NS[5]은&lt;span&gt;&amp;nbsp;&lt;/span&gt;thread 1, 2, 3에 의해 사용된다.&lt;/li&gt;
&lt;li&gt;NS[6]은&lt;span&gt;&amp;nbsp;&lt;/span&gt;thread 1, 2에 의해 사용된다.&lt;/li&gt;
&lt;li&gt;NS[7]은 thread 1에 의해 사용된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2D Convolution&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Padding&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;335&quot; data-origin-height=&quot;173&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnEmwv/btszluTlgpT/bwJ4RJJ1O0gjw02bDD03QK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnEmwv/btszluTlgpT/bwJ4RJJ1O0gjw02bDD03QK/img.png&quot; data-alt=&quot;padding&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnEmwv/btszluTlgpT/bwJ4RJJ1O0gjw02bDD03QK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbnEmwv%2FbtszluTlgpT%2FbwJ4RJJ1O0gjw02bDD03QK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;335&quot; height=&quot;173&quot; data-origin-width=&quot;335&quot; data-origin-height=&quot;173&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;padding&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;2D matrix를 DRAM에서 burst할 때, &lt;b&gt;width가 burst의 배수가 아닌 경우 misalignment&lt;/b&gt;가 발생할 수 있다. 때문에 &lt;b&gt;width를 burst의 배수에 맞춰 row의 시작점을 DRAM burst에 맞추는 방식&lt;/b&gt;이 좋다. 이는 width에 추가적인 padding을 넣는 방식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;만약 위 예시에서 padding이 없는 상황을 가정해 보자. 이 때 burst는 4개의 element를 가져온다고 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그러면 M$_1, _0$은 burst 0에 속해 있고, M$_1, _1$과 M$_1, _2$는 burst 2에 속해 있기에 row 1을 가져오기 위해 DRAM access를 2번 해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;반면 padding이 있다면 1번만 DRAM access해도 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Tiling Strategy&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Tile 설계&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;288&quot; data-origin-height=&quot;265&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eHisfw/btszkmO9JYb/JyyNqK5n49kC3sv6Gt7c20/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eHisfw/btszkmO9JYb/JyyNqK5n49kC3sv6Gt7c20/img.png&quot; data-alt=&quot;tiling 설계&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eHisfw/btszkmO9JYb/JyyNqK5n49kC3sv6Gt7c20/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeHisfw%2FbtszkmO9JYb%2FJyyNqK5n49kC3sv6Gt7c20%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;288&quot; height=&quot;265&quot; data-origin-width=&quot;288&quot; data-origin-height=&quot;265&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;tiling 설계&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;thread block을 input tile에 매핑&lt;/b&gt;한다. 그러면 모든 thread는 N의 tile을 shared memory에 올린다. 그러면 thread들은 N의 element를 사용해 P를 계산한다.&lt;/li&gt;
&lt;li&gt;TILE_SIZE는 x, y축의 output tile 크기를 정의한다.&lt;/li&gt;
&lt;li&gt;thread block size는 TILE_SIZE와 input tiling의 mask width에 의존한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Indexing&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;285&quot; data-origin-height=&quot;309&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/beML80/btszg0GyQoq/GKJbdefZsAeHvenkVtt3wk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/beML80/btszg0GyQoq/GKJbdefZsAeHvenkVtt3wk/img.png&quot; data-alt=&quot;tile indexing&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/beML80/btszg0GyQoq/GKJbdefZsAeHvenkVtt3wk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbeML80%2Fbtszg0GyQoq%2FGKJbdefZsAeHvenkVtt3wk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;285&quot; height=&quot;309&quot; data-origin-width=&quot;285&quot; data-origin-height=&quot;309&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;tile indexing&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1698432193731&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int tx = threadIdx.x;
int ty = threadIdx.y;

int row_o = blockIdx.y * TILE_SIZE + ty;
int col_o = blockIdx.x * TILE_SIZE + tx;

int row_i = row_o &amp;ndash; MASK_WIDTH/2;
int col_i = col_o &amp;ndash; MASK_WIDTH/2;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그러면 input/output index는 위 코드와 같다. input의 경우 ghost cell을 포함해야 하기에&amp;nbsp; output보다 더 크다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Shared Memory에 Input Tile Load&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;328&quot; data-origin-height=&quot;285&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/el6Gii/btszhoAuMto/dWlCIkOmgchz0itUilJRAK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/el6Gii/btszhoAuMto/dWlCIkOmgchz0itUilJRAK/img.png&quot; data-alt=&quot;Shared Memory에 Input Tile Load&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/el6Gii/btszhoAuMto/dWlCIkOmgchz0itUilJRAK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fel6Gii%2FbtszhoAuMto%2FdWlCIkOmgchz0itUilJRAK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;328&quot; height=&quot;285&quot; data-origin-width=&quot;328&quot; data-origin-height=&quot;285&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Shared Memory에 Input Tile Load&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1698432438438&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;float output = 0.0f;

if ((row_i &amp;gt;= 0) &amp;amp;&amp;amp; (row_i &amp;lt; N.height) &amp;amp;&amp;amp; (col_i &amp;gt;= 0) &amp;amp;&amp;amp; (col_i &amp;lt; N.width)) {
    Ns[ty][tx] = N[row_i*N.width + col_i];
}
else {
    // threads that load halos outside N returns 0.0
    Ns[ty][tx] = 0.0f;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;input tile을 shared memory에 올리는 코드는 위와 같다. 범위가 벗어나는 경우 ghost element이므로 if-else를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Output 계산&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;294&quot; data-origin-height=&quot;403&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgHVCy/btszi60yLdM/x3ZoYpKXwomQ3m0CsRlB20/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgHVCy/btszi60yLdM/x3ZoYpKXwomQ3m0CsRlB20/img.png&quot; data-alt=&quot;output 계산&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgHVCy/btszi60yLdM/x3ZoYpKXwomQ3m0CsRlB20/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbgHVCy%2Fbtszi60yLdM%2Fx3ZoYpKXwomQ3m0CsRlB20%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;294&quot; height=&quot;403&quot; data-origin-width=&quot;294&quot; data-origin-height=&quot;403&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;output 계산&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1698432603001&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;float output = 0.0f;

if (ty &amp;lt; TILE_SIZE &amp;amp;&amp;amp; tx &amp;lt; TILE_SIZE){
    for (i = 0; i &amp;lt; MASK_WIDTH; i++) {
        for (j = 0; j &amp;lt; MASK_WIDTH; j++) {
        	output += M[i][j] * Ns[i+ty][j+tx];
        }
    }
    if (row_o &amp;lt; P.height &amp;amp;&amp;amp; col_o &amp;lt; P.width)
        P[row_o * P.width + col_o] = output;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;3번줄의 첫 번째 if문은 TILE 안에 있는 thread만 수행하게 만든다. thread block size가 TILE_SIZE보다 크기 때문에 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;9번줄의 if문은 output이 올바른 범위 내에 있는지 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Bandwidth Reduction 분석&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1D Convolution의 경우&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;b&gt;TILE_SIZE + MASK_WIDTH - 1&lt;/b&gt;개의 element가 &lt;b&gt;shared memory&lt;/b&gt;에 올라간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;b&gt;TILE_SIZE * MASK_WIDTH번의 global memory access가 shared memory access로&lt;/b&gt; 바뀐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;따라서 bandwidth reduction은 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;$\frac{\text{(TILE_SIZE * MASK_WIDTH)}}{\text{(TILE_SIZE + MASK_WIDTH - 1)}$&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;위 수식은 edge tile에 있는 ghost cell을 무시한 수식이다.&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;Ghost Cell을 고려한 경우&lt;/h4&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;boundary tile의 예외를 처리하기 위해 TILE_SIZE + $\frac{\text{(MASK_WIDTH - 1)}}{2}$개의 element를 shared memory에 올렸다.&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 때, ghost cell들에 대해서는 global memory access를 하지 않았다. 그만큼 더 줄어든다.&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;일반화&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;일반화하면, MASK_WIDTH &amp;lt;&amp;lt; TILE_WIDTH인 경우 &lt;b&gt;MASK_WIDTH에 근사하게 bandwidth가 줄게&lt;/b&gt; 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;예시&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;528&quot; data-origin-height=&quot;157&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5YJIp/btszjHsE86g/Io7bG0ok3cNqeJN1DJfXO0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5YJIp/btszjHsE86g/Io7bG0ok3cNqeJN1DJfXO0/img.png&quot; data-alt=&quot;1D convolution에서 bandwidth 감소율&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5YJIp/btszjHsE86g/Io7bG0ok3cNqeJN1DJfXO0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5YJIp%2FbtszjHsE86g%2FIo7bG0ok3cNqeJN1DJfXO0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;528&quot; height=&quot;157&quot; data-origin-width=&quot;528&quot; data-origin-height=&quot;157&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;1D convolution에서 bandwidth 감소율&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;mask_width가 5인 위 예시에서, 각 P의 element를 계산하기 위해 5개의 N element가 있어야 한다. tiling을 쓴 경우, 5개의 N element는 shared memory에 있는 것을 참조한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;shared memory에는 12개의 element가 올라갔으며, 전체 output tile을 위해 8 * 5개의 shared memory access를 했다. 따라서 bandwidth reduction은 40/12 = 3.3으로, 3.3배 향상되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;boundary tile까지 고려해 보자. boundary가 아닌 element는 10개가 shared memory에 올라간다. 이 떄 ghost cell들은 global memory access를 하지 않으니, 전체 access 회수는 3 + 4 + 6*5 = 37이다. 따라서 37 / 12로, 3.7배 향상되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2D Convolution의 경우&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;b&gt;(TILE_SIZE + MASK_WIDTH - 1)$^2$&lt;/b&gt;개의 element가 &lt;b&gt;shared memory&lt;/b&gt;에 올라간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;P의 각 element를 계산하기 위해 MASK_WIDTH$^2$개의 N element에 접근해야 하므로 &lt;b&gt;TILE_SIZE$^2$ * MASK_WIDTH$^2$번의 global memory access가 발생&lt;/b&gt;한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;따라서 bandwidth reduction은 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;$\frac{\text{TILE_SIZE}^2 \times \text{MASK_WIDTH}^2}{\text{(TILE_SIZE + MASK_WIDTH - 1)} ^ 2}$&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;위 수식은 edge tile에 있는 ghost cell을 무시한 수식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;TILE_SIZE에 비례해 memory bandwidth가 급격하게 줄게 된다. 근사하면 TILE_SIZE$^2$배만큼 성능이 향상되기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그렇지만 &lt;b&gt;TILE_WIDTH가 커질수록 필요한 shared memory size가 더 커진다&lt;/b&gt;는 것을 인지해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;이외의 최적화&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Texture Memory 사용&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;input image를 shared memory 대신 texture cache에 올리기
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;코드가 더 쉽고 깔끔해지며, texture hardware path를 통해 global memory read를 할 수 있다.&lt;/li&gt;
&lt;li&gt;이 경우 data read는 2D/3D spatial locality에 특별화된 texture cache에 저장된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;CUDA array 사용
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;global memory에 1D/2D/3D 형태의 data 저장을 위한 object를 만든다.&lt;/li&gt;
&lt;li&gt;OpenGL이나 DirectX의 표준 교환 형식이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;host code&lt;/h4&gt;
&lt;pre id=&quot;code_1698434639660&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// global declaration of 2D float texture (visible for host and device code)
texture&amp;lt;float, cudaTextureType2D, cudaReadModeElementType&amp;gt; tex; &amp;hellip;

// Create explicit channel description (could use an implicit as well)
cudaChannelFormatDesc channelDesc = cudaCreateChannelDesc(32, 0, 0, 0, cudaChannelFormatKindFloat);

// Allocate CUDA array in device memory
cudaArray* cuArray;
cudaMallocArray(&amp;amp;cuArray, &amp;amp;channelDesc, width, height);

// Copy some data located at address h_data in host memory into CUDA array
cudaMemcpyToArray(cuArray, 0, 0, h_data, size, cudaMemcpyHostToDevice);
&amp;hellip;

// Bind the array to the texture reference
cudaBindTextureToArray(tex, cuArray, channelDesc);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;texture data storage를 할당하고, texture를 해당 data storage에 할당한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;device code&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1698434661504&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;float value = tex2D(tex, xpos, ypos);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;texture reference를 사용해 data fetch할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Loop Unrolling&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;loop를 펼쳐서 control flow overhead를 줄이는 방식. 이 방법을 사용하면 global read 성능이 향상될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;예를 들어 숫자 1개 대신 4개의 숫자를 가져오던가(memory coalescing), float4 vector type을 쓰면 locality와 bandwidth 최적화에서 이점이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Matrix Multiplication&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;입력 image를 matrix로 바꾸고, matrix multiplication을 수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;기본적으로 convolution은 filter와 이동하는 window에 의해 선택된 local region 사이의 내적이므로 memory에 있는 모든 window를 확장하고 matrix multiplication으로 최적화 할 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예를 들어 input image f가 [1, 2, 3, 4]rh filter가 [-1, -2, -3]인 경우,&lt;/li&gt;
&lt;li&gt;f = $ \begin{bmatrix} 1 &amp;amp; 2 &amp;amp; 3 \\ 2 &amp;amp; 3 &amp;amp; 4 \\ \end{bmatrix}$, g = $\begin{bmatrix} -1 \\ -2 \\ -3 \end{bmatrix}$로 설정하고 matrix multiplication하면 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Computational Transformation&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;algorithmic strength reduction을 통해 multiplication 연산 회수를 줄일 수 있다. 예를 들어 덧셈같은 weak operation의 회수를 늘이고 곱셈같은 strong operation의 회수를 줄일 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Winograd&lt;/li&gt;
&lt;li&gt;Strassen Algorithm : 행렬 곱을 부분으로 나눠, 8번의 곱연산을 7번의 곱연산과 n번의 덧셈으로 나누는 기술이다.&lt;/li&gt;
&lt;li&gt;Fast Fourier Transform&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;잘못된 내용이나 오탈자에 대한 지적, 질문&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;b&gt;등은 언제나 환영합니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>CS/Parallel Computing</category>
      <author>hyelie</author>
      <guid isPermaLink="true">https://hyelie.tistory.com/676</guid>
      <comments>https://hyelie.tistory.com/entry/%EC%9D%B4%EC%A2%85%EB%B3%91%EB%A0%AC%EC%BB%B4%ED%93%A8%ED%8C%85-Parallel-Patterns-Convolution#entry676comment</comments>
      <pubDate>Sat, 28 Oct 2023 04:42:37 +0900</pubDate>
    </item>
    <item>
      <title>[이종병렬컴퓨팅] Performance Considerations</title>
      <link>https://hyelie.tistory.com/entry/%EC%9D%B4%EC%A2%85%EB%B3%91%EB%A0%AC%EC%BB%B4%ED%93%A8%ED%8C%85-Performance-Considerations</link>
      <description>&lt;blockquote style=&quot;text-align: left;&quot; data-ke-style=&quot;style2&quot;&gt;이 글은 포스텍 성효진 교수님의 이종병렬컴퓨팅(CSED490C) 강의를 기반으로 재구성한 것입니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 글에서는 GPU resource의 제약과, 이들이 성능에 미치는 영향을 알아본다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;최적화 목표&lt;/li&gt;
&lt;li&gt;memory coalescing&lt;/li&gt;
&lt;li&gt;shared memory bank 충돌&lt;/li&gt;
&lt;li&gt;점유율&lt;/li&gt;
&lt;li&gt;thread granularity&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;최적화 목표&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Performance 고려 사항&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;parallel한 코드와 hardware resource의 제약, 이 두가지를 관리하는 것이 고성능의 핵심이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그러나 먼저, 어디서 제일 많은 시간이 걸리는지 측정해야 한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Amdahl의 법칙을 생각해야 한다.&lt;/li&gt;
&lt;li&gt;coarse grained한 부분부터 측정하고, 이후에 fine grained한 부분을 측정하면 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;다음으로 main resource의 병목을 찾아야 한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;application마다 bottleneck이 다르다.&lt;/li&gt;
&lt;li&gt;하나의 resource 사용량을 다른 것과 교환해서 성능을 올릴 수 있는지 고려해야 한다.&lt;/li&gt;
&lt;li&gt;compute-bound인지 memory-bound인지 고려해야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;최적화 목표&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;최적화의 목표는 &lt;b&gt;computing unit과 memory bandwidth를 최대로 사용하는 것&lt;/b&gt;이 목적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;computing unit을 최대로 사용하기 위해서는,&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Flops가 최대 연산량에 근접하게 처리해야 한다.&lt;/li&gt;
&lt;li&gt;각 thread에서는 &lt;b&gt;latency와 control divergence를 줄여야&lt;/b&gt; 한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;DRAM bandwidth 줄이기&lt;/b&gt; : shared memory나 memory hierarchy를 사용해야 한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;memory coalescing&lt;/b&gt; : memory bandwidth를 더 효율적으로 사용해야 한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;shared memory bank collision 회피&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;control divergence 회피&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;thread끼리는 더 concurrent&lt;/b&gt;하게 만들어야 한다. 이는 occupancy이며, SM resource를 동적으로 분할하면 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;memory bandwidth를 최대로 사용하기 위해서는&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;thread granularity&lt;/b&gt; : 각 thread는 더 independent하게 접근해야 한다.&lt;/li&gt;
&lt;li&gt;thread끼리는 더 concurrent하게 만들어야 한다. 이는 &lt;b&gt;occupancy&lt;/b&gt;이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Memory Coalescing&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;DRAM Burst&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;561&quot; data-origin-height=&quot;120&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bySXkp/btszk3uUekl/n6UAlhSDY3L8khqIh4H1tk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bySXkp/btszk3uUekl/n6UAlhSDY3L8khqIh4H1tk/img.png&quot; data-alt=&quot;DRAM burst&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bySXkp/btszk3uUekl/n6UAlhSDY3L8khqIh4H1tk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbySXkp%2Fbtszk3uUekl%2Fn6UAlhSDY3L8khqIh4H1tk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;561&quot; height=&quot;120&quot; data-origin-width=&quot;561&quot; data-origin-height=&quot;120&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;DRAM burst&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;DRAM burst는 &lt;b&gt;memory에서 data를 읽거나 쓸 떄 한 번에 연속적인 data 묶음을 사용하는 방법&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;기본적으로 off chip memory, DRAM은 chunk로 접근한다. 만약 하나의 byte에 접근하더라도, 그 byte가 속한 chunk에 있는 모든 byte를 읽어온다. 때문에 chunk 전체를 읽지 않으면 bandwidth가 낭비된다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Memory Coalescing&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;527&quot; data-origin-height=&quot;161&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bre3Ye/btszlwXVPPy/1ivrDk77uYefptHkonxCsk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bre3Ye/btszlwXVPPy/1ivrDk77uYefptHkonxCsk/img.png&quot; data-alt=&quot;coalesced access&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bre3Ye/btszlwXVPPy/1ivrDk77uYefptHkonxCsk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbre3Ye%2FbtszlwXVPPy%2F1ivrDk77uYefptHkonxCsk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;527&quot; height=&quot;161&quot; data-origin-width=&quot;527&quot; data-origin-height=&quot;161&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;coalesced access&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;모든 warp에서 memory operation이 발생하고, warp 내의 32개의 thread가 memory에 접근한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;만약 &lt;b&gt;모든 thread의 memory에 접근하는 위치&lt;/b&gt;가 연속적이고 &lt;b&gt;하나의 burst section에 있는 경&lt;/b&gt;우 하나의 DRAM 요청만 발생하게 되므로, &lt;b&gt;모든 access가 coalescing&lt;/b&gt;(통합)된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위 그림에서는 T0, T1, T2, T3가 memory의 같은 burst에 접근하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;520&quot; data-origin-height=&quot;171&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dEu6S7/btszkCdg346/UXwMYaFpPTHVdkgCJAS7o1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dEu6S7/btszkCdg346/UXwMYaFpPTHVdkgCJAS7o1/img.png&quot; data-alt=&quot;un-coalesced access&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dEu6S7/btszkCdg346/UXwMYaFpPTHVdkgCJAS7o1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdEu6S7%2FbtszkCdg346%2FUXwMYaFpPTHVdkgCJAS7o1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;520&quot; height=&quot;171&quot; data-origin-width=&quot;520&quot; data-origin-height=&quot;171&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;un-coalesced access&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;반면 memory에 접근하는 위치가 1개 이상의 burst인 경우, &lt;b&gt;coalescing이 실패&lt;/b&gt;하므로 &lt;b&gt;여러 개의 DRAM 요청&lt;/b&gt;이 만들어진다. 이렇게 받은 memory의 일부 정보는 thread에서 사용하지만, 몇몇 정보는 thread에서 사용하지 않기 때문에 &lt;b&gt;bandwidth가 낭비&lt;/b&gt;된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위 그림에서는 T0, T1, T2, T3가 2개의 DRAM burst에 접근하고, 이마저도 100% 사용하는 것이 아니기 때문에 bandwidth가 낭비된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Coalesced Access&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;만약 array index가 다음과 같은 형식인 경우, warp의 memory access는 연속적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;`A[X + threadIdx.x]` 또는 `A[X + (blockDim.x*blockIdx.x + threadIdx.x)]`&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;(여기서 X는 X 이외의 항과 독립적이다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;예시 : Output Tiling Matrix Multiplication&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;지지난 포스팅에서 살펴본 output tiling matrix multiplication을 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;408&quot; data-origin-height=&quot;202&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ceLl8g/btszj7xWPoZ/dg0ogJWGGpVEzp4bQKgYkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ceLl8g/btszj7xWPoZ/dg0ogJWGGpVEzp4bQKgYkk/img.png&quot; data-alt=&quot;memory access pattern&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ceLl8g/btszj7xWPoZ/dg0ogJWGGpVEzp4bQKgYkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FceLl8g%2Fbtszj7xWPoZ%2Fdg0ogJWGGpVEzp4bQKgYkk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;408&quot; height=&quot;202&quot; data-origin-width=&quot;408&quot; data-origin-height=&quot;202&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;memory access pattern&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;각 thread는 위 그림처럼 memory에 접근한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgRG3S/btszi6sGHUs/NdP4aNrsnwhwdMuuH2kkw1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgRG3S/btszi6sGHUs/NdP4aNrsnwhwdMuuH2kkw1/img.png&quot; data-origin-width=&quot;537&quot; data-origin-height=&quot;328&quot; data-is-animation=&quot;false&quot; style=&quot;width: 51.7156%; margin-right: 10px;&quot; data-widthpercent=&quot;52.32&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgRG3S/btszi6sGHUs/NdP4aNrsnwhwdMuuH2kkw1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbgRG3S%2Fbtszi6sGHUs%2FNdP4aNrsnwhwdMuuH2kkw1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;537&quot; height=&quot;328&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xVO5E/btszkbAdIG6/GgwUDMvFohjC3AkXS8mCSK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xVO5E/btszkbAdIG6/GgwUDMvFohjC3AkXS8mCSK/img.png&quot; data-origin-width=&quot;543&quot; data-origin-height=&quot;364&quot; data-is-animation=&quot;false&quot; style=&quot;width: 47.1216%;&quot; data-widthpercent=&quot;47.68&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xVO5E/btszkbAdIG6/GgwUDMvFohjC3AkXS8mCSK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxVO5E%2FbtszkbAdIG6%2FGgwUDMvFohjC3AkXS8mCSK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;543&quot; height=&quot;364&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;memory N, M의 access pattern&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;왼쪽 그림은 matrix N에, 오른쪽 그림은 matrix M에 해당하는 memory에 접근하는 방식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그림에서 알 수 있듯 N의 경우 memory access가 coalescing되어 있다. 접근이 연속적이기 때문이다.&amp;nbsp;반면 M은 coalescing되어 있지 않다. 연속적인 memory에 접근하지 않기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;예시 : Input Tiling Matrix Multiplication&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;607&quot; data-origin-height=&quot;188&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bIpqzp/btszlY7I0vF/TPKAHcXvKeATPR0gdxsPh0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bIpqzp/btszlY7I0vF/TPKAHcXvKeATPR0gdxsPh0/img.png&quot; data-alt=&quot;tiling matrix multiplication에서 memory access pattern&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bIpqzp/btszlY7I0vF/TPKAHcXvKeATPR0gdxsPh0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbIpqzp%2FbtszlY7I0vF%2FTPKAHcXvKeATPR0gdxsPh0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;607&quot; height=&quot;188&quot; data-origin-width=&quot;607&quot; data-origin-height=&quot;188&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;tiling matrix multiplication에서 memory access pattern&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;반면 tiling을 한 경우 memory coalescing이 일어난다. 연속된 memory를 shared memory로 읽어오기 때문이다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;예시 : strided access&lt;/h4&gt;
&lt;pre id=&quot;code_1698421152431&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;__global__ void foo (int* input, float3* input2)
{
    int i = blockDim.x * blockIdx.x + threadIdx.x;
    
    // Stride 1
    int a = input[i];
    
    // Stride 2, half the bandwidth is wasted
    int b = input[2*i];
    
    // Stride 3, 2/3 of the bandwidth wasted
    float c = input2[i].x;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 위 코드를 보자.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;stride 1의 경우 access pattern이 i이다.&lt;/li&gt;
&lt;li&gt;stride 2의 경우 access pattern이 2i이므로 bandwidth의 50%가 낭비된다.&lt;/li&gt;
&lt;li&gt;stride 3의 경우, input2는 float3이므로 bandwidth의 2/3이 낭비된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;예시 : Structure의 Array와 Array의 Structure&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;C를 공부했다면 알만한 내용이다.&lt;/p&gt;
&lt;pre id=&quot;code_1698421245769&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;struct AoS
{
    int key;
    int value;
    int flag;
};
AoS *d_AoS_data;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;309&quot; data-origin-height=&quot;45&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjbyhm/btszlYGEaIA/VwXK5b6VRDbB0kd7tTglK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjbyhm/btszlYGEaIA/VwXK5b6VRDbB0kd7tTglK1/img.png&quot; data-alt=&quot;structure의 array&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjbyhm/btszlYGEaIA/VwXK5b6VRDbB0kd7tTglK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbjbyhm%2FbtszlYGEaIA%2FVwXK5b6VRDbB0kd7tTglK1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;309&quot; height=&quot;45&quot; data-origin-width=&quot;309&quot; data-origin-height=&quot;45&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;structure의 array&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1698421264805&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;struct SoA
{
    int * keys;
    int * values;
    int * flags;
};
SoA *d_SoA_data;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;45&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MtuZd/btszltmyVjn/pzqfyAcGsiwlsBcRJwBMoK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MtuZd/btszltmyVjn/pzqfyAcGsiwlsBcRJwBMoK/img.png&quot; data-alt=&quot;array의 structure&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MtuZd/btszltmyVjn/pzqfyAcGsiwlsBcRJwBMoK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMtuZd%2FbtszltmyVjn%2FpzqfyAcGsiwlsBcRJwBMoK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;300&quot; height=&quot;45&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;45&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;array의 structure&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1698421321587&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;__global__ void bar(AoS *d_AoS_data, SoA *d_SoA_data)
{
    int i = blockDim.x * blockIdx.x + threadIdx.x
    
    // AoS wastes bandwidth
    int key_aos = d_AoS_data[i].key;
    
    // SoA efficient use of bandwidth
    int key_soa = d_SoA_data-&amp;gt;keys[i];
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 경우 memory bandwidth는 어떨까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;물론 코드 짜기 나름이겠지만, 위 코드의 경우 array of structure는 bandwidth의 2/3을 낭비한다. structure of array는 100% 쓰고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;요약&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;각 warp에서 100% address coalescing을 이루면 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이를 위해서 시작 주소를 정렬해서 쓰거나(padding이 필요할 수도 있다.), &lt;b&gt;warp는 연속적인 memory에 access하는 것이 이상적&lt;/b&gt;이다. thread 사이에 큰 stride가 있거나, access 간격이 큰 것은 별로다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이를 위해 address access pattern을 분석하고 최적화해야 한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;request당 memory transaction 회수 분석&lt;/li&gt;
&lt;li&gt;array of structure 대신 structure of array 사용&lt;/li&gt;
&lt;li&gt;read only data는 read only variable을 사용하는 것이 좋다.&lt;/li&gt;
&lt;li&gt;가능하면 shared memory를 쓰는 것이 좋다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Shared Memory Bank Conflict&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;b&gt;shared memory&lt;/b&gt;는 &lt;b&gt;thread block 내의 thread의 communication&lt;/b&gt;과 &lt;b&gt;global memory access 회수를 줄이기 위해&lt;/b&gt; 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이런 shared memory는 &lt;b&gt;4byte로 이뤄진 32개의 bank&lt;/b&gt;로 이뤄져 있으며, 서로 다른 bank를 통해 연속적인 word에 접근할 수 있다. (bank level parallelism)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;성능의 경우,&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;shared memory access는 warp별로 만들어진다.&lt;/li&gt;
&lt;li&gt;각 SM의 clock당, bank의 bandwidth는 4byte이다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;serialization&lt;/b&gt; : N개의 thread가 하나의 bank 안에 있는 32개의 다른 word에 접근한다면, N개의 access는 순서대로 실행된다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;mutlicast&lt;/b&gt; : N thread는 하나의 fetch로 같은 word에 접근할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;b&gt;각 bank는 모두 independent&lt;/b&gt;하다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;optimal&lt;/b&gt; : &lt;b&gt;모든 thread가 다른 bank에 접근하는 경우 optimal&lt;/b&gt;하며, multicast할 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;worst&lt;/b&gt; : &lt;b&gt;2개 이상의 thread가 같은 bank에 접근하는 경우 bank conflict&lt;/b&gt;가 발생한다. 이 경우 serialization이 발생하기 때문에 효율이 떨어진다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Bank Conflict 회피&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;예시 : Matrix Transpose&lt;/h4&gt;
&lt;pre id=&quot;code_1698423056624&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;__global__ transpose(float in[], float out[])
{
    __shared__ float tile[TILE][TILE];
    int glob_in = xIndex + yIndex*N;
    int glob_out = xIndex + yIndex*N;
    tile[threadIndx.y][threadIndx.x] = in[global_in];
    __sync_threads();
    out[glob_out] = tile[threadIdx.x][threadIndx.y];
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위 코드를 실행시킬 때&amp;nbsp;shared memory에 32 by 32의 array가 있다고 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;799&quot; data-origin-height=&quot;232&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbv7qi/btszk6Zs94a/x7ATa4ufyZYqwaZSLhaVR0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbv7qi/btszk6Zs94a/x7ATa4ufyZYqwaZSLhaVR0/img.png&quot; data-alt=&quot;bank conflict 예시&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbv7qi/btszk6Zs94a/x7ATa4ufyZYqwaZSLhaVR0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbbv7qi%2Fbtszk6Zs94a%2Fx7ATa4ufyZYqwaZSLhaVR0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;799&quot; height=&quot;232&quot; data-origin-width=&quot;799&quot; data-origin-height=&quot;232&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;bank conflict 예시&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;read에 대해서는 stride 1이기 때문에 coalescing이 발생한다. 때문에 가운데 그림처럼 bank 0에는 0번째 column이, bank 1에는 1번째 column이, ... 들어간다. 이 경우는 괜찮다! 모든 thread가 다른 bank에 접근하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;반면 bank에 값을 쓰는 상황을 보자. thread 0은 bank 0의 0번째에 값을 쓰고, thread 1은 bank 0의 1번째에, ... thread k는 bank 0의 k번째에 값을 쓴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 경우&lt;b&gt; bank conflict&lt;/b&gt;가 일어나며 이 요청은 serialize되기에 성능이 대폭 떨어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;390&quot; data-origin-height=&quot;228&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CJ2Qq/btszhuggr2A/AE9fHp0m9LKGVXApBqyyVK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CJ2Qq/btszhuggr2A/AE9fHp0m9LKGVXApBqyyVK/img.png&quot; data-alt=&quot;bank conflict 회피 - padding 추가&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CJ2Qq/btszhuggr2A/AE9fHp0m9LKGVXApBqyyVK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCJ2Qq%2Fbtszhuggr2A%2FAE9fHp0m9LKGVXApBqyyVK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;390&quot; height=&quot;228&quot; data-origin-width=&quot;390&quot; data-origin-height=&quot;228&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;bank conflict 회피 - padding 추가&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이를 막기 위해 사용하지 않는 column 하나를 덧붙이는 trick이 있다. 기존에 column 0에 해당하는 것은 모두 bank 0에 들어갔는데, 이렇게 바꾸면 column 0에 모든 종류의 bank가 들어가게 되어 bank conflict가 줄어든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Occupancy&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Review : Thread Scheduling&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;SM은 overhead가 없는 warp scheduling을 했다. SM은 언제든 준비된 warp를 실행하고, 이 때 context swtiching cost가 없었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;만약 모든 warp가 정지되어 실행할 warp가 없는 경우, 실행할 instruction이 없어 성능이 떨어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그러면 왜 멈출까? 아래와 같은 이유가 있다. 두 경우 모두 active warp로 switch해서 latency를 숨겨야 하는데, 그러지 못하는 경우다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;global memory access 대기&lt;/li&gt;
&lt;li&gt;compute unit를 대기&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Occupancy&lt;/h3&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;Occupancy = $\frac{\text{SM에서 활성화된 thread의 개수}}{\text{SM의 thread 개수}}$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Occupancy, 점유율의 정의는 위와 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;occupancy가 높을수록 latency를 숨기는 데 도움이 된다. 당연하다! 실행할 수 있는 warp가 있기 때문에 그동안 idling하지 않는다.&lt;/li&gt;
&lt;li&gt;달성된 occupancy vs 이론적 occupancy
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;compute unit을 포화시키기 위해, 모든 SM을 채울 수 있는 충분한 thread block을 실행해야 한다.&lt;/li&gt;
&lt;li&gt;memory bandwidth를 포화시키기 위해 concurrent memory request를 가진 충분한 thread block을 실행해야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Occupancy와 성능&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;최대 성능을 위해 100% occupancy가 필요하지는 않다. &lt;b&gt;특정 occupancy&lt;/b&gt;에 도달하면, 더 늘려도 성능이 향상되지 않는다는 말이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 [특정 occupancy]는 코드에 따라 다르다. 더 independen할수록 더 적은 occupancy가 필요하다. 일반적으로 memory에 의존하는 코드는 latency가 더 많기에 더 많은 occupancy가 필요한 경향이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Resource Limit&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;각 SM에서 각 thread는 register, shared memory를 공유한다. 또한 resource는 한계가 있기 때문에&amp;nbsp;hardware scheduler는 SM에 맞는 thread의 개수를 결정한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;thread당 register&lt;/b&gt; : SM register는 thread에 나눠진다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;thread block당 shared memory&lt;/b&gt; : SM shared memory는 thrread block에 나눠진다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;SM당 thread block&lt;/b&gt;&amp;nbsp;: thread는 thread block granularity에 따라 나눠진다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;SM당 thread&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;예시&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;531&quot; data-origin-height=&quot;262&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tLXta/btszj4HY9Le/T4DCNh2Hek3y4gRx5KFKAK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tLXta/btszj4HY9Le/T4DCNh2Hek3y4gRx5KFKAK/img.png&quot; data-alt=&quot;resource의 한계&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tLXta/btszj4HY9Le/T4DCNh2Hek3y4gRx5KFKAK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtLXta%2Fbtszj4HY9Le%2FT4DCNh2Hek3y4gRx5KFKAK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;531&quot; height=&quot;262&quot; data-origin-width=&quot;531&quot; data-origin-height=&quot;262&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;resource의 한계&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위 그림을 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;왼쪽의 경우, thread block 3개가 register와 shared memory를 나눠가진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;반면 오른쪽의 경우 shared memory가 부족해 thread block 0, 1이 대부분의 shared memory를 점유하고 있다. 때문에 thread block 3가 활성화되지 못했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Thread Block Sizing&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/H76m0/btszlolo4Em/Zu2L7SDDXE4o2hOzY06Bzk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/H76m0/btszlolo4Em/Zu2L7SDDXE4o2hOzY06Bzk/img.png&quot; data-origin-width=&quot;357&quot; data-origin-height=&quot;135&quot; data-is-animation=&quot;false&quot; style=&quot;width: 42.2532%; margin-right: 10px;&quot; data-widthpercent=&quot;42.75&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/H76m0/btszlolo4Em/Zu2L7SDDXE4o2hOzY06Bzk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FH76m0%2Fbtszlolo4Em%2FZu2L7SDDXE4o2hOzY06Bzk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;357&quot; height=&quot;135&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cPfBZG/btszmxvn2cA/O1uhumpjKYTTKOcAiPeKLK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cPfBZG/btszmxvn2cA/O1uhumpjKYTTKOcAiPeKLK/img.png&quot; data-origin-width=&quot;471&quot; data-origin-height=&quot;133&quot; data-is-animation=&quot;false&quot; style=&quot;width: 56.584%;&quot; data-widthpercent=&quot;57.25&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cPfBZG/btszmxvn2cA/O1uhumpjKYTTKOcAiPeKLK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcPfBZG%2Fbtszmxvn2cA%2FO1uhumpjKYTTKOcAiPeKLK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;471&quot; height=&quot;133&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;thread block sizing&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위 예시에서 볼 수 있듯 thread block의 개수와 thread의 개수는 thread block size과 관련이 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;위 그림의 왼쪽 부분. thread block이 너무 작으면 안 된다. SM이 occupancy의 임계점에 다다르기 전에 thread block의 한도에 다다르기 때문이다.&lt;/li&gt;
&lt;li&gt;위 그림의 오른쪽 부분. thread block이 너무 커서도 안 된다. SM이 occupancy의 임계점에 다다르기 전에 thread 한도에 다다르기 때문이다. thread에 대해서는 resource가 충분하지만 thread block에 대해서 충분하지 않을 수도 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Occupancy Guideline&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;thread block 크기의 경우 (thread block당 thread의 개수)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각 thread block당 128 - 256개로 먼저 시작하고, 기능에 따라 조정하면 된다.&lt;/li&gt;
&lt;li&gt;warp size인 32에 배수인 것이 좋다.&lt;/li&gt;
&lt;li&gt;occupancy가 성능에 중요한 영향을 미치는 경우, thread block size가 register나 shared memory resource에 영향을 미치는지 확인해야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;grid size (grid당 thread block 개수)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;1000개 이상의 thread block이 있는 것이 좋다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Thread Granularity&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;b&gt;thread가 얼마나 많은 일을 하게 둘지에 대한 지표&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;일반적으로 independent한 thread가 더 많을수록 occupancy를 높일 수 있고, parallel하게 처리할 수 있다. 그러나 thread간 중복된 작업이 있는 경우 compute unit에서 thread가 정지될 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SM은 floating point, load나 branch instruction에 대해 제한된 bandwidth를 가지고 있다. 때문에 중복 작업을 없애는 것이 좋다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;예시 : Tiling Matrix Multiplication&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;433&quot; data-origin-height=&quot;384&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2zd3q/btszkbAeiZ0/oSKSto85cKEKETCIJd1Ke0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2zd3q/btszkbAeiZ0/oSKSto85cKEKETCIJd1Ke0/img.png&quot; data-alt=&quot;tiling matrix multiplication&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2zd3q/btszkbAeiZ0/oSKSto85cKEKETCIJd1Ke0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2zd3q%2FbtszkbAeiZ0%2FoSKSto85cKEKETCIJd1Ke0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;433&quot; height=&quot;384&quot; data-origin-width=&quot;433&quot; data-origin-height=&quot;384&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;tiling matrix multiplication&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 경우 각 M, N tile을 가져오는 데 중복이 있다. 이 경우 2개의 thread block을 하나로 합치면 global memory access를 줄 일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;단, 이 경우 active thread의 개수가 줄어들어 SM resource에 대한 압박이 증가할 수 있고, 총 thread block의 개수가 줄어들어 parallelism에 문제가 생길 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;요약&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;성능에 영향을 미치는 병목을 찾아내고 이를 해결해야 한다. 만약 병목을 찾아낸 경우, tuning을 적용해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; sequential code를 parallelize하는 방법도 고려해야 한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;kernel launch를 조절해 occupancy 높이기&lt;/li&gt;
&lt;li&gt;global memory access coalescing 을 사용해 memory bandwidth 효율 높이기&lt;/li&gt;
&lt;li&gt;shared memory &amp;amp; shared memory bank conflict 회피를 사용해 global memory에 대한 중복 접근을 없애기&lt;/li&gt;
&lt;li&gt;같은 warp 내에서 다른 execution path를 줄여 control divergence 없애기&lt;/li&gt;
&lt;li&gt;stream, unified memory를 사용해 host - device data 전송 최소화 또는 숨기기&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;잘못된 내용이나 오탈자에 대한 지적, 질문&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;b&gt;등은 언제나 환영합니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>CS/Parallel Computing</category>
      <author>hyelie</author>
      <guid isPermaLink="true">https://hyelie.tistory.com/673</guid>
      <comments>https://hyelie.tistory.com/entry/%EC%9D%B4%EC%A2%85%EB%B3%91%EB%A0%AC%EC%BB%B4%ED%93%A8%ED%8C%85-Performance-Considerations#entry673comment</comments>
      <pubDate>Sat, 28 Oct 2023 02:03:13 +0900</pubDate>
    </item>
    <item>
      <title>[이종병렬컴퓨팅] Thread Execution Efficiency</title>
      <link>https://hyelie.tistory.com/entry/%EC%9D%B4%EC%A2%85%EB%B3%91%EB%A0%AC%EC%BB%B4%ED%93%A8%ED%8C%85-Thread-Execution-Efficiency</link>
      <description>&lt;blockquote style=&quot;text-align: left;&quot; data-ke-style=&quot;style2&quot;&gt;이 글은 포스텍 성효진 교수님의 이종병렬컴퓨팅(CSED490C) 강의를 기반으로 재구성한 것입니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 글에서는 다음과 같은 내용들을 살핀다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SIMD hardware에서 GPU thread가 실행되는 방식 : warp partitioning, control divergence&lt;/li&gt;
&lt;li&gt;control divergence가 성능에 미치는 영향을 분석하는 방법 : boundary condition checking&lt;/li&gt;
&lt;li&gt;GPU thread execution을 겹치는 방법 : CUDA stream&lt;/li&gt;
&lt;li&gt;GPU의 synchronization primitive 동작 : warp synchronization, atomics&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;SIMD hardware에서 GPU thread가 실행되는 방식&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Scheduling Thread Blocks&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;hardware는 thread block을 가능한 processor에게 보내며, 어떤 SM이 어떤 thread block을 실행할지 결정한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;GPU는 많은 processor(SM)들이 있다. 보통 16개 ~ 132개 정도가 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;각 processor는 여러 thread block을 concurrent하게 실행할 수 있다&lt;/b&gt;. 때문에 kernel launch가 충분한 thread block을 생성해서 &lt;b&gt;busy한 상태를 유지&lt;/b&gt;하게 하는지 살펴야 한다. (이에는 memory latency를 숨기는 효과도 있다.)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;thread block의 개수가 SM의 개수보다 작으면 SM을 모두 사용하지 못하는 것이고, 즉슨 성능을 100% 활용하지 못하는 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;hardware는 resource가 사용가능할 때 thread block을 실행한다. 때문에 &lt;b&gt;thread block 간의 순서가 보장되지 않으므로&lt;/b&gt; 이에 알고리즘이 thread block의 실행 순서에 영향을 받지 않게 설계해야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Scheduling 단위로써 Warp&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;646&quot; data-origin-height=&quot;162&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AnPVU/btszfJXR2Lr/nMgDbCWb9BG4OzElTtdR6K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AnPVU/btszfJXR2Lr/nMgDbCWb9BG4OzElTtdR6K/img.png&quot; data-alt=&quot;warps as scheduling units&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AnPVU/btszfJXR2Lr/nMgDbCWb9BG4OzElTtdR6K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAnPVU%2FbtszfJXR2Lr%2FnMgDbCWb9BG4OzElTtdR6K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;646&quot; height=&quot;162&quot; data-origin-width=&quot;646&quot; data-origin-height=&quot;162&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;warps as scheduling units&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각 &lt;b&gt;thread block은 하나 이상의 warp에 mapping&lt;/b&gt;된다.&lt;/li&gt;
&lt;li&gt;hardware scheduler는 &lt;b&gt;각 warp를 independent하게 scheduling&lt;/b&gt;한다. 즉, &lt;b&gt;warp가 scheduling의 단위&lt;/b&gt;이다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;warp 안의 thread는 SIMD 방식으로 함께 수행된다.&lt;/li&gt;
&lt;li&gt;thread block의 다른 warp는 independent하게 실행된다. 예를 들어 warp size가 32인 경우, thread block 크기가 32보다 크면 thread block을 실행하는 warp는 2개 이상으로 나뉘며, 이 warp들은 independent하게 실행된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;warp의 크기나 execution/communication model은 버전/제조사에 따라 다르다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;N차원 Thread Block에서 Warp&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;678&quot; data-origin-height=&quot;329&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yaBMM/btszbUfkY6O/yCRVyVlmo9OJRphwDBbiQK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yaBMM/btszbUfkY6O/yCRVyVlmo9OJRphwDBbiQK/img.png&quot; data-alt=&quot;thread block linearlize&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yaBMM/btszbUfkY6O/yCRVyVlmo9OJRphwDBbiQK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyaBMM%2FbtszbUfkY6O%2FyCRVyVlmo9OJRphwDBbiQK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;678&quot; height=&quot;329&quot; data-origin-width=&quot;678&quot; data-origin-height=&quot;329&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;thread block linearlize&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;n-D 형태의 thread block은 row-major 순서로 1D로 linearize되며, x, y, z축 순서이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;954&quot; data-origin-height=&quot;133&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ubgcx/btszhrvpsPL/gjms7lugjrxeeYuDYvudFK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ubgcx/btszhrvpsPL/gjms7lugjrxeeYuDYvudFK/img.png&quot; data-alt=&quot;multi dimensional thread block 안의 warp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ubgcx/btszhrvpsPL/gjms7lugjrxeeYuDYvudFK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fubgcx%2FbtszhrvpsPL%2Fgjms7lugjrxeeYuDYvudFK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;954&quot; height=&quot;133&quot; data-origin-width=&quot;954&quot; data-origin-height=&quot;133&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;multi dimensional thread block 안의 warp&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이렇게 나누는 이유는 thread block을 warp로 나누기 위함이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;1D로 바뀐 thread block은 warp로 나뉘며, warp 내의 thread index는 연속적이며 증가한다.&lt;/li&gt;
&lt;li&gt;partitioning하는 방식은 모든 기기에서 동일하므로 control flow에 사용할 수 있다. CUDA의 경우 어떤 warp에 어떤 thread를 넣을지 explicit하게 설정할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위 예시에서는 4 by 6 by 2의 형태인데, 여기서 warp를 할당하기 위해 1D로 펼치고 32개의 thread를 하나의 warp에 넣는다. 0부터 31까지는 warp 0에, 32부터 64까지는 warp 1에, ... 넣는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Warp 채우기&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt; thread block은 warp를 가득 채울 수 있는 것&lt;/b&gt;이 좋다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예를 들어 thread block size가 1이면 1개의 warp에 할당되지만 나머지 부분이 비어서 별로다.&lt;/li&gt;
&lt;li&gt;32면 1개의 warp에 할당되지만 warp의 빈 공간이 적어서 좋다.&lt;/li&gt;
&lt;li&gt;128이면 4개의 warp를 채울 수 있어서 더 좋다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;hardware가 warp를 전환할 수 있게 하려면&amp;nbsp;thread block당 충분한 thread가 존재해야 한다. thread block이 warp에 매핑되기 때문이며, &lt;b&gt;warp가 여러 개 있어야 memory access latency를 숨길 수 있기 때문&lt;/b&gt;이다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;scratchpad memory&lt;/b&gt;와 같은 resource는 &lt;b&gt;thread block당 thread 개수를 제한&lt;/b&gt;할 수도 있다. 앞선 글에서 살펴본 것처럼 한 thread block에 너무 많은 thread가 할당되면 하나의 SM에서 실행할 수 있는 thread의 개수가 낭비되기 때문이다.&lt;/li&gt;
&lt;li&gt;thread의 개수가 thread block의 개수의 배수가 아니면 kernel 내부에 &lt;b&gt;boundary test&lt;/b&gt;를 삽입해야 한다. 앞서 살펴본 if문과 동일하다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;boundary test가 없는 경우 thread blodk이 array의 index 외부에 접근할 수 있기 때문이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Control Divergence&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;warp의 모든 thread는 동일한 instruction을 실행해야 한다. &lt;span style=&quot;letter-spacing: 0px;&quot;&gt;때문에, 모든 thread가 같은 control flow path를 따르는 경우 제일 효율적이다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예를 들어 모든 if-then-else는 같은 결정을 내리며, 모든 loop는 같은 회수만큼 반복된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;만약 &lt;b&gt;warp의 thread가 branch로 인해 서로 다른 작업을 수행&lt;/b&gt;하면 어떻게 될까? 이게 &lt;b&gt;control divergence&lt;/b&gt;이다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;이 내용은 &lt;a href=&quot;https://hyelie.tistory.com/entry/%EC%9D%B4%EC%A2%85%EB%B3%91%EB%A0%AC%EC%BB%B4%ED%93%A8%ED%8C%85-GPU-architectures-SIMT%EC%99%80-SIMD&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;SIMT와 SIMD 포스팅&lt;/a&gt;에서 잠깐 다뤘었다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;control divergence는 서로 다른 control decision이 발생해 서로 다른 control flow path로 갈 때 발생한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예를 들어, if-then-else에서 몇몇은 then으로, 몇몇은 else로 가는 경우이다.&lt;/li&gt;
&lt;li&gt;다른 예로 몇몇 thread가 loop 회수가 다를 수도 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 때, 서로 다른 path를 사용하는 경우, GPU 내부에서 &lt;b&gt;serialize&lt;/b&gt;된다. 모든 warp의 thread는 같은 instruction을 실행해야 하기 때문이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;warp의 thread가 선택한 control path는 더 이상 다른 path를 가진 thread가 없을 때까지 한 번에 하나의 path에 해당하는 thread를 실행한다. 돌지 않는 thread는 idling한다.&lt;/li&gt;
&lt;li&gt;각 path를 실행하는 동안 모든 thread는 parallel하게 실행되며, 해당 path를 사용하지 않는 모든 thread는 mask된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;예시&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HwOq3/btszf2Qpwvz/Gb0ArmXScazIKcndVr2vu0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HwOq3/btszf2Qpwvz/Gb0ArmXScazIKcndVr2vu0/img.png&quot; data-origin-width=&quot;471&quot; data-origin-height=&quot;105&quot; data-is-animation=&quot;false&quot; style=&quot;width: 74.1844%; margin-right: 10px;&quot; data-widthpercent=&quot;75.06&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HwOq3/btszf2Qpwvz/Gb0ArmXScazIKcndVr2vu0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHwOq3%2Fbtszf2Qpwvz%2FGb0ArmXScazIKcndVr2vu0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;471&quot; height=&quot;105&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BdW1c/btszfJXR9WS/wQghDrecSuvASp7VDB7wZ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BdW1c/btszfJXR9WS/wQghDrecSuvASp7VDB7wZ0/img.png&quot; data-origin-width=&quot;240&quot; data-origin-height=&quot;161&quot; data-is-animation=&quot;false&quot; style=&quot;width: 24.6528%;&quot; data-widthpercent=&quot;24.94&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BdW1c/btszfJXR9WS/wQghDrecSuvASp7VDB7wZ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBdW1c%2FbtszfJXR9WS%2FwQghDrecSuvASp7VDB7wZ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;240&quot; height=&quot;161&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;control divergence 예시&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위와 같은 상황을 고려해 보자. 한 warp에는 32개의 thread가 들어간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;517&quot; data-origin-height=&quot;267&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oJMf6/btszb9i8RSz/vDsWGCoSg0AGmfkmFNhl9k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oJMf6/btszb9i8RSz/vDsWGCoSg0AGmfkmFNhl9k/img.png&quot; data-alt=&quot;control divergence 실행 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oJMf6/btszb9i8RSz/vDsWGCoSg0AGmfkmFNhl9k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoJMf6%2Fbtszb9i8RSz%2FvDsWGCoSg0AGmfkmFNhl9k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;517&quot; height=&quot;267&quot; data-origin-width=&quot;517&quot; data-origin-height=&quot;267&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;control divergence 실행 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;파란색 warp 1에 속한 thread들은 모두 같은 control decision을 내리기 때문에 divergence가 없다.&lt;/li&gt;
&lt;li&gt;빨간색 warp 2에 속한 thread들의 경우 일부 thread는 threadIdx.y가 0이고, 일부 thread는 아니다. 때문에 [B에 해당하는 control decision을 내린 thread들]과 [C에 해당하는 control decision들을 내린 thread들]을 serialize해서 실행한다. 이후 D는 모든 thread가 같은 control decision을 내렸기 때문에 모두 같이 실행된 모습이다. 이 경우 divergence이다.&lt;/li&gt;
&lt;li&gt;초록색 warp 3에 속한 thread들의 경우 thread block이 warp size를 삐져나갔다. 때문에 warp 내에 있는 thread들만 활성화된 모습을 볼 수 있다. 이 경우 divergence는 아니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Nested Control Divergence&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;835&quot; data-origin-height=&quot;337&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9Dlhf/btszeLaAi00/Nq1sUebNFkpt6yND6dTRKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9Dlhf/btszeLaAi00/Nq1sUebNFkpt6yND6dTRKK/img.png&quot; data-alt=&quot;nested control divergence&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9Dlhf/btszeLaAi00/Nq1sUebNFkpt6yND6dTRKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9Dlhf%2FbtszeLaAi00%2FNq1sUebNFkpt6yND6dTRKK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;753&quot; height=&quot;304&quot; data-origin-width=&quot;835&quot; data-origin-height=&quot;337&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;nested control divergence&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위 그림처럼 nested branch가 있는 경우, 모든 branch가 serialize되므로 divergence가 더 심해진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Divergent Iteration&lt;/h4&gt;
&lt;pre id=&quot;code_1698336512277&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;__global__ void per_thread_sum(int *indices, float *data, float *sums)
{
    ...
    // number of loop iterations is data dependent
    for(int j=indices[i]; j&amp;lt;indices[i+1]; j++)
    {
        sum += data[j];
    }
    sums[i] = sum;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;nested loop 이외에도 나쁜 divergent의 예시이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위와 같은 kernel code를 실행한다고 하자. 이 경우 실행 회수가 모두 다르기 때문에 thread 하나가 전체 warp의 실행 시간을 결정할 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Control Divergence가 성능에 미치는 영향&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;divergence는 프로그램의 정확성에 영향을 주지 않기 때문에,&amp;nbsp;프로그램을 짤 때 divergence에 대해 correctness를 고려할 필요가 없다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;control divergence가 있는 코드가 synchronization을 할 때 deadlock이 발생하기도 하지만, 매우 드물다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그렇지만 일반적으로 divergence가 많아질수록 성능이 떨어지기 때문에, performance를 분석하고 &lt;b&gt;divergence를 줄일 수 있도록&lt;/b&gt; 해야 한다. 1개 정도는 괜찮지만, nested branch는 지양해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;예시 : control divergence가 성능에 미치는 영향&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;array size가 1000이고, thread block당 256개의 thread가 있고, 각 thread block당 8개의 warp가 있다고 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;thread block 0, 1, 2에 해당하는 thread들은 0부터 767이며, 총 24개의 warp가 있다. 이들은 control divergence가 없다.&lt;/li&gt;
&lt;li&gt;thread block 3에서 control divergence가 발생한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;warp 0 ~ 6 (thread 0 ~ 223)까지는 1000 안에 들어오므로 control divergence가 없다.&lt;/li&gt;
&lt;li&gt;warp 7의 992 ~ 999에 해당하는 thread들은 범위 내에 있다.&lt;/li&gt;
&lt;li&gt;warp 7의 1000 ~ 1023에 해당하는 thread들은 범위 밖에 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;이 경우 32개의 warp 중 1개의 warp에 control divergence가 발생하기 때문에 성능에 미치는 영향은 약 3%일 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이처럼 큰 input data에 대해서는 boundary test로 인한 영향이 적어야 한다. &lt;span style=&quot;letter-spacing: 0px;&quot;&gt;그렇지만 기능이 올바르게 동작하는 것을 보장하기 위해서는 boundary test를 사용해야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Addressing Control Divergence Guideline&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;control divergence로 인해 parallel의 효율이 매우 크게 저하될 수 있다. worst case, 한 thread만이 매우 복잡한 branch를 따르고 나머지 32개의 thread는 그렇지 않다면 32배 성능 손실을 본다.&lt;/li&gt;
&lt;li&gt;warp 내부에서 divergence를 피해야 한다. 다른 warp는 다른 code를 실행할 수 있으므로 성능에 영향을 주지 않는다.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre id=&quot;code_1698337135882&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// divergence가 발생하는 예시
// branch granularity &amp;lt; warp size
if (threadIdx.x &amp;gt; 2) {...}
else {...}

// divergence가 발생하지 않는 예시
// branch granularity is a whole multiple of warp size
if (threadIdx.x / WARP_SIZE &amp;gt; 2) {...}
else {...}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위 예시와 같은 방법을 쓸 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위 코드는 무조건 divergence가 발생한다. 반면 아래 코드의 경우, 특정 warp에 대해서만 if문이 걸리기 때문에 divergence가 발생하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;cost를 고려해서 boundary test를 해야 한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;만약 boundary test cost가 낮다면 branch를 써도 된다.&lt;/li&gt;
&lt;li&gt;반면 boundary test cost가 높다면 여러 개의 kernel을 사용하는 것이 좋다. 예를 들어 하나는 범위 내의 것, 하나는 범위 밖의 것으로.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;kernel specialization : cost가 매우 크게 드는 일부 목록을 분리하는 방법이다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Concurrency With Stream&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Synchronicity&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;폰 노이만 모델은 계산 단계가 synchronous이다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;실제로는 false인 경우가 대부분이다. compiler나 out of order reorder나 pipelined CPU가 instruction을 중복한다. 이처럼 실제 동작은 다르다.&lt;/li&gt;
&lt;li&gt;그러나 여전히 &lt;b&gt;abstract level을 보는 programmer에게는 synchronous한 실행으로 보인다&lt;/b&gt;.&lt;/li&gt;
&lt;li&gt;만약 program level에서 asynchronous하다면 프로그래머는 어떤 일이 어떤 순서로 일어나는지 면밀히 관심을 가져야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;GPU의 경우 &lt;b&gt;host와 kernel&lt;/b&gt; code의 synchronicity와, &lt;b&gt;GPU stream&lt;/b&gt;들끼리의 synchronicity를 고려해야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;GPU Kernel Code에서 Synchronicity&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;하나의 warp에서 코드는 synchronous&lt;/b&gt;하게 실행된다. 즉 모든 instruction은 이전 instruction이 끝날 때까지 기다린다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;다른 warp들은 랜덤하게 overlap&lt;/b&gt;된다. 이는 `__syncthreads()`를 호출해 올바른 동작을 유도할 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;다른 thread block들은 랜덤하게 overlap&lt;/b&gt;된다. 이들은 어떻게 synchronous하게 동작하게 유도할까? 가장 쉬운 방법은 작업이 끝나면 kernel을 종료하는 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;CUDA Host Code에서 Synchronicity&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모든 CUDA 호출은 host에 대해 synchronous 또는 asynchronous이다
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;synchronous의 경우 : 작업을 대기열에 추가하고 끝날 때까지 기다린다.&lt;/li&gt;
&lt;li&gt;asynchronous의 경우 : 작업을 대기열에 추가하고 즉시 리턴한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Default API의 경우
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;CUDA kernel launch는 CPU와 asychronous&lt;/b&gt;이다. (non-blocking) host에서 kernel call을 해도 kernel이 끝날 때까지 다음 instruction을 실행하는 것을 보류하지 않는다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;대부분의 CUDA call은 synchronous / blocking&lt;/b&gt;이다. 예를 들어 `cudaMemcpy()`의 경우 복사가 끝날 때까지 기다린다.&lt;/li&gt;
&lt;li&gt;asynchronous한 버전의 API도 존재한다. 예를 들어 `cudaMemcpyAsync()`는 CPU와 asynchronous하다.&lt;/li&gt;
&lt;li&gt;asynchronous한 API를 호출한 경우, `cudaDeviceSynchronize()`를 호출해 완료를 기다리거나 `cudaMemcpy()`를 호출해야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;CUDA Streams&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;CUDA Streams는 &lt;b&gt;GPU에서 만들어진 순서대로 실행되는 operation sequence&lt;/b&gt;. host는 작업을 queue에 넣고, device는 resource를 쓸 수 있을 때 stream에 작업을 넣는다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;stream을 사용해 여러 개의 CUDA operation을 동시에 할 수 있다. 때문에 concurrency와 pipelining을 실현하는 programming model이다.&lt;/li&gt;
&lt;li&gt;종류는 크게 2가지, kernel launch와 data transfer이다.&lt;/li&gt;
&lt;li&gt;또한 CPU에서 GPU로, GPU에서 CPU로 data를 옮기는 communication을 숨길 수 있다!&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;예시&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;818&quot; data-origin-height=&quot;352&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cjdwdC/btszcGOxqXi/KCGQWK8uObNTtKXtcDmhMK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cjdwdC/btszcGOxqXi/KCGQWK8uObNTtKXtcDmhMK/img.png&quot; data-alt=&quot;cuda stream 성능향상 예시&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cjdwdC/btszcGOxqXi/KCGQWK8uObNTtKXtcDmhMK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcjdwdC%2FbtszcGOxqXi%2FKCGQWK8uObNTtKXtcDmhMK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;818&quot; height=&quot;352&quot; data-origin-width=&quot;818&quot; data-origin-height=&quot;352&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;cuda stream 성능향상 예시&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;stream을 사용하면 위 예시와 같이 kernel launch와 data transfer를 겹칠 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;예시 : 코드&lt;/h4&gt;
&lt;pre id=&quot;code_1698345302773&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cudaSream_t stream; // Declaring the stream variable
cudaStreamCreate(&amp;amp;stream); // Creating the stream

// Assigning stream to kernel launch
myKernel&amp;lt;&amp;lt;grid, shmem, stream&amp;gt;&amp;gt;(args);

// Checking if the stream has finished
if (cudaStreamQuery(stream) == cudaSuccess) cout &amp;lt;&amp;lt; &amp;ldquo;Finished&amp;rdquo;;

// Waiting for finalization
cudaStreamSynchronize(stream);

// Deallocating memory
cudaStreamDestory(stream);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Stream Semantics&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;stream이 지정되지 않은 경우 기본값인 stream 0이 사용된다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;같은 stream&lt;/b&gt;에서 만들어진 두 연산은 &lt;b&gt;만들어진 순서대로 실행&lt;/b&gt;된다. 예를 들어 operation A, operation B 순서대로 만들어졌다면 A가 끝날 때 까지 B는 실행되지 않는다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;서로 다른 stream&lt;/b&gt;에서 만들어진 두 연산은 &lt;b&gt;순서 없이 실행&lt;/b&gt;된다.&lt;/li&gt;
&lt;/ol&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;`cudaMemcpyAsync()`나 kernel launch를 stream으로 사용하는 것이 일반적이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Default Stream (stream 0)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;stream이 지정되지 않은 경우 사용되는 stream이다.&lt;/li&gt;
&lt;li&gt;default stream은 특별한 synchronization 규칙이 있다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;host와 device에 대해 100% synchronous&lt;/b&gt;하다. 다른 stream에 대해서도 synchronous하다.&lt;/li&gt;
&lt;li&gt;`cudaDeviceSynchronize()`가 모든 CUDA operation 전후에 삽입된 것처럼 보인다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;host에 대해서는 asynchronous하다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;kernel은 default stream에서 실행된다. 만약 `cudaMemcpyAsync()`나 `cudaMemsetAsync()`를 호출하면 asynchronous하게 실행된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Concurrency의 요구사항&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CUDA operation은 &lt;b&gt;0이 아닌 서로 다른 stream&lt;/b&gt;에 있어야 한다.&lt;/li&gt;
&lt;li&gt;`cudaMemcpyAsync()`는 host의 &lt;b&gt;pinned memory&lt;/b&gt;와 사용되어야 한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;pinned memory는 page locked memory이며, page eviction이 발생하지 않는 memory에 고정된 page이다.&lt;/li&gt;
&lt;li&gt;`cudaMallocHost()`나 `cudaHostAlloc()`으로 할당할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;충분한 resource를 사용할 수 있어야 한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;`cudaMemcpyAsync()`의 방향이 다르기 때문이다.&lt;/li&gt;
&lt;li&gt;만약 SM, register, memory, block 등이 충분하지 않지 않다면 concurrency가 발생하지 않는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Pinned Memory&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;pinned memory는 &lt;b&gt;GPU의 exclusive access를 위한 virtual memory page&lt;/b&gt;이며, CPU에 할당된다. pinned memory로 지정된 memory는 host virtual memory에서 제거되며, paging의 대상에서 벗어난다. (항상 on memory라고 생각하면 된다.)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CPU나 GPU 사이에서 asynchronous하게 memcopy하며, host와 device copy보다 빠르다.&lt;/li&gt;
&lt;li&gt;direct memory access의 방식이기에 asynchronous하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;사용 방법은 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;`cudaHostAlloc()`과 `cudaFreeHost()`&lt;/li&gt;
&lt;li&gt;`cudaHostRegister()`과 `cudaHostUnregister()`&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;654&quot; data-origin-height=&quot;312&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cWZ6dJ/btszdgaWsns/kyZppDYaR2sikkZdNrLRF1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cWZ6dJ/btszdgaWsns/kyZppDYaR2sikkZdNrLRF1/img.png&quot; data-alt=&quot;pinned memory&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cWZ6dJ/btszdgaWsns/kyZppDYaR2sikkZdNrLRF1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcWZ6dJ%2FbtszdgaWsns%2FkyZppDYaR2sikkZdNrLRF1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;654&quot; height=&quot;312&quot; data-origin-width=&quot;654&quot; data-origin-height=&quot;312&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;pinned memory&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;`cudaMemcpy()`와 같이 pinned memory로 할당하지 않는 data transfer는, pageable host memory에서 implicitly pinned memory로 이동한 후 device memory로 이동한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;반면 pinned memory로 할당한 data transfer는 바로 device memory로 이동한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;예시 : synchronous&lt;/h4&gt;
&lt;pre id=&quot;code_1698338701275&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cudaMalloc (&amp;amp;dev1, size);
double *host1 = (double *) malloc(&amp;amp;host1, size);
&amp;hellip;
cudaMemcpy(dev, host1, size, H2D);
kernel2&amp;lt;&amp;lt;&amp;lt;grid, block, 0&amp;gt;&amp;gt;&amp;gt;(&amp;hellip;, dev2, &amp;hellip;);
kernel3&amp;lt;&amp;lt;&amp;lt;grid, block, 0&amp;gt;&amp;gt;&amp;gt;(&amp;hellip;, dev3, &amp;hellip;);
cudaMemcpy(host4, dev4, size, D2H);
...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위 코드에서 `cudaMemcpy()` 2개 사이에 있는 kernel launch는 100% synchronous이다. default stream의 모든 CUDA operation끼리는 100% synchronous이기 때문이다. kernel launch는 host와 asynchronous이지만 default stream 내에서는 synchronous이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;예시 : asynchronous&lt;/h4&gt;
&lt;pre id=&quot;code_1698338738852&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cudaMalloc (&amp;amp;dev1, size)
double *host1 = (double *) malloc(&amp;amp;host1, size);
&amp;hellip;
cudaMemcpy(dev, host1, size, H2D);
kernel2&amp;lt;&amp;lt;&amp;lt;grid, block, 0&amp;gt;&amp;gt;&amp;gt;(&amp;hellip;, dev2, &amp;hellip;);
some_CPU_method();
kernel3&amp;lt;&amp;lt;&amp;lt;grid, block, 0&amp;lt;&amp;lt;&amp;lt;(&amp;hellip;, dev3, &amp;hellip;);
cudaMemcpy(host4, dev4, size, D2H);
&amp;hellip;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위 코드에서 `kernel2&amp;lt;&amp;lt;&amp;lt;grid, block, 0&amp;gt;&amp;gt;&amp;gt;()`과 `some_CPU_method()`는 겹칠 수 있다. GPU kernel launch는 host와 asynchronous이기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1698338782063&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cudaStream_t stream1, stream2, stream3, stream4;
cudaStreamCreate(&amp;amp;stream1);
&amp;hellip;
cudaMalloc(&amp;amp;dev1, size);
cudaMallocHost(&amp;amp;host1, size); // pinned memory required on host
&amp;hellip;
cudaMemcpyAsync(dev1, host1, size, H2D, stream1);
Kernel2&amp;lt;&amp;lt;&amp;lt;grid, block, 0, stream2&amp;gt;&amp;gt;&amp;gt;(&amp;hellip;, dev2, &amp;hellip;);
Kernel3&amp;lt;&amp;lt;&amp;lt;grid, block, 0, stream3&amp;gt;&amp;gt;&amp;gt;(&amp;hellip;, dev3, &amp;hellip;);
cudaMemcpyAsync(host4, dev4, size, D2H, stream4);
some_CPU_method();
&amp;hellip;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;다른 예시이다. 위 코드에서 `cudaMemcpyAsync()`부터 `some_CPU_method()`는 모두 겹칠 수 있다. `cudaMemcpyAsyn()`과 kernel launch에서 stream을 썼기 때문이다. `some_CPU_method()` 또한 겹칠 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Explicit Synchronization&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모든 것을 synchronize하고 싶을 때 : `cudaDeviceSynchronize()`로 실행할 수 있고, 모든 CUDA call이 완료될 때까지 host를 block한다.&lt;/li&gt;
&lt;li&gt;특정 stream에 대해 synchronize하고 싶을 때 : `cudaStreamSynchronize()`로 실행할 수 있고, 모든 stream CUDA call이 완료될 때까지 host를 block한다.&lt;/li&gt;
&lt;li&gt;event를 사용해 synchronize하고 싶을 때 : stream 내부에서 event를 사용해 synchronize한다. `cudaEventRecord()`, `cudaEventSynchronize()`, `cudaStreamWaitEvent()`, `cudaEventQuery()` 등이 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Implicit Synchronization&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;아래 작업들은 CUDA operation을 implicitly synchronize한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;paged locked memory allocation : `cudaMallocHost()`, `cudaHostAlloc()`&lt;/li&gt;
&lt;li&gt;device memory allocation : `cudaMalloc()`&lt;/li&gt;
&lt;li&gt;non async version of memory operation : `cudaMemcpy90`, `cudaMemset()`&lt;/li&gt;
&lt;li&gt;L1이나 shared memory로 변경 : `cudaDeviceSetCacheConfig()`&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Stream Scheduling&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;GPU가 stream을 예약하는 방법. &lt;b&gt;Computer Engine Queue, H2D Copy Engine Queue, D2H Copy Engine Queue&lt;/b&gt; 3개의 queue가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;CUDA operation은 &lt;b&gt;만들어진 순서대로 hardware에 전달&lt;/b&gt;되고, 그리고 관&lt;b&gt;계 있는 queue에 배치&lt;/b&gt;된다. kernel launch는 computer engine queue에, memcpy는 관련된 queue에 들어가는 식이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;engine queue들끼리 stream dependency는 유지되지만, 각각의 engine queue에서는 dependency가 유지되지 않는다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이후, 아래 조건을 만족할 때 engine queue에서 pop된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;같은 stream의 이전 호출이 완료되었을 때&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;하나의 stream에는 kernel launch, host2device, device2host operation이 여러 개 있을 수 있다. 예를 들어 이 stream이 kernel launch - host2device - device2host 순서로 호출을 한다고 하자. device2host가 실행되기 위해서는 host2device의 실행이 끝나야 한다! 이런 의미다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;같은 queue에 있는 호출이 전달되었을 때&lt;/b&gt; : 이건 작업 queue에서 순서가 왔을 때 실행할 수 있다는 의미이므로 직관적이다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;resource를 사용할 수 있을 때&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;b&gt;서로 다른 stream에 있는 CUDA kernel은 concurrent하게 실행&lt;/b&gt;될 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;주어진 kernel의 thread block은 이전 kernel에 대한 모든 thread block이 예약되었거나, 아직 사용할 수 있는 SM resource가 있을 때 schedule된다.&lt;/li&gt;
&lt;li&gt;참고로, blocked operation은 같은 queue의 다른 모든 operation을 block한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;예시&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;synchronous : `cudaMemcpy()`&lt;/li&gt;
&lt;li&gt;같은 stream일 때 asynchronous : `cudaMemcpyAsync(..., stream1)`로 memory를 옮기고 `foo&amp;lt;&amp;lt;&amp;lt;..., stream1&amp;gt;&amp;gt;&amp;gt;()`로 kernel launch한다.&lt;/li&gt;
&lt;li&gt;다른 stream일 때 asynchronous : `cudaMemcpyAsync(..., stream1)`로 memory를 옮기고 `foo&amp;lt;&amp;lt;&amp;lt;..., stream2&amp;gt;&amp;gt;&amp;gt;()`로 kernel launch한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;예시 : Blocked Queue&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;2개의 stream이 있다고 하자. stream 1은 HDa1, HDb2, K1, DH1 순서고, stream 2는 DH2 작업이 있다고 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;837&quot; data-origin-height=&quot;217&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/l6D9R/btszjHzdrO0/LPTnV5b2gVzA4zwDxq0pj0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/l6D9R/btszjHzdrO0/LPTnV5b2gVzA4zwDxq0pj0/img.png&quot; data-alt=&quot;stream 1이 먼저 만들어진 경우&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/l6D9R/btszjHzdrO0/LPTnV5b2gVzA4zwDxq0pj0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fl6D9R%2FbtszjHzdrO0%2FLPTnV5b2gVzA4zwDxq0pj0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;837&quot; height=&quot;217&quot; data-origin-width=&quot;837&quot; data-origin-height=&quot;217&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;stream 1이 먼저 만들어진 경우&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;stream 1이 먼저 만들어진 경우 execution은 오른쪽 그림과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;queue는 dependency를 관리하지 않기에, queue의 signal이 synchronization을 만든다. 각 stream의 CUDA operation은 아래 조건을 만족할 때 실행된다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;이후, 아래 조건을 만족할 때 engine queue에서 pop된다.&lt;br /&gt;&amp;nbsp;- 같은 stream의 이전 호출이 완료되었을 때&lt;br /&gt;&amp;nbsp;- 같은 queue에 있는 호출이 전달되었을 때&lt;br /&gt;&amp;nbsp;- resource를 사용할 수 있을 때&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;때문에 stream 1이 모두 실행된 후 stream 2의 DH2가 실행된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;811&quot; data-origin-height=&quot;228&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c8KBZO/btszjFuBrdE/PClSroPwa2NSTawibkZjp0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c8KBZO/btszjFuBrdE/PClSroPwa2NSTawibkZjp0/img.png&quot; data-alt=&quot;stream 2가 먼저 만들어진 경우&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c8KBZO/btszjFuBrdE/PClSroPwa2NSTawibkZjp0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc8KBZO%2FbtszjFuBrdE%2FPClSroPwa2NSTawibkZjp0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;811&quot; height=&quot;228&quot; data-origin-width=&quot;811&quot; data-origin-height=&quot;228&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;stream 2가 먼저 만들어진 경우&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;반면 stream 2가 먼저 만들어진 경우 실행은 위와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;stream 2의 DH2가 실행되고, 동시에 steam 1의 HDa1은 이전 호출이 없고, H2D queue도 비었고, 작업을 사용할 수 있으므로 HDa1을 실행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;때문에 stream 1의 HDa1과 stream 2의 DH2가 concurrrent하게 실행된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;예시 : 다른 blocked kernel&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;만약 두 stream이 CUDA kernel을 호출하기만 한다고 하자. 그러면 stream 1은 Ka1, Kb1이고 Stream 2는 Ka2, Kb2이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1015&quot; data-origin-height=&quot;323&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Ex6KC/btszg0NfRn0/lHYRNBkiMhLv2fB6gu3qvk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Ex6KC/btszg0NfRn0/lHYRNBkiMhLv2fB6gu3qvk/img.png&quot; data-alt=&quot;depth vs width&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Ex6KC/btszg0NfRn0/lHYRNBkiMhLv2fB6gu3qvk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEx6KC%2Fbtszg0NfRn0%2FlHYRNBkiMhLv2fB6gu3qvk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1015&quot; height=&quot;323&quot; data-origin-width=&quot;1015&quot; data-origin-height=&quot;323&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;depth vs width&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 때 kernel이 작아서 SM의 절반을 채울 수 있다고 하자. 그러면 depth부터 먼저 적용하면 runtime은 3이고, breath를 먼저 적용하면 runtime은 2가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1075&quot; data-origin-height=&quot;291&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nunUu/btszk3uQxn9/LhbkRvDq87qP6IA4DvJv8K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nunUu/btszk3uQxn9/LhbkRvDq87qP6IA4DvJv8K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nunUu/btszk3uQxn9/LhbkRvDq87qP6IA4DvJv8K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnunUu%2Fbtszk3uQxn9%2FLhbkRvDq87qP6IA4DvJv8K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1075&quot; height=&quot;291&quot; data-origin-width=&quot;1075&quot; data-origin-height=&quot;291&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;반면 각 kernel의 크기가 다른 경우 위와 같다. depth를 우선순위로 두면 runtime은 5, breath를 우선순위로 두면 runtime은 4가 된다. 개발자가 적당히 잘 조절하면 3까지도 줄일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Concurrency Guideline&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;cost가 높은 GPU 작업을 pipelining하기 위해 CUDA stream과 asynchronous API를 사용한다.&lt;/li&gt;
&lt;li&gt;issue order에 따라 실행 결과가 크게 달라지기 때문에 주의해야 한다.&lt;/li&gt;
&lt;li&gt;concurrrency를 깨뜨릴 수 있는 resource와 operation에 주의해야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Thead Synchronization&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Synchronizing Threads&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;grid level collective synchronization&lt;/b&gt; : &lt;b&gt;global memory&lt;/b&gt;를 통해 서로 &lt;b&gt;다른 thread block끼리&lt;/b&gt; communication과 synchronization을 한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;block level collective synchronization&lt;/b&gt; : &lt;b&gt;shared memory&lt;/b&gt;를 사용해 &lt;b&gt;thread block끼리&lt;/b&gt; communication과 synchronization을 한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;warp level collective synchronization&lt;/b&gt; : warp level primitive를 사용하면 &lt;b&gt;warp 내의 thread끼리 register&lt;/b&gt;를 사용해서 direct communication을 할 수 있다.&lt;/li&gt;
&lt;li&gt;fine grained synchronization :&amp;nbsp;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;만약 2개 이상의 thread가 동시에 같은 shared memory에 접근하고, 하나 이상의 thread가 write operation을 수행할 때 race condition이 발생한다.&lt;/li&gt;
&lt;li&gt;synchronization하지 않는다면 update가 사라지게 되므로 예측할 수 없는 동작이 발생한다.&lt;/li&gt;
&lt;li&gt;non-deterministic fine grained communication의 경우 kernel launch나 barrier가 동작하지 않는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Warp Level Synchronization&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;869&quot; data-origin-height=&quot;242&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tb5Tg/btszgYhCM8W/kRj2pl3sza8oJNMM6kTFB0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tb5Tg/btszgYhCM8W/kRj2pl3sza8oJNMM6kTFB0/img.png&quot; data-alt=&quot;__syncwarp()&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tb5Tg/btszgYhCM8W/kRj2pl3sza8oJNMM6kTFB0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Ftb5Tg%2FbtszgYhCM8W%2FkRj2pl3sza8oJNMM6kTFB0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;869&quot; height=&quot;242&quot; data-origin-width=&quot;869&quot; data-origin-height=&quot;242&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;__syncwarp()&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;`__syncwarp()` : warp 내의 thread를 synchronize하는 데 사용하는 함수로, parameter로 넣은 mask에 해당하는 모든 wap 내의 thread가 `__syncwarp()`를 호출할 때까지 기다린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Atomics&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;필요성&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;제일 일반적인 문제는 shared data에 read-modify-write를 할 때 race condition이 발생한다는 것이다. 특히 transaction이나 data access에 대해! 이를 위해 data aggregation과 enumeration을 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Atomic Operation&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;CUDA는 여러 thread에서 접근하는 shared variable에 대한 atomic function을 제공한다. 이는 다른 thread의 중단 없이 memory를 atomic하게 수정하는 방식으로 동작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이를 통해 동시에 만들어진 atomic update가 수행되고, 모든 thread가 update 결과를 볼 수 있다는 것을 보장한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;access는 serialize되어 &lt;b&gt;한 번에 하나의 thread만 접근하고 나머지는 대기&lt;/b&gt;하는 방식이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;CUDA에서 Atomic Operation&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;단일 instruction으로 변환되는 function를 호출해 쓸 수 있다. `atomicAdd()`, `atomicSub()`, ... 등등이 있다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예를 들어 `atomicAdd()`의 경우 원래 global/shared memory에 있는 값을 읽고, 거이에 값을 더하고 저장한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Atomic의 성능 효과&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;atomics는 일반적인 memory load/store보다는 느리다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;shared memory에 있는 변수에 대해서는 빠르지만 global memory에 있는 변수에 대해서는 느리다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;많은 thread가 몇몇 작은 위치에 atomic operation을 사용할 경우 성능 저하가 있을 수 있다.&lt;/li&gt;
&lt;li&gt;더 많은 parallelism과 locality를 만들기 위해 hierarchy를 만든다.&lt;/li&gt;
&lt;li&gt;가능할 때마다 synchronization을 피하기 위한 자료구조가 필요하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;잘못된 내용이나 오탈자에 대한 지적, 질문&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;b&gt;등은 언제나 환영합니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>CS/Parallel Computing</category>
      <author>hyelie</author>
      <guid isPermaLink="true">https://hyelie.tistory.com/672</guid>
      <comments>https://hyelie.tistory.com/entry/%EC%9D%B4%EC%A2%85%EB%B3%91%EB%A0%AC%EC%BB%B4%ED%93%A8%ED%8C%85-Thread-Execution-Efficiency#entry672comment</comments>
      <pubDate>Sat, 28 Oct 2023 00:14:03 +0900</pubDate>
    </item>
    <item>
      <title>[이종병렬컴퓨팅] Memory와 Data Locality - Tiled Multiplication &amp;amp; Unified Memory</title>
      <link>https://hyelie.tistory.com/entry/%EC%9D%B4%EC%A2%85%EB%B3%91%EB%A0%AC%EC%BB%B4%ED%93%A8%ED%8C%85-Memory%EC%99%80-Data-Locality-Tiled-Multiplication-Unified-Memory</link>
      <description>&lt;blockquote style=&quot;text-align: left;&quot; data-ke-style=&quot;style2&quot;&gt;이 글은 포스텍 성효진 교수님의 이종병렬컴퓨팅(CSED490C) 강의를 기반으로 재구성한 것입니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 글에서는 다음과 같은 내용들을 살핀다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CUDA memory를 효율적으로 사용하는 방법
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;memory access 효율이 performance에 미치는 영향&lt;/li&gt;
&lt;li&gt;다양한 memory의 수명&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;tiled parallel algorithm&lt;/li&gt;
&lt;li&gt;matrix multiplication - tiled multiplication kernel&lt;/li&gt;
&lt;li&gt;unified memory&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Introduction&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Matrix Multiplication&lt;/h3&gt;
&lt;pre id=&quot;code_1698257831696&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;__global__ void MatrixMulKernel(float* M, float* N, float* P, int Width) {
    // Calculate the row index of the P element and M
    int Row = blockIdx.y*blockDim.y+threadIdx.y;
    
    // Calculate the column index of P and N
    int Col = blockIdx.x*blockDim.x+threadIdx.x;
    
    if ((Row &amp;lt; Width) &amp;amp;&amp;amp; (Col &amp;lt; Width)) {
        float Pvalue = 0;
        // each thread computes one element of the block sub-matrix
        for (int k = 0; k &amp;lt; Width; ++k) {
            Pvalue += M[Row*Width+k]*N[k*Width+Col];
        }
        P[Row*Width+Col] = Pvalue;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;M, N이 given, P가 output일 때, 지난 포스팅에서 배운 내용으로 matrix multiplication을 구현하면 위와 같을 것이다. if문 검사하는 부분은 matrix size가 input size의 배수가 아닐 수 있기 때문에 하는 검사 로직이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Group 3413.png&quot; data-origin-width=&quot;986&quot; data-origin-height=&quot;300&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8oqui/btsy8rKPhbI/EnZGqcDGCp9KasjsRMG6ZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8oqui/btsy8rKPhbI/EnZGqcDGCp9KasjsRMG6ZK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8oqui/btsy8rKPhbI/EnZGqcDGCp9KasjsRMG6ZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8oqui%2Fbtsy8rKPhbI%2FEnZGqcDGCp9KasjsRMG6ZK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;568&quot; height=&quot;173&quot; data-filename=&quot;Group 3413.png&quot; data-origin-width=&quot;986&quot; data-origin-height=&quot;300&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;곱하는 부분을 그림으로 나타내면 위와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;문제점: GPU의 성능&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위 코드의 경우, 모든 thread가 global memory의 `input` matrix에 접근한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;floating point 곱셈과 덧셈 연산에서 2번의 memory access가 발생한다.&lt;/li&gt;
&lt;li&gt;각 floating point operation Flops당 4byte의 memory bandwidth가 발생한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그러면 이 때 GPU가 600GB/s DRAM이고, GPU는 1.6TFlops - 초당 1.6T개의 floating point operation - 를 할 수 있다고 가정하자.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;GPU가 가지는 1.6TFlops를 모두 감당하기 위해서는 4 * 1.6 = 6.4TB/s의 bandwidth가 필요하다.&lt;/li&gt;
&lt;li&gt;가정한 memory bandwidth는 &lt;span style=&quot;text-align: left;&quot;&gt;600GB/s이므로, &lt;/span&gt;150GFlops밖에 하지 못한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;즉, floating point 연산 속도에 비해 memory bandwidth가 크게 못미치는 상황이다. 따라서, 1.6TFlops에 근접하기 위해서는 memory access를 줄여야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Cuda Memory Hierarchy&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;372&quot; data-origin-height=&quot;409&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dgZHWW/btszbPjyGWK/EuPTmOriEV80ZNtNrS0fy0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dgZHWW/btszbPjyGWK/EuPTmOriEV80ZNtNrS0fy0/img.png&quot; data-alt=&quot;CUDA memory hierarchy&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dgZHWW/btszbPjyGWK/EuPTmOriEV80ZNtNrS0fy0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdgZHWW%2FbtszbPjyGWK%2FEuPTmOriEV80ZNtNrS0fy0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;372&quot; height=&quot;409&quot; data-origin-width=&quot;372&quot; data-origin-height=&quot;409&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;CUDA memory hierarchy&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;CUDA memory hierarchy는 위와 같다.&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각 thread는
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;thread별 register에 1-2 cycle만에 read/write할 수 있다.&lt;/li&gt;
&lt;li&gt;thread별 local memory에 약 500 cycle만에 read/write할 수 있다.&lt;/li&gt;
&lt;li&gt;block별 shared memory에 10 cycle만에 read/write할 수 있다.&lt;/li&gt;
&lt;li&gt;grid별 global memory에 500 cycle만에 read/write할 수 있다.&lt;/li&gt;
&lt;li&gt;grid별 constant memory나 texture memory에 100 cycle만에 read only할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;CUDA의 Variable Type&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;777&quot; data-origin-height=&quot;180&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/l24xm/btszhqpARgn/lwzBjj7oW4rX90USOyUlX1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/l24xm/btszhqpARgn/lwzBjj7oW4rX90USOyUlX1/img.png&quot; data-alt=&quot;CUDA variable type&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/l24xm/btszhqpARgn/lwzBjj7oW4rX90USOyUlX1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fl24xm%2FbtszhqpARgn%2FlwzBjj7oW4rX90USOyUlX1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;777&quot; height=&quot;180&quot; data-origin-width=&quot;777&quot; data-origin-height=&quot;180&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;CUDA variable type&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;LocalVar는 thread의&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;register&lt;/b&gt;에 저장되고, thread가 끝나면 사라진다.&lt;/li&gt;
&lt;li&gt;`__shared__`로 선언하는 SharedVar는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;shared memory&lt;/b&gt;에 저장되고, thread block이 끝나면 사라진다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;thread block당 하나&lt;/b&gt;가 생성된다.&lt;/li&gt;
&lt;li&gt;`__device__`로 선언하는 GlobalVar는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;global memory&lt;/b&gt;에 저장되고, grid, 즉 application이 끝나면 사라진다.&lt;/li&gt;
&lt;li&gt;`__constant__`로 선언하는 ConstantVar는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;constant memory&lt;/b&gt;에 저장되고, application이 끝나면 사라진다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Variable의 선언 위치&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;host가 접근해야 하는지 여부에 따라 variable을 어디에 선언하는지가 달라진다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;True면 function 밖에 선언한다. `__constant__`나 `__device__`로 선언한다.&lt;/li&gt;
&lt;li&gt;False면 kernel 안에 선언한다. `LocalVar`나 `__shared__`로 선언한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Memory Type에 따른 전략&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;read only : constant memory에 둔다. 이는 64KB 이하의 작은, thread가 공유하는 read only data인 경우 좋다. 빠르다.&lt;/li&gt;
&lt;li&gt;block 내부에서 공유하고, read/write하는 경우 : local memory 또는 shared memory에 둔다. 빠르다.&lt;/li&gt;
&lt;li&gt;각 thread에서 read/write하는 경우 : thread register에 둔다. 빠르다.&lt;/li&gt;
&lt;li&gt;각 thread 내부에서 indexed read/write하는 경우(array인 경우) : thread-local memory에 둔다. 느리다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;따라서 array를 쓰는 경우 global memory에 두고 shared memory로 가져오는 편이 좋다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;input/result read/write : global memory에 둔다. 느리다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;GPU의 Shared Memory&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;b&gt;kernel 코드에서 explicitly하게 정의되고 사용되는 특별한 memory&lt;/b&gt;이다. 즉 &lt;b&gt;프로그래머가 직접 관리&lt;/b&gt;해야 하는 memory이다. L2 cache는 공개 cache이기 때문에 많은 thread가 모두 공유하는데, GPU의 경우 thread가 매우 많기 때문에 L2 cache에 caching되더라도 evict될 확률이 너무 높다. 따라서 L2 cache만을 사용해서 locality를 제공하는 것은 한계가 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;각 SM당 하나씩 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;access 및 sharing의 범위는 &lt;b&gt;thread block&lt;/b&gt;이다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;lifetime은 &lt;b&gt;thread block&lt;/b&gt;이다. 즉, thread block의 실행이 끝나면 내용이 사라진다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;explicitly하게 memory load/store instruction을 호출해야 한다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;scratchpad memory라고도 불린다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;직접 관리하는 cache라고 보면 된다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Cache vs Shared Memory&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;451&quot; data-origin-height=&quot;241&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/w5mAw/btsy8taPb1d/rZ0xqjXE8fFoYpKnTxZ8c1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/w5mAw/btsy8taPb1d/rZ0xqjXE8fFoYpKnTxZ8c1/img.png&quot; data-alt=&quot;cache와 shared memory&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/w5mAw/btsy8taPb1d/rZ0xqjXE8fFoYpKnTxZ8c1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fw5mAw%2Fbtsy8taPb1d%2FrZ0xqjXE8fFoYpKnTxZ8c1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;451&quot; height=&quot;241&quot; data-origin-width=&quot;451&quot; data-origin-height=&quot;241&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;cache와 shared memory&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%; text-align: center;&quot;&gt;&lt;b&gt;cache&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; text-align: center;&quot;&gt;&lt;b&gt;shared memory&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;hardware과 관리한다.&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;software가 관리한다. (별도의 address space를 사용하므로 cache 나 global memory와 공유하지 않는다.)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;tag matching으로 인해 overhead가 있다.&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;전력 소비량이 낮고, directly addressed이다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;implicit하게 data가 이동한다.&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;explicit하게 data가 이동한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;replacement policy를 따르기 때문에 memory 낭비가 발생할 수 있다.&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;작고 효율적이다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;예시: Shared Memory의 필요성&amp;nbsp;&lt;/h4&gt;
&lt;pre id=&quot;code_1698317133439&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// motivate shared variables with Adjacent Difference application
// compute result[i] = input[i] &amp;ndash; input[i-1]
__global__ void adj_diff_naive(int *result, int *input) {
    // compute this thread&amp;rsquo;s global index
    unsigned int i = blockDim.x * blockIdx.x + threadIdx.x;
    
    if (i &amp;gt; 0) {
        // how many times does this kernel load input[i]?
        int x_i = input[i]; // once by thread i
        int x_i_minus_one = input[i-1]; // once by thread i+1

        result[i] = x_i &amp;ndash; x_i_minus_one;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위 코드는 인접한 index의 차이를 result에 넣는 kernel function이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 경우, input[i]는 thread i에 의해 한 번, thread i+1에 의해 또 한 번 불러진다.&amp;nbsp;그러나 input은 global memory에 있기 때문에 느리다! 따라서 이러한 중복된 global memory access를 줄이는 것이 shared memory의 목표이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Barrier Synchronization&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;530&quot; data-origin-height=&quot;371&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AUpfm/btszb4PuDtQ/9KC49hAKQRdSLI8bpsAOp0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AUpfm/btszb4PuDtQ/9KC49hAKQRdSLI8bpsAOp0/img.png&quot; data-alt=&quot;barrijer synchronization&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AUpfm/btszb4PuDtQ/9KC49hAKQRdSLI8bpsAOp0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAUpfm%2Fbtszb4PuDtQ%2F9KC49hAKQRdSLI8bpsAOp0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;436&quot; height=&quot;305&quot; data-origin-width=&quot;530&quot; data-origin-height=&quot;371&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;barrijer synchronization&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;`__syncthreads()` 함수를 사용해 &lt;b&gt;한 thread block에 있는 모든 thread&lt;/b&gt;가 해당 barrier에 도착하면 모든 thread가 도착할 때까지 기다리게 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Shared Memory의 사용법&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;static allocation과 dynamic allocation 2가지가 있는데, 둘 다 예시를 통해 살펴볼 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;예시 : Shared Memory Fixed Allocation&lt;/h4&gt;
&lt;pre id=&quot;code_1698317325433&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// optimized version of adjacent difference
__global__ void adj_diff(int *result, int *input)
{
    int tx = threadIdx.x; // shorthand for threadIdx.x
    
    // allocate a __shared__ array, one element per thread
    __shared__ int s_data[BLOCK_SIZE];
    // each thread reads one element to s_data
    unsigned int i = blockDim.x * blockIdx.x + tx;
    s_data[tx] = input[i];
    
    // avoid race condition: ensure all loads
    // complete before continuing
    __syncthreads(); // thread block 내의 모든 thread의 실행이 끝난 것을 보장한다. 즉, s_data에 모든 값이 잘 들어갔음을 보장한다.
    
    if (tx &amp;gt; 0)
    	result[i] = s_data[tx] &amp;ndash; s_data[tx&amp;ndash;1];
    else if (i &amp;gt; 0) // tx == 0 &amp;amp;&amp;amp; i &amp;gt; 0
    {
        // handle thread block boundary
        result[i] = s_data[tx] &amp;ndash; input[i-1];
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 경우 fixed size allocation으로 shared memory를 사용하는 예시이다. fixed allocation인 만큼 BLOCK_SIZE로 s_data를 할당했으며, BLOCK_SIZE는 thread block size와 동일하다. (one element per thread이므로)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;shared memory 부분에는 `s_data[tx] = input[i]`로 수행되었는데, s_data의 경우 &lt;b&gt;tx로 indexing하고 input의 경우 i로 indexing&lt;/b&gt;했다. tx는 thread index이고, i는 전체에서 실행 중인 thread의 index이다. 이렇게 한 이유는, shared memory는 thread block 내에서 공유하기 때문에 s_data를 tx로 indexing하기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이후에는 `__syncthread()`를 호출하는데, 다음 단계로 진행하기 전에 barrier를 만들어 s_data에 모든 data가 들어갔는지 보장하기 위해 사용한다. 만약 `__syncthread()`가 없다면 data가 아직 들어오지 않았을 수 있기 때문에 다음 코드에서 문제가 생긴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이후의 if문에서는 shared data에 접근해서 값을 계산한다. 이 코드에서는 `result[i] = ...`와 같이 global memory에 값을 1번 쓰고 있는데, 오직 1번의 global memory write만 하므로 괜찮다. (문제가 되는 것은 여러 번 access할 때이므로)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;else if문에서는 `result[i] = s_data[tx] - input[i-1];`의 방식으로, s_data는 shared memory에 접근하고 input은 global memory에 접근하는데, tx == 0인 경우는 해당 thread block의 shared memory에 저장되지 않은 input[i-1]의 값이 필요하다. 즉,&amp;nbsp;&lt;b&gt;다른 thread block의 data를 필요로 하는 경우&lt;/b&gt;가 있기 때문에, 이 경우만 예외적으로 처리해 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;예시 : Shared Memory의 Dynamic Allocation&lt;/h4&gt;
&lt;pre id=&quot;code_1698317438096&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/ when the size of the array isn&amp;rsquo;t known at compile time...
__global__ void adj_diff(int *result, int *input)
{
    // use extern to indicate a __shared__ array will be
    // allocated dynamically at kernel launch time
    extern __shared__ int s_data[];
    ...
}

// pass the size of the per-block array, in bytes, as the third
// argument to the triple chevrons
adj_diff&amp;lt;&amp;lt;&amp;lt;num_blocks, block_size, block_size * sizeof(int)&amp;gt;&amp;gt;&amp;gt;(r,i);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;dynamic allocation을 하기 위해서는 아래 2가지를 지켜야 한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;kernel의 `&lt;span style=&quot;text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;__shared__` 앞에 `extern`을 붙여 써야 한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;kernel launch를 할 때 &lt;b&gt;shared memory 크기를 지정&lt;/b&gt;해 줘야 한다.&lt;/span&gt;&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;shared memory는 thread block에 할당되며, SM이 하나의 thread block을 실행한다. kernel launch에서 shared memory 크기를 알려 주고, 이를 통해 scheduler가 applicatoion 전체에서 필요한 공간을 계산한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;예시 : Shared Memory에 여러 개의 Dynamic Array&lt;/h4&gt;
&lt;pre id=&quot;code_1698317509570&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;extern __shared__ int s[];
int *integerData = s; // nI ints
float *floatData = (float*)&amp;amp;integerData[nI]; // nF floats
char *charData = (char*)&amp;amp;floatData[nF]; // nC chars
...

//kernel launch from host
myKernel&amp;lt;&amp;lt;&amp;lt;gridSize, blockSize,
nI*sizeof(int)+nF*sizeof(float)+nC*sizeof(char)&amp;gt;&amp;gt;&amp;gt;(...);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;만약 여러 개의 array를 사용하고 싶다면 위 코드와 같이 하나의 큰 array를 할당한 후, pointer를 사용해 나누어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Tiling&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Tiling이 없는 경우&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;426&quot; data-origin-height=&quot;232&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bcGwnf/btszbQKAQKB/dXQE9KvlvUtAgdMhVDugM1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bcGwnf/btszbQKAQKB/dXQE9KvlvUtAgdMhVDugM1/img.png&quot; data-alt=&quot;tiling 없이 memory access pattern&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bcGwnf/btszbQKAQKB/dXQE9KvlvUtAgdMhVDugM1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbcGwnf%2FbtszbQKAQKB%2FdXQE9KvlvUtAgdMhVDugM1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;426&quot; height=&quot;232&quot; data-origin-width=&quot;426&quot; data-origin-height=&quot;232&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;tiling 없이 memory access pattern&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;앞서 살펴본 matrix multiplication은 같은 element에 중복해 접근하는 일이 많았고, 이 중복이 모두 global memory access였다. 각 thread는 M과 N을 width번 호출한다. 호출 위치가 global memory이기 때문에 상당히 느리다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Tiling을 쓰는 경우&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;439&quot; data-origin-height=&quot;220&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bTO8hq/btszbVSHWks/334UgJLLyKCcVmo8X1TJKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bTO8hq/btszbVSHWks/334UgJLLyKCcVmo8X1TJKk/img.png&quot; data-alt=&quot;tiling 사용했을 때 memory access pattern&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bTO8hq/btszbVSHWks/334UgJLLyKCcVmo8X1TJKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbTO8hq%2FbtszbVSHWks%2F334UgJLLyKCcVmo8X1TJKk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;439&quot; height=&quot;220&quot; data-origin-width=&quot;439&quot; data-origin-height=&quot;220&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;tiling 사용했을 때 memory access pattern&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;global memory를 tile을 기준으로 나누고, 각 thread 또한 tile에만 집중해서 계산하는 방식이다. 이 방법을 사용하면 &lt;b&gt;global memory를 중복해 호출하는 회수를 줄일 수 있다&lt;/b&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 구현은 L1 cache나 L2 cache를 사용해 caching effect를 극대화 할 수도 있고, shared memory(scratchpad)를 사용할 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Synchronization&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;416&quot; data-origin-height=&quot;197&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TudHJ/btszbLvSVqq/AW0vkopniYAahojp7R3XE1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TudHJ/btszbLvSVqq/AW0vkopniYAahojp7R3XE1/img.png&quot; data-alt=&quot;synchronization&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TudHJ/btszbLvSVqq/AW0vkopniYAahojp7R3XE1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTudHJ%2FbtszbLvSVqq%2FAW0vkopniYAahojp7R3XE1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;416&quot; height=&quot;197&quot; data-origin-width=&quot;416&quot; data-origin-height=&quot;197&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;synchronization&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;단, 이 방식은 &lt;b&gt;synchronization&lt;/b&gt;이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위 경우, 같은 element에 2개의 thread가 비슷한 시간대에 접근하고 있다. 반면, 아래 경우 같은 element에 2개의 thread가 매우 다른 시간대에 접근하고 있다. 아래 경우는 cache effect를 받지 못하고, 그만큼 on chip memory에 데이터를 계속 올려둬야 하기에 좋지 않은 모델이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;따라서, &lt;b&gt;thread들이 비슷한 시간에 access&lt;/b&gt;할 때 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;요약&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;b&gt;global memory access는 on chip memory보다 느리기&lt;/b&gt; 때문에 이 global memory access 대신 &lt;b&gt;shared memory access&lt;/b&gt;를 사용한다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;global memory content에 사용할 &lt;b&gt;tile&lt;/b&gt;을 만든다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;global memory에서 shared memory로&lt;/b&gt; tile을 가져온다.&lt;/li&gt;
&lt;li&gt;thread는 &lt;b&gt;shared memory에 access해서 계산&lt;/b&gt;한다.&lt;/li&gt;
&lt;li&gt;synchronization을 위해 &lt;b&gt;barrier&lt;/b&gt;를 사용한다.&lt;/li&gt;
&lt;li&gt;(필요 시) shared memory에서 global memory로 계산 결과를 복사한다.&lt;/li&gt;
&lt;li&gt;다음 tile로 이동한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Tiling : Matrix Multiplication&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;tiling의 종류에는 크게 2가지, input tiling과 output tiling이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;output tiling은 output matrix를 tile 기준으로 나누고 thread block에 매핑하는 방식이다. 반면 input tiling은 input matrix를 tile 기준으로 나누는 방식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Output Tiling&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;431&quot; data-origin-height=&quot;393&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tuhZ3/btszfJcsQXr/hNmMRNd1jtgn0f8niXIfYK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tuhZ3/btszfJcsQXr/hNmMRNd1jtgn0f8niXIfYK/img.png&quot; data-alt=&quot;output tiling 예시&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tuhZ3/btszfJcsQXr/hNmMRNd1jtgn0f8niXIfYK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtuhZ3%2FbtszfJcsQXr%2FhNmMRNd1jtgn0f8niXIfYK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;431&quot; height=&quot;393&quot; data-origin-width=&quot;431&quot; data-origin-height=&quot;393&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;output tiling 예시&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;output tiling은 위 그림처럼 &lt;b&gt;output matrix을 tile로 나누고, 이 tile을 thread block에 할당&lt;/b&gt;하는 방식이다. thread block을 output matrix에 매핑하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그러면 &lt;b&gt;하나의 thread block은 하나의 output tile을 계산&lt;/b&gt;한다. 일반적으로 thread block을 tile과 동일한 크기로 잡으므로, thread block은 output matrix에서 하나의 element를 계산하게 된다. 그러면 각 thread는 M의 row와 tile에 해당하는 N의 column에 access한다. 위 그림에서는 Md, Nd로 표현되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;예제: Output Matrix를 Thread Block에 매핑&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;409&quot; data-origin-height=&quot;231&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/onfiC/btszhpEeBoy/BWkar5K07Imqk6YzKfbyKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/onfiC/btszhpEeBoy/BWkar5K07Imqk6YzKfbyKk/img.png&quot; data-alt=&quot;tile&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/onfiC/btszhpEeBoy/BWkar5K07Imqk6YzKfbyKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FonfiC%2FbtszhpEeBoy%2FBWkar5K07Imqk6YzKfbyKk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;409&quot; height=&quot;231&quot; data-origin-width=&quot;409&quot; data-origin-height=&quot;231&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;tile&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;TILD_WIDTH * TILD_WIDTH 크기의 thread block을 선언한다. 그러면 각 thread block에는 TILD_WIDTH$^2$개의 thread가 들어가고, thread block은 총 $\frac{\text{WIDTH}}{\text{TILE_WIDTH}}^2$개가 존재한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위 예시는 WIDTH가 4, TILD_WIDTH가 2인 예시이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Memory Layout&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;564&quot; data-origin-height=&quot;255&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LKgaV/btszelbThrY/gZ0Qr2hYTkkNP0z5Gjej7K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LKgaV/btszelbThrY/gZ0Qr2hYTkkNP0z5Gjej7K/img.png&quot; data-alt=&quot;indexing&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LKgaV/btszelbThrY/gZ0Qr2hYTkkNP0z5Gjej7K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLKgaV%2FbtszelbThrY%2FgZ0Qr2hYTkkNP0z5Gjej7K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;564&quot; height=&quot;255&quot; data-origin-width=&quot;564&quot; data-origin-height=&quot;255&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;indexing&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이전 포스팅에서 다뤘듯 모든 n-D memory는 &lt;b&gt;1D array로 평면화&lt;/b&gt;된다. 따라서 &lt;b&gt;indexing&lt;/b&gt;을 해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;예를 들어 위 그림에서 M$_2, _1$에 접근하기 위해서는 2 * WIDTH + 1에 접근해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;코드&lt;/h4&gt;
&lt;pre id=&quot;code_1698326319944&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;__global__ void MatrixMulKernel(float* M, float* N, float* P, int Width) {
    // Calculate the row index of the P element and M
    int Row = blockIdx.y*blockDim.y+threadIdx.y;
    
    // Calculate the column index of P and N
    int Col = blockIdx.x*blockDim.x+threadIdx.x
    
    if ((Row &amp;lt; Width) &amp;amp;&amp;amp; (Col &amp;lt; Width)) {
        float Pvalue = 0;
        // each thread computes one element of the block sub-matrix
        for (int k = 0; k &amp;lt; Width; ++k) {
            Pvalue += M[Row*Width+k]*N[k*Width+Col];
        }
        P[Row*Width+Col] = Pvalue;
    }
}

void MatrixMulOnDevice(float* M, float* N, float* P, int Width) {
    int size = Width * Width * sizeof(float);
    cl_mem Md, Nd, Pd;
    cudaMalloc((void**) &amp;amp;Md, size);
    cudaMalloc((void**) &amp;amp;Nd, size);
    cudaMalloc((void**) &amp;amp;Pd, size);
    
    cudaMemCpy(Md, M, size, cudaMemcpyHostToDevice);
    cudaMemCpy(Nd, N, size, cudaMemcpyHostToDevice);
    cudaMemset(Pd, 0, size);

    // kernel invocation code
    ...

    // Read P from the device
    cudaMemCpy(P, Pd, size, cudaMemcpyDeviceToHost);

    // Free device matrices
    cudaFree(Md);
    cudaFree(Nd);
    cudaFree(Pd); 
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;output tiling의 코드는 위와 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;`MatrixMulOnDevice`에서 `cudaMalloc()`, `cudaMemcpy()`, `cudaMemset()`을 호출해 M, N, P를 초기화한다. 이후 `cudaMemcpy()`를 호출해 Pd를 P에 복사하고 있다.&lt;/li&gt;
&lt;li&gt;`MatrixMulKernel`은 M의 모든 row, N의 모든 column으로 output tile의 한 element에 들어갈 값들을 계산한다. 이게 한 thread에서 수행하는 일이고, TILE_WIDTH * TILE_WIDTH개의 thread... 즉 thread block이 `MatrixMulKernel`을 실행하면 하나의 output tile의 모든 element를 계산하게 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Input Tiling&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;450&quot; data-origin-height=&quot;398&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bXzTnf/btszeILyXO6/iLIR0oAvfetMtKUljNlmq1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bXzTnf/btszeILyXO6/iLIR0oAvfetMtKUljNlmq1/img.png&quot; data-alt=&quot;tiled matrix multiplication&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bXzTnf/btszeILyXO6/iLIR0oAvfetMtKUljNlmq1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbXzTnf%2FbtszeILyXO6%2FiLIR0oAvfetMtKUljNlmq1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;450&quot; height=&quot;398&quot; data-origin-width=&quot;450&quot; data-origin-height=&quot;398&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;tiled matrix multiplication&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;input tiling은 &lt;b&gt;input을 tile로 나누는 방법&lt;/b&gt;이다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;output tiling에서는 M의 모든 row, N의 모든 column으로 P의 한 element를 계산했는데 여기서는 그 방법 대신 M의 tile, N의 tile을 사용한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;input tiling에서는 각 &lt;b&gt;thread의 실행을 phase로 나눈다&lt;/b&gt;. 그러면 각 phase에서 thread가 접근하는 data가 M의 tile 1개, N의 tile 1개에 집중된다. 위 그림에서는 처음에는 파란색 tile의 곱을 계산하고, 이후에는 주황색 tile의 곱을 계산한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;물론 한 번의 tile의 곱으로 완벽한 결과를 내지 못한다. 모든 tile의 연산이 끝나야 올바른 결과가 나온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;대신, 하나의 thread가 하나의 element의 결과를 연산하는 것이 아니라, shared memory에 올린 모든 data, 그러니까 tile에 해당하는 모든 data에 대해 연산한다. 따라서, &lt;b&gt;각 tile을 shared memory에 올리고&lt;/b&gt; - 이것은 thread block의 모든 thread가 공유한다 - &lt;b&gt;각 thread는 shared memory를 참조하면서 결과를 계산&lt;/b&gt;한다는 것이 input tiling의 기본 골자이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;예시&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;511&quot; data-origin-height=&quot;309&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zcECs/btszekYpGda/s6DwanoQ1rRG3KGKH9eSv1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zcECs/btszekYpGda/s6DwanoQ1rRG3KGKH9eSv1/img.png&quot; data-alt=&quot;phase 0 : block load&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zcECs/btszekYpGda/s6DwanoQ1rRG3KGKH9eSv1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzcECs%2FbtszekYpGda%2Fs6DwanoQ1rRG3KGKH9eSv1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;511&quot; height=&quot;309&quot; data-origin-width=&quot;511&quot; data-origin-height=&quot;309&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;phase 0 : block load&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;M의 tile 1개와 N의 tile 1개를 각각 shared memory에 넣는다. 그러면 각 thread block은 자신만의 tile을 가지게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8yz10/btszg0xQlj9/8KkJIXnVQidRgAHN401RTk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8yz10/btszg0xQlj9/8KkJIXnVQidRgAHN401RTk/img.png&quot; data-origin-width=&quot;534&quot; data-origin-height=&quot;312&quot; data-is-animation=&quot;false&quot; style=&quot;width: 48.8237%; margin-right: 10px;&quot; data-widthpercent=&quot;49.4&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8yz10/btszg0xQlj9/8KkJIXnVQidRgAHN401RTk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8yz10%2Fbtszg0xQlj9%2F8KkJIXnVQidRgAHN401RTk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;534&quot; height=&quot;312&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GCWGj/btszfLuBcEp/S4hEAYgwRcHO4lHcL5tWAK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GCWGj/btszfLuBcEp/S4hEAYgwRcHO4lHcL5tWAK/img.png&quot; data-origin-width=&quot;540&quot; data-origin-height=&quot;308&quot; data-is-animation=&quot;false&quot; style=&quot;width: 50.0135%;&quot; data-widthpercent=&quot;50.6&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GCWGj/btszfLuBcEp/S4hEAYgwRcHO4lHcL5tWAK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGCWGj%2FbtszfLuBcEp%2FS4hEAYgwRcHO4lHcL5tWAK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;540&quot; height=&quot;308&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;phase 0 : block (0, 0)을 계산&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;loading이 끝나면 shared memory에 로딩된 block (0, 0)을 사용해 계산한다. 여기서 계산의 결과값은 최종값이 아니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;왼쪽 그림은 iteration 0으로 M의 column 0과 N의 row 0 - M$_0, _0$과 M$_1, _0$, N$_0, _0$과 N$_0, _1$ - 을 사용한다.&lt;/li&gt;
&lt;li&gt;오른쪽 그림은 iteration 1로, M의 column 1과 N의 row 1 - M$_0, _1$과 M$_1, _1$, N$_1, _0$과 N$_1, _1$ - 을 사용한다.&lt;/li&gt;
&lt;li&gt;그냥 단순히 matrix multiplication이다! (각 iteration은 ijk matrix multiplication에서 k를 의미한다고 생각하면 된다.)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;533&quot; data-origin-height=&quot;323&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JFpJ6/btszgyans1B/wyKIkR2TPVJ1Mza8D7dPB0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JFpJ6/btszgyans1B/wyKIkR2TPVJ1Mza8D7dPB0/img.png&quot; data-alt=&quot;phase 1 : block load&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JFpJ6/btszgyans1B/wyKIkR2TPVJ1Mza8D7dPB0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJFpJ6%2Fbtszgyans1B%2FwyKIkR2TPVJ1Mza8D7dPB0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;533&quot; height=&quot;323&quot; data-origin-width=&quot;533&quot; data-origin-height=&quot;323&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;phase 1 : block load&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;block (0, 0)은 아직 계산이 덜 되었다. 이전에 계산한 tile 말고, 다른 M의 tile 1개와 N의 tile 1개를 shared memory에 넣는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhlygP/btszg0xQtqb/LAJaHjG17HoP6Kk583SlJk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhlygP/btszg0xQtqb/LAJaHjG17HoP6Kk583SlJk/img.png&quot; data-origin-width=&quot;524&quot; data-origin-height=&quot;323&quot; data-is-animation=&quot;false&quot; style=&quot;width: 48.7972%; margin-right: 10px;&quot; data-widthpercent=&quot;49.37&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhlygP/btszg0xQtqb/LAJaHjG17HoP6Kk583SlJk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbhlygP%2Fbtszg0xQtqb%2FLAJaHjG17HoP6Kk583SlJk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;524&quot; height=&quot;323&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sbXq5/btszcFaYDyz/AuRtz6kwQVAAFKxlvzhBpk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sbXq5/btszcFaYDyz/AuRtz6kwQVAAFKxlvzhBpk/img.png&quot; data-origin-width=&quot;544&quot; data-origin-height=&quot;327&quot; data-is-animation=&quot;false&quot; style=&quot;width: 50.04%;&quot; data-widthpercent=&quot;50.63&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sbXq5/btszcFaYDyz/AuRtz6kwQVAAFKxlvzhBpk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsbXq5%2FbtszcFaYDyz%2FAuRtz6kwQVAAFKxlvzhBpk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;544&quot; height=&quot;327&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;phase 1 : block (0, 0)을 계산&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;shared memory에 있는 값으로 block (0, 0)을 계산한다. 세부 내용은 위와 동일하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Indexing&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;465&quot; data-origin-height=&quot;403&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/13BMD/btszb6fC9Xf/qPfAtNBmOsv6ZdpBPBJhk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/13BMD/btszb6fC9Xf/qPfAtNBmOsv6ZdpBPBJhk0/img.png&quot; data-alt=&quot;input tiling index&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/13BMD/btszb6fC9Xf/qPfAtNBmOsv6ZdpBPBJhk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F13BMD%2Fbtszb6fC9Xf%2FqPfAtNBmOsv6ZdpBPBJhk0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;465&quot; height=&quot;403&quot; data-origin-width=&quot;465&quot; data-origin-height=&quot;403&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;input tiling index&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;2D의 경우
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;M[row][m*TILE_WIDTH + tx]&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;N[m*TILE_WIDTH+ty][col]&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;1D로 바꾸면
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;M[row*WIDTH + m*TILE_WIDTH + tx]&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;N[(m*TILE_WIDTH+ty)*WIDTH + col]&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;참고로 row = `blockIdx.y * blockDim.y + threadIdx.y`, `col = blockIdx.x * blockDim.x + threadIdx.x`이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;여기서 m = for loop의 iteration index.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Boundary Check&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;446&quot; data-origin-height=&quot;240&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/llAa8/btszgXOKCT9/4dpd9nkHKf5FcGQa2QSfek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/llAa8/btszgXOKCT9/4dpd9nkHKf5FcGQa2QSfek/img.png&quot; data-alt=&quot;boundary check&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/llAa8/btszgXOKCT9/4dpd9nkHKf5FcGQa2QSfek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FllAa8%2FbtszgXOKCT9%2F4dpd9nkHKf5FcGQa2QSfek%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;446&quot; height=&quot;240&quot; data-origin-width=&quot;446&quot; data-origin-height=&quot;240&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;boundary check&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;1D에서 처리했던 것과 마찬가지로 &lt;b&gt;WIDTH가 TILE_WIDTH의 배수가 아닐 수 있기 때문에&lt;/b&gt; 이에 관한 &lt;b&gt;예외 처리&lt;/b&gt;를 해야 한다. 만약 하지 않는다면, 값을 넣지 않은 공간을 계산하기 때문에 결과가 달라질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이를 수행하는 방법은 아래와 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;index&lt;/b&gt;를 계산하고, &lt;b&gt;valid&lt;/b&gt;한지 검사한다. &lt;span style=&quot;text-align: left;&quot;&gt;구체적으로는,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;M의 row, column에 대해 / N의 row, column가 모두 Width보다 작은지 검사한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;범위를 벗어나는 것에 대해서는 0을&lt;/b&gt; 둔다. 이는 결과에 영향을 미치지 않기 때문이다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1698319279920&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Loop over the M and N tiles required to compute the P element
for (int p = 0; p &amp;lt; ((Width-1)/TILE_WIDTH)+1; ++p) {
    __shared__ float ds_M[TILE_WIDTH][TILE_WIDTH];
    __shared__ float ds_N[TILE_WIDTH][TILE_WIDTH];

    int bx = blockIdx.x;
    int by = blockIdx.y;
    int tx = threadIdx.x;
    int ty = threadIdx.y;
    int Row = by * blockDim.y + ty;
    int Col = bx * blockDim.x + tx;
    float Pvalue = 0;
    
    // Loop over the M and N tiles required to compute the P element.
    for (int p = 0; p &amp;lt; ((Width-1)/TILE_WIDTH)+1; ++p) { // phase
        // Collaborative loading of M and N tiles into shared memory
        if (Row&amp;lt;Width &amp;amp;&amp;amp; p*TILE_WIDTH+tx &amp;lt; Width) {
            ds_M[ty][tx] = M[Row*Width + p*TILE_WIDTH+tx];
        } else {
            ds_M[ty][tx] = 0.0;
        }

        if (p*TILE_WIDTH+ty &amp;lt; Width &amp;amp;&amp;amp; Col&amp;lt;Width) {
            ds_N[ty][tx] = N[(p*TILE_WIDTH+ty)*Width + Col];
        } else {
            ds_N[ty][tx] = 0.0;
        }
    }
    
    __syncthreads(); // 모든 data가 shared memory에 저장된 것을 보장
    
    if (Row &amp;lt; Width &amp;amp;&amp;amp; Col &amp;lt; Width) {
        for (int i = 0; i &amp;lt; TILE_WIDTH; ++i)
            Pvalue += ds_M[ty][i] * ds_N[i][tx];
        }
    }
    __synchthreads(); // 모든 data가 계산된 것을 보장
    
    if (Row &amp;lt; Width &amp;amp;&amp;amp; Col &amp;lt; Width)
    	P[Row*Width+Col] = Pvalue;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그러면 코드는 위와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;`__syncthread()`를 사용하는 이유는 위와 같이, 모든 data가 shared memory에 저장된 것을 보장하고, 모든 data가 계산된 것을 보장하기 위해 사용한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;첫 번째 `__syncthread()`는 thread의 실행 속도가 다르기 때문에 모든 data가 shared memory에 저장된 것을 보장한다.&lt;/li&gt;
&lt;li&gt;두 번째 `__syncthread()`는 모든 data가 계산된 것을 보장한다. 만약 이것이 없다면 다음 phase에서 다른 값을 집어넣고, pvalue를 계산할 수 있기 때문에 꼭 필요하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;for문에서 `p &amp;lt; (Width-1) / TILE_WIDTH + 1`는 phase의 ceil을 취한 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Tile Size 결정&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;b&gt;tiling multiplication을 하면 TILE_WIDTH만큼의 global memory access 회수를 줄일 수 있다&lt;/b&gt;. 원래 구현이 N$^3$이라면, tiling multiplication에서는 2N$^2$ * $\frac{\text{N}}{\text{TILE_WIDTH}}$이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;각 thread block은 TILE_WIDTH * TILE_WIDTH개의 thread를 가진다. 예를 들어 TILE_WIDTH가 16이면 16$^2$ = 256개의 thread를 가진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 때, 각 thread block이 실행하는 연산 회수는 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;M에 대해 1번, N에 대해 1번, 총 2 * [thread 개수]만큼의 float load&lt;/li&gt;
&lt;li&gt;for문에서 TILE_WIDTH * [thread 개수] 만큼의 multiply, TILE_WIDTH * [thread 개수]만큼 add operation, 총 2 * TILE_WIDTH * [thread 개수]만큼의 mul/add&lt;/li&gt;
&lt;li&gt;그러면 1번의 load에 대해 TILE_WIDTH만큼의 연산을 할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그러나 무작정 TILE_WIDTH를 늘린다고 좋은 것은 아니다. shared memory size가 한정되어 있기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;예를 들어 SM당 shared memory가 16KB라고 하자.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;만약 TILE_WIDTH가 16이면 각 thread block은 2 * 16 * 16&amp;nbsp; * 4byte = 2KB의 shared memory를 사용하게 된다. 즉, 한 번에 8개의 thread block만 사용할 수 있다. 그러면 8 * 2 * [thread 개수]만큼의 pending load가 발생한다.&lt;/li&gt;
&lt;li&gt;반면 TILE_WIDTH가 32면 각 thread block은 2 * 32 * 32 * 4byte = 8KB의 shared memory를 사용하게 된다. 즉, 한 번에 2개의 thread block을 사용할 수 있다. 그러나 GPU의 SM당 thread는 1536개로 제한되어 있으므로 SM당 block 수가 1개로 줄어든다. 한계는 1536인데 실제로 사용하는 것은 1024개로, thread의 낭비가 발생한다!&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이처럼 shared memory size가 active thread의 개수를 한정하므로, 일반적으로는 thread block이 많은 것이 더 좋을 것이다. 그렇지만 thread block의 크기가 커지면 global memory access 회수가 줄어들므로 중간지점을 잘 잡아야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Unified Memory&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;250&quot; data-origin-height=&quot;175&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blKWKR/btszeJwZQEy/8PNwPOTEhg7t6skK4DcMY1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blKWKR/btszeJwZQEy/8PNwPOTEhg7t6skK4DcMY1/img.png&quot; data-alt=&quot;unifired memory&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blKWKR/btszeJwZQEy/8PNwPOTEhg7t6skK4DcMY1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FblKWKR%2FbtszeJwZQEy%2F8PNwPOTEhg7t6skK4DcMY1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;250&quot; height=&quot;175&quot; data-origin-width=&quot;250&quot; data-origin-height=&quot;175&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;unifired memory&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;b&gt;높은 programmability를 위해 성능을 조금 향상한 것&lt;/b&gt;. GPU와 CPU 둘 다에서 사용할 수 있는 통합용 메모리이다.&amp;nbsp;잘못 사용하는 경우 성능이 급격하게 떨어지기 때문에 잘 사용해야 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;특히 tree나 graph traversal과 같은 몇몇 알고리즘은 GPU에서 돌리기 어려운데, unified memory는 이를 가능하게 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이외에도 아래와 같은 특징들이 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;더 큰 memory를 제공한다 : GPU memory size보다 더 큰 data를 가져올 수 있다. paging mechanism을 사용해 GPU memory에 올리는 방식이다.&lt;/li&gt;
&lt;li&gt;data access가 더 쉽다 : CPU/GPU data coherence가 보장된다.&lt;/li&gt;
&lt;li&gt;unified memory는 programmability를 위한 것이기 때문에 user API가 많다. 예를 들어 cudaMemAdvise()는 어떤 memory에 접근하는지 hint를 제공하며 이를 바탕으로 성능을 향상시킨다. prefetching이라 생각하면 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;예시 : Unified Memory를 사용하지 않은 경우&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;760&quot; data-origin-height=&quot;301&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bWyyTQ/btszgxJnJDM/hT34T9iSJLtgbsS0iAKXEk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bWyyTQ/btszgxJnJDM/hT34T9iSJLtgbsS0iAKXEk/img.png&quot; data-alt=&quot;unified memory를 사용하지 않은 경우&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bWyyTQ/btszgxJnJDM/hT34T9iSJLtgbsS0iAKXEk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbWyyTQ%2FbtszgxJnJDM%2FhT34T9iSJLtgbsS0iAKXEk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;760&quot; height=&quot;301&quot; data-origin-width=&quot;760&quot; data-origin-height=&quot;301&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;unified memory를 사용하지 않은 경우&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;`cudaMalloc()`을 사용해 GPU memory를 할당하고, `cudaMemcpy()`로 data를 복사하고, CUDA kernal launch 한다. 이후 `cudaMemcpy()`를 사용해 결과값을 CPU memory로 다시 가져오고, `cudaFree()`로 GPU memory를 해제해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;예시 : Unified Memory를 사용한 경우&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;769&quot; data-origin-height=&quot;302&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6qy3m/btszcqygqrG/ZB5nBKnk3Gk5vCCxgQRw91/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6qy3m/btszcqygqrG/ZB5nBKnk3Gk5vCCxgQRw91/img.png&quot; data-alt=&quot;unified memory를 사용하는 경우&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6qy3m/btszcqygqrG/ZB5nBKnk3Gk5vCCxgQRw91/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6qy3m%2FbtszcqygqrG%2FZB5nBKnk3Gk5vCCxgQRw91%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;769&quot; height=&quot;302&quot; data-origin-width=&quot;769&quot; data-origin-height=&quot;302&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;unified memory를 사용하는 경우&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;코드가 훨씬 줄어든다. `cudaMallocManaged()`를 사용해 unified memory를 사용한다는 것을 알린다. 이 때 pointer는 CPU와 GPU 둘 다에서 사용할 수 있다. 이후 같은 pointer를 사용해 kernel launch한다. 이후 `cudaDeviceSynchronize()`로 data 처리 결과값을 받아온다. 마지막으로 `cudaFree()`한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;cudaMemcpy()는 synchronous이므로 추가적인 synchronize method가 필요없다. 반면 unified memory를 사용하는 경우 barrier를 사용한 synchronize가 필요하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;작동 방식&lt;/h3&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위 코드 예시에서 볼 수 있듯 `cudaMallocManaged()`로 호출하며,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;demand paging 방식&lt;/b&gt;으로 작동한다.&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;`cudaMallocManaged()`을 호출하면 GPU에 memory를 할당하고, CPU memory에 할당된 data를 GPU memory로 내부적으로 알아서 옮겨준다. 이 때, CPU에서 값을 수정한 후 GPU에서 접근하면 알아서 값을 옮겨준다. 그 반대도 마찬가지다.&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;한편 &lt;b&gt;page fault는 overhead가 크기 때문에 unified memory는 느릴 수 밖에&lt;/b&gt; 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;요약&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;memory와 data locality... 즉 memory hierarchy가 GPU의 성능을 결정한다. 일반적으로 GPU의 계산 속도는 빠르지만 memory bandwidth가 훨씬 작기 때문에 memory bandwidth를 덜 사용하는 것으로 throughput을 높일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이를 위해 `__shared__` - shared memory를 사용해 중복 global memory access를 줄이며, 이를 통해 memory bottleneck을 해소해 throughput을 높인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;또한, architectural trend는 portability를 높이고 programming에 대한 부담을 줄이는 쪽으로 발전하고 있다. 때문에 unified memory 등 방법이 고안되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;잘못된 내용이나 오탈자에 대한 지적, 질문&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;b&gt;등은 언제나 환영합니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>CS/Parallel Computing</category>
      <author>hyelie</author>
      <guid isPermaLink="true">https://hyelie.tistory.com/671</guid>
      <comments>https://hyelie.tistory.com/entry/%EC%9D%B4%EC%A2%85%EB%B3%91%EB%A0%AC%EC%BB%B4%ED%93%A8%ED%8C%85-Memory%EC%99%80-Data-Locality-Tiled-Multiplication-Unified-Memory#entry671comment</comments>
      <pubDate>Fri, 27 Oct 2023 00:38:37 +0900</pubDate>
    </item>
    <item>
      <title>[이종병렬컴퓨팅] CUDA Basics</title>
      <link>https://hyelie.tistory.com/entry/%EC%9D%B4%EC%A2%85%EB%B3%91%EB%A0%AC%EC%BB%B4%ED%93%A8%ED%8C%85-CUDA-Basics</link>
      <description>&lt;blockquote style=&quot;text-align: left;&quot; data-ke-style=&quot;style2&quot;&gt;이 글은 포스텍 성효진 교수님의 이종병렬컴퓨팅(CSED490C) 강의를 기반으로 재구성한 것입니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 글에서는 CUDA programming language를 사용하는 방법을 알아본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;CUDA&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;CUDA는 &lt;b&gt;NVIDIA GPU 전용 software&lt;/b&gt;이다. 기본적으로는 C/C++이며 여기에 몇몇 library를 추가해서 쓸 수 있다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;CUDA kernel을 사용하고 실행하는 방법&lt;/li&gt;
&lt;li&gt;GPU memory를 관리하는 방법&lt;/li&gt;
&lt;li&gt;communication과 synchronization을 관리하는 방법&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Host와 Device&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;host memory : CPU의 memory&lt;/li&gt;
&lt;li&gt;device memory : GPU의 memory&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;heterogeneous computing은 serialize한 부분과 parallel한 부분이 나뉜다. serialize한 부분은 CPU가, parallel한 부분은 GPU가 실행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Heterogeneous Computing의 단계&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;990&quot; data-origin-height=&quot;531&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d6U5eG/btszb8DeIxu/o71ugKRvCMTqlPuLaUWAa1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d6U5eG/btszb8DeIxu/o71ugKRvCMTqlPuLaUWAa1/img.png&quot; data-alt=&quot;heterogenous computing 단계&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d6U5eG/btszb8DeIxu/o71ugKRvCMTqlPuLaUWAa1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd6U5eG%2Fbtszb8DeIxu%2Fo71ugKRvCMTqlPuLaUWAa1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;774&quot; height=&quot;415&quot; data-origin-width=&quot;990&quot; data-origin-height=&quot;531&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;heterogenous computing 단계&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;실행되는 방법은 크게 아래와 같다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;CPU memory에 있는 data를 GPU로 옮긴다.&lt;/li&gt;
&lt;li&gt;kernel(GPU program)을 실행한다. 그러면 GPU는 안에 있는&amp;nbsp; cache에 값을 쓴다.&lt;/li&gt;
&lt;li&gt;필요 시 GPU memory에 있는 결과를 CPU로 옮긴다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;CUDA Programming&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Hello World!&lt;/h3&gt;
&lt;pre id=&quot;code_1698252234818&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;__global__ void mykernel(void) {
}

int main(void) {
    mykernel&amp;lt;&amp;lt;&amp;lt;1,1&amp;gt;&amp;gt;&amp;gt;();
    printf(&quot;Hello World!\n&quot;);
    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위 코드에서 `__global__`로 선언한 함수가 GPU에서 실행된다. 이를 호출하기 위해서는 기본적인 function call과 동일하지만 `&amp;lt;&amp;lt;&amp;lt;&amp;gt;&amp;gt;&amp;gt;`를 추가해야 한다. 여기에 들어가는 숫자는 grid의 크기, thread block의 크기이며 작업을 어떤 단위로 나눌지에 대한 숫자인데, 후술하겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 코드가 컴파일되면 `__global__`이 붙은 부분은 nvcc가 컴파일하며, GPU에서 실행시키기 위해 한 binary executable file로 바뀐다. 나머지 부분은 gcc가 컴파일하며, CPU에서 실행시키기 위해 또다른 binary executable file로 바뀐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;`mykernel&amp;lt;&amp;lt;&amp;lt;1,1&amp;gt;&amp;gt;&amp;gt;()` 이 부분을 &lt;b&gt;kernel launch&lt;/b&gt;라고 하며, 이 코드가 GPU에서 실행된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Vector Addition&lt;/h3&gt;
&lt;pre id=&quot;code_1698252517552&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;__global__ void add(int *a, int *b, int *c) {
    *c = *a + *b;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;vector addition을 위해서는 위와 같이 코드를 쓴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 때 a, b, c는 GPU에서 실행되기 때문에 device memory에 올라가 있어야 한다. 따라서 GPU에 미리 값을 올려 두어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Memory Management&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;b&gt;host memory와 device memory는 별개&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;즉, host memory에 있는 값들은 device로 전달되거나 받아올 수 있지만 device에서 dereference되지 않는다. 같은 이유로 device memory에 있는 값들은 host로 전달되거나 받아올 수 있지만 host에서 deference되지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이를 위해 `cudaMalloc()`, `cudaFree()`, `cudaMemcpy()`를 사용해 device memory를 할당하고, 해제하고, 복사한다. C의 malloc(), free(), memcpy()와 동일하다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;cudaMalloc ( void** devPtr, size_t size ) : device의 global memory에 memory를 할당한다.&lt;/li&gt;
&lt;li&gt;cudaFree ( void* devPtr ) : device의 global memory에서 memory를 할당 해제한다.&lt;/li&gt;
&lt;li&gt;cudaMemcpy ( void* dst, const void* src, size_t count, cudaMemcpyKind kind ) : host memory와 device memory에서 정보를 옮긴다. asynchronous하다!
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;cudaMemcpyKind는 `cudaMemcpyDeviceToHost`와 `cudaMemcpyHostToDevice`, `cudaMemcpyDeviceToDevice`가 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;기본적인 틀&lt;/h3&gt;
&lt;pre id=&quot;code_1698252757425&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;__global__ void add(int *a, int *b, int *c) {
	*c = *a + *b;
}

int main(void) {
	// 1.
    int a, b, c; // host copies of a, b, c
    int *d_a, *d_b, *d_c; // device copies of a, b, c
    int size = sizeof(int);
    
    // 2. Allocate space for device copies of a, b, c
    cudaMalloc((void **)&amp;amp;d_a, size);
    cudaMalloc((void **)&amp;amp;d_b, size);
    cudaMalloc((void **)&amp;amp;d_c, size);
    
    // 3. Setup input values
    a = 2;
    b = 7;
    
    // 4. Copy inputs to device
    cudaMemcpy(d_a, &amp;amp;a, size, cudaMemcpyHostToDevice);
    cudaMemcpy(d_b, &amp;amp;b, size, cudaMemcpyHostToDevice);
    
    // 5. Launch add() kernel on GPU
    add&amp;lt;&amp;lt;&amp;lt;1,1&amp;gt;&amp;gt;&amp;gt;(d_a, d_b, d_c);
    
    // 6. Copy result back to host
    cudaMemcpy(&amp;amp;c, d_c, size, cudaMemcpyDeviceToHost);
    
    // 7. Cleanup
    cudaFree(d_a); cudaFree(d_b); cudaFree(d_c);
    
    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위 코드와 같으며, 글로 표현하면 다음과 같다. 각 method의 사용 방법은 공식 문서를 찾아보는 것이 좋다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;host에 할당될 변수, device에 할당될 변수를 선언한다.&lt;/li&gt;
&lt;li&gt;cudaMalloc을 사용해 device에 memory를 할당한다.&lt;/li&gt;
&lt;li&gt;host에 값을 쓴다. (필요 시 host에 memory를 할당하고 값을 써야 할 수도 있다.)&lt;/li&gt;
&lt;li&gt;cudaMemcpy()를 사용해 host에서 device로 값을 복사한다.&lt;/li&gt;
&lt;li&gt;kernel을 호출한다.&lt;/li&gt;
&lt;li&gt;kernel의 실행&amp;nbsp; 결과는 device에 있으므로, 이 값을 host로 옮겨온다.&lt;/li&gt;
&lt;li&gt;cudaFree()를 사용해 device에 할당한 memory를 해제한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Parallel하게 실행하는 방법&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Block&lt;/h3&gt;
&lt;pre id=&quot;code_1698253000028&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;__global__ void add(int *a, int *b, int *c) {
	c[blockIdx.x] = a[blockIdx.x] + b[blockIdx.x];
}

add&amp;lt;&amp;lt;&amp;lt;N, 1&amp;gt;&amp;gt;&amp;gt;();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위 코드처럼 `add&amp;lt;&amp;lt;&amp;lt;1, 1&amp;gt;&amp;gt;&amp;gt;()` 대신 `add&amp;lt;&amp;lt;&amp;lt;N, 1&amp;gt;&amp;gt;&amp;gt;()`로 호출하면 add()를 N번 병렬로 실행한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 때 add() 함수의 parallel한 호출을 &lt;b&gt;block&lt;/b&gt;이라고 하며, block의 집합을 grid라고 한다. 여기서 &lt;b&gt;N은 block 개수&lt;/b&gt;를 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 때 각 호출에서 `blockIdx.x`를 사용해 thread index를 잡을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;774&quot; data-origin-height=&quot;77&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GnzG7/btszbisRXPx/zmKiydWwfxdLwXHTFtSJC1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GnzG7/btszbisRXPx/zmKiydWwfxdLwXHTFtSJC1/img.png&quot; data-alt=&quot;add&amp;amp;lt;&amp;amp;lt;&amp;amp;lt;4, 1&amp;amp;gt;&amp;amp;gt;&amp;amp;gt;()의 실행 분석&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GnzG7/btszbisRXPx/zmKiydWwfxdLwXHTFtSJC1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGnzG7%2FbtszbisRXPx%2FzmKiydWwfxdLwXHTFtSJC1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;774&quot; height=&quot;77&quot; data-origin-width=&quot;774&quot; data-origin-height=&quot;77&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;add&amp;lt;&amp;lt;&amp;lt;4, 1&amp;gt;&amp;gt;&amp;gt;()의 실행 분석&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Thread&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;각 block은 thread로 구성되고, block 내의 thread 또한 parallel하게 동작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1698254033831&quot; class=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;cpp&quot;&gt;&lt;code&gt;__global__ void add(int *a, int *b, int *c) {
	c[threadIdx.x] = a[threadIdx.x] + b[threadIdx.x];
}

add&amp;lt;&amp;lt;&amp;lt;1, N&amp;gt;&amp;gt;&amp;gt;();&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위 코드처럼 `add&amp;lt;&amp;lt;&amp;lt;1, N&amp;gt;&amp;gt;&amp;gt;()`로 호출해도 add()는 N번 병렬로 실행한다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;단, 위의 경우는 block-parallel이었지만 여기서는 thread-parallel이다. 때문에 위에서 `blockIdx.x`를 사용한 대신 여기서는 `threadIdx.x`를 사용한다.&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;Block과 Thread 합치기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그 전에 앞에서 살폈던 개념을 합쳐보자.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;thread&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: sequential한&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;실행 단위&lt;/b&gt;. 따라서 thread는 parallel하게 동작하며, 같은 sequential program을 실행한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;thread block&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;:&lt;b&gt; thread들의 그룹&lt;/b&gt;이다. 하나의&lt;span&gt;&amp;nbsp;&lt;/span&gt;SM - streaming multiprocessor - 에서 실행되며, block 내의 thread는 synchronize할 수 있고, shared memory를 사용해 communication할 수 있기에 synchronization과 data 교환이 빠르다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;grid&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: &lt;b&gt;thread block의 집합&lt;/b&gt;.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;grid의 thread block은 여러 개의 SM에서 실행된다.&lt;/li&gt;
&lt;li&gt;thread block끼리는 synchronization이 일어나지 않는다.&lt;/li&gt;
&lt;li&gt;thread block끼리 communication cost는 매우 높다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;434&quot; data-origin-height=&quot;402&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b4ycbL/btszelaLT99/SkrqEQ7RZG8I2mlaFxJEU1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b4ycbL/btszelaLT99/SkrqEQ7RZG8I2mlaFxJEU1/img.png&quot; data-alt=&quot;execution model과 hardware&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b4ycbL/btszelaLT99/SkrqEQ7RZG8I2mlaFxJEU1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb4ycbL%2FbtszelaLT99%2FSkrqEQ7RZG8I2mlaFxJEU1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;434&quot; height=&quot;402&quot; data-origin-width=&quot;434&quot; data-origin-height=&quot;402&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;execution model과 hardware&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;thread는 core에 매핑된다. - core에서 실행된다.&lt;/li&gt;
&lt;li&gt;thread block은 SM에 매핑된다 - SM에서 실행된다.&lt;/li&gt;
&lt;li&gt;grid는 device에 매핑된다. - device에서 실행된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Block끼리는 independent&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;b&gt;block끼리는 indepdent&lt;/b&gt;여야 한다. &lt;b&gt;synchronization과 communication cost가 매우 높기 때문&lt;/b&gt;에 dependency가 없어야 한다. dependency가 없는 경우 순서 없이, parallel하게 실행할 수 있기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;ID와 Dimension&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;thread는 1D, 2D, 3D ID를 가질 수 있으며, block 내부에서 고유하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;block도 동일하게 1D, 2D, 3D ID를 가질 수 있으며 grid 내부에서 고유하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 dimension은 kernel을 시작할 때 결정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;아래와 같은 내장 변수들이 있으며, 이를 사용해서 n차원에 대한 memory addressing을 단순화한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;threadIdx&lt;/b&gt; : `threadIdx.x`, `threadIdx.y`, `threadIdx.z`로 표기하며, 각각&amp;nbsp;&lt;b&gt;block 내부의 thread index&lt;/b&gt;를 의미한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;blockIdx&lt;/b&gt; :&lt;span&gt;&amp;nbsp;&lt;/span&gt;`&lt;span style=&quot;text-align: left;&quot;&gt;blockIdx&lt;/span&gt;.x`, `&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;text-align: left;&quot;&gt;blockIdx&lt;/span&gt;.y`, `&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;text-align: left;&quot;&gt;blockIdx&lt;/span&gt;.z`로 표기하며, 각각&amp;nbsp;&lt;b&gt;grid 내부의 block index&lt;/b&gt;를 의미한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;blockDim&lt;/b&gt; : `&lt;span style=&quot;text-align: left;&quot;&gt;blockDim&lt;/span&gt;.x`, ` &lt;span style=&quot;text-align: left;&quot;&gt;blockDim&lt;/span&gt;.y`, ` &lt;span style=&quot;text-align: left;&quot;&gt;blockDim&lt;/span&gt;.z`로 표기하며,&amp;nbsp;&amp;nbsp;&lt;b&gt;block에 있는 thread의 개수&lt;/b&gt;를 의미한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;gridDim&lt;/b&gt; : `gridDim&lt;span style=&quot;text-align: left;&quot;&gt;.x`, `&lt;span&gt; grid&lt;/span&gt;&lt;/span&gt;Dim&lt;span style=&quot;text-align: left;&quot;&gt;.y`, `&lt;span&gt; grid&lt;/span&gt;&lt;/span&gt;Dim.z`로 표기하며, &lt;b&gt;grid에 있는 block의 개수&lt;/b&gt;를 의미한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;`dim3` type을 가진 변수들은 dimension을 초기화하기 위해 사용한다. 값을 넣지 않은 것들은 1로 초기화된다. 예를 들어 dim3 `gridDim(256)`이라고 했으면 gridDim.x는 256, gridDim.y와 gridDim.z는 1이다. &amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;예시&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;291&quot; data-origin-height=&quot;354&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/55qFw/btsy9MOsEFz/wIWIJBNjsk97dkGTt2KDM1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/55qFw/btsy9MOsEFz/wIWIJBNjsk97dkGTt2KDM1/img.png&quot; data-alt=&quot;grid, block, thread index&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/55qFw/btsy9MOsEFz/wIWIJBNjsk97dkGTt2KDM1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F55qFw%2Fbtsy9MOsEFz%2FwIWIJBNjsk97dkGTt2KDM1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;291&quot; height=&quot;354&quot; data-origin-width=&quot;291&quot; data-origin-height=&quot;354&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;grid, block, thread index&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위 예시에서 grid 1에는 총 6개의 block이 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;gridDim : gridDim.x는 3, gridDim.y는 2, gridDim.z는 1&lt;/li&gt;
&lt;li&gt;blockDim : blockDim.x는 5, blockDim.y는 3, blockDim.z는 1&lt;/li&gt;
&lt;li&gt;blockIdx와 threadIdx는 그림에 적혀 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Kernel Launch에서 ID&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그럼 `func&amp;lt;&amp;lt;&amp;lt;gridDim, blockDim&amp;gt;&amp;gt;&amp;gt;();`를 살펴보자.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;gridDim은 grid에 있는 block의 개수&lt;/b&gt;를 정의한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;blockDim은 block에 있는 thread의 개수&lt;/b&gt;를 정의한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;예시&lt;/h4&gt;
&lt;pre id=&quot;code_1698255683209&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;dim3 threadPerBlock(16, 16);
dim3 numBlocks(N/threadsPerBlock.x, N/threadsPerBlock.y);
MatAdd&amp;lt;&amp;lt;&amp;lt;numBlocks, threadsPerBlock&amp;gt;&amp;gt;&amp;gt;(A, B, C);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;예를 살펴보자. input은 N by N의 matrix이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;threadPerBlock, 즉 blockDim은 각 block에 thread를 16 by 16으로 정의하겠다는 것이고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;numBlocks, 즉 gridDim은 grid에 block을 N/blockDim.x by N/blockDim.y로 정의하겠다는 것이다. - 이거는 input을 이렇게 나누어야 모든 input을 처리할 수 있기에 이렇게 두는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Dimension 결정&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;grid와 block의 크기를 설정하기 위해서는 다음과 같은 기준을 따라야 한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;전체 입력을 처리하고, GPU를 busy한 상태로 처리하기 위해서는 충분한 양의 thread가 필요하다.&lt;/li&gt;
&lt;li&gt;block size의 선택은 warp 점유율과 관련한 최적화 단계이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;무작정 크게 둔다고 좋은 것이 아니라, 한계도 있다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;grid의 경우 (x, y, z)가 (2$^{31}$ - 1, 65535, 65535)&lt;/li&gt;
&lt;li&gt;thread block의 경우 (x, y, z)가 (1024, 1024, 64)&lt;/li&gt;
&lt;li&gt;block당 thread의 최대 개수는 1024개이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Indexing&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;자. 지금까지 block과 thread에 대해 살펴봤다. indexing을 해 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;724&quot; data-origin-height=&quot;125&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JsNXl/btsy8jzwjlF/uQMd2Ko8i8EG7AW4lV4kL1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JsNXl/btsy8jzwjlF/uQMd2Ko8i8EG7AW4lV4kL1/img.png&quot; data-alt=&quot;indexing 예시&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JsNXl/btsy8jzwjlF/uQMd2Ko8i8EG7AW4lV4kL1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJsNXl%2Fbtsy8jzwjlF%2FuQMd2Ko8i8EG7AW4lV4kL1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;724&quot; height=&quot;125&quot; data-origin-width=&quot;724&quot; data-origin-height=&quot;125&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;indexing 예시&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위 예시는 block당 8개의 thread를 사용하고 block은 4개를 사용하는 상황이다. 즉 blockDim.x는 8이라는 말!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;여기서 thread index를 원하는 위치에 잡는 방법은 `blockIdx.x * blockDim.x + threadIdx.x`이다.&amp;nbsp;아래 내용을 복기하면서, 왜 이렇게 나오나 생각해보자.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;threadIdx : block 내부의 thread index를 의미&lt;br /&gt;blockIdx : grid 내부의 block index를 의미&lt;br /&gt;blockDim : block에 있는 thread의 개수를 의미&lt;br /&gt;gridDim : grid에 있는 block의 개수를 의미&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;최종 형태&lt;/h3&gt;
&lt;pre id=&quot;code_1698256305924&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;__global__ void add(int *a, int *b, int *c, int n) {
     int index = threadIdx.x + blockIdx.x * blockDim.x;
     if (index &amp;lt; n)
         c[index] = a[index] + b[index];
}

add&amp;lt;&amp;lt;&amp;lt;(N + M-1) / M,M&amp;gt;&amp;gt;&amp;gt;(d_a, d_b, d_c, N);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그러면 최종적으로 위와 같은 형태가 나온다. index를 검사하는 이유는, &lt;b&gt;input이 blockDim의 배수가 아닌 경우가 많기 때문&lt;/b&gt;에 이에 대한 예외를 처리하기 위해서이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;kernal launch는 `(N+M-1)/M, M`으로 되었는데, M은 block에 있는 thread의 개수이고, `(N+M-1)/M`은 N/M의 결과를 올림하기 위한 연산이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Managing Device&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;b&gt;kernal launch는 asynchronous&lt;/b&gt;하다. 때문에 kernal launch 이후 control이 CPU로 바로 돌아온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;따라서 결과를 사용하기 전에 &lt;b&gt;synchonize&lt;/b&gt;를 무조건 해 주어야 한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;cudaMemcpy() : copy가 끝날 때까지 CPU 실행을 block한다. CUDA call이 끝나야 copy를 시작한다.&lt;/li&gt;
&lt;li&gt;cudaMemcpyAsync() : asynchronous하며, CPU를 block하지 않는다.&lt;/li&gt;
&lt;li&gt;cudaDeviceSynchronize() : CUDA call이 끝날 때 까지 CPU를 block한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;기본적으로 host(CPU)가 device(GPU) memory를 관리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Unified Memory Support&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; unified memory는 system의 모든 processor에서 접근할 수 있는 single address space이다. CPU와 GPU 둘 모두에서 해당 memory에 읽고 쓸 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;malloc()에 대한 호출을 cudaMallocManaged()로 호출한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1698257513604&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;__global__ void add(int n, float *x, float *y){
    int index = blockIdx.x * blockDim.x + threadIdx.x;
    int stride = blockDim.x * gridDim.x;
    for (int i = index; i &amp;lt; n; i += stride)
    y[i] = x[i] + y[i];
}

int main(void)
    int N = 1&amp;lt;&amp;lt;20;
    float *x, *y,
    cudaMallocManaged(&amp;amp;x, N*sizeof(float));
    cudaMallocManaged(&amp;amp;y, N*sizeof(float));
    &amp;hellip; // initialization
    add&amp;lt;&amp;lt;&amp;lt;numBlocks, blockSize&amp;gt;&amp;gt;&amp;gt;(N, x, y);
    &amp;hellip;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;예시 코드는 위와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;CUDA Device Memory Space Overview&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;381&quot; data-origin-height=&quot;417&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bWNJ2M/btszekXfItt/knkicRCoUumVOKPmt3ohh0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bWNJ2M/btszekXfItt/knkicRCoUumVOKPmt3ohh0/img.png&quot; data-alt=&quot;CUDA device memory space overview&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bWNJ2M/btszekXfItt/knkicRCoUumVOKPmt3ohh0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbWNJ2M%2FbtszekXfItt%2FknkicRCoUumVOKPmt3ohh0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;381&quot; height=&quot;417&quot; data-origin-width=&quot;381&quot; data-origin-height=&quot;417&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;CUDA device memory space overview&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각 thread들은
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;thread별 register에 read/write할 수 있다.&lt;/li&gt;
&lt;li&gt;thread별 local memory에 read/write할 수 있다.&lt;/li&gt;
&lt;li&gt;block별 shared memory에 read/write할 수 있다.&lt;/li&gt;
&lt;li&gt;grid별 global memory에 read/write할 수 있다.&lt;/li&gt;
&lt;li&gt;grid별 constant memory나 texture memory에 read only이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;host는 global, constant, texture memory에 read/write할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;잘못된 내용이나 오탈자에 대한 지적, 질문&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;b&gt;등은 언제나 환영합니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>CS/Parallel Computing</category>
      <author>hyelie</author>
      <guid isPermaLink="true">https://hyelie.tistory.com/670</guid>
      <comments>https://hyelie.tistory.com/entry/%EC%9D%B4%EC%A2%85%EB%B3%91%EB%A0%AC%EC%BB%B4%ED%93%A8%ED%8C%85-CUDA-Basics#entry670comment</comments>
      <pubDate>Thu, 26 Oct 2023 19:31:00 +0900</pubDate>
    </item>
    <item>
      <title>[이종병렬컴퓨팅] GPU architectures - NVIDIA</title>
      <link>https://hyelie.tistory.com/entry/%EC%9D%B4%EC%A2%85%EB%B3%91%EB%A0%AC%EC%BB%B4%ED%93%A8%ED%8C%85-GPU-architectures-NVIDIA</link>
      <description>&lt;blockquote style=&quot;text-align: left;&quot; data-ke-style=&quot;style2&quot;&gt;이 글은 포스텍 성효진 교수님의 이종병렬컴퓨팅(CSED490C) 강의를 기반으로 재구성한 것입니다.&lt;/blockquote&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 글에서는 다음과 같은 내용들을 살핀다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;GPU architecture의 예시 - NVIDIA&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;NVIDIA GPU Architecture&lt;/h2&gt;
&lt;h3 style=&quot;text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;용어 정리&lt;/h3&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 26.938%; text-align: center;&quot;&gt;&lt;b&gt;NVIDIA&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 18.1006%; text-align: center;&quot;&gt;&lt;b&gt;AMD&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 54.9612%; text-align: center;&quot;&gt;&lt;b&gt;뜻&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 26.938%; text-align: center;&quot;&gt;kernel&lt;/td&gt;
&lt;td style=&quot;width: 18.1006%; text-align: center;&quot;&gt;kernel&lt;/td&gt;
&lt;td style=&quot;width: 54.9612%;&quot;&gt;&lt;b&gt;GPU의 multiple thread에 의해 동작하는 함수&lt;/b&gt;. CPU와 parallel하게 동작할 수 있다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 26.938%; text-align: center;&quot;&gt;thread block&lt;/td&gt;
&lt;td style=&quot;width: 18.1006%; text-align: center;&quot;&gt;work group&lt;/td&gt;
&lt;td style=&quot;width: 54.9612%;&quot;&gt;다른 data에 대해 같은 kernel을 실행하는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;thread의 그룹&lt;/b&gt;. 단일 SM/CU에서 warp의 그룹으로 실행된다. 내부의 thread끼리는 communicate할 수 있는데 이는 hardward에 의해 지원된다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 26.938%; text-align: center;&quot;&gt;thread&lt;/td&gt;
&lt;td style=&quot;width: 18.1006%; text-align: center;&quot;&gt;work item / thread&lt;/td&gt;
&lt;td style=&quot;width: 54.9612%;&quot;&gt;warp의 개별 실행 단위&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 26.938%; text-align: center;&quot;&gt;streaming multiprocessor (SM)&lt;/td&gt;
&lt;td style=&quot;width: 18.1006%; text-align: center;&quot;&gt;compute unit (CU)&lt;/td&gt;
&lt;td style=&quot;width: 54.9612%;&quot;&gt;parallel ALU를 포함하는 GPU의 parallel vector processor 중 하나.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 26.938%; text-align: center;&quot;&gt;warp&lt;/td&gt;
&lt;td style=&quot;width: 18.1006%; text-align: center;&quot;&gt;wavefront&lt;/td&gt;
&lt;td style=&quot;width: 54.9612%;&quot;&gt;lock step에서 실행되고, 동일한 instruction을 실행하며, 같은 control flow path를 공유하는 작업 단위, mask될 수 있으며, hardward thread에 의해 vectorize된다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;thread block의 크기에 따라, 즉&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;thread block의 크기가 너무 작은 경우(thread block에 thread 1개) : thread block의 개수가 streaming multiprocessor보다 더 많아져서 scheduling overhead가 너무 커진다.&lt;/li&gt;
&lt;li&gt;thread block의 크기가 너무 큰 경우(thread block에 thread 개수가 data size와 동일) : 1개의 thread block, data size개의 thread가 있는 경우 그러면 100개의 streaming multiprocessor가 있더라도 1개밖에 사용하지 못하기 때문에 비효율적이다. 때문에 적당히 나눠야 하며, 나눈 thread block들은 streaming multiprocessor에 적당히 할당되어 실행된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;NVIDIA Memory Hierarchy&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;425&quot; data-origin-height=&quot;484&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vYXrs/btszbNMNea4/GysKLLCeklTwhywm7Jtpj0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vYXrs/btszbNMNea4/GysKLLCeklTwhywm7Jtpj0/img.png&quot; data-alt=&quot;thread, thread block, grid&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vYXrs/btszbNMNea4/GysKLLCeklTwhywm7Jtpj0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvYXrs%2FbtszbNMNea4%2FGysKLLCeklTwhywm7Jtpj0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;425&quot; height=&quot;484&quot; data-origin-width=&quot;425&quot; data-origin-height=&quot;484&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;thread, thread block, grid&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;thread&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: kernel의 instance로, thread만이 접근할 수 있는 private memory가 있다. thread block 내의 thread ID, PC, register, input/output 결과들이 이 memory에 저장된다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;thread block&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 동시에 실행되는 thread의 집합. block별 shared memory가 있으며, barrirer나 shared memory를 통해 communicate한다. thread block을 식별하기 위한 block ID가 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;grid&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 동일한 kernel을 실행하는 thread block의 array. 즉 grid는 전체 kernel이므로 global memory에 읽고 쓰며, 이를 통해 global synchronization을 한다. 단 thread block의 barrier나 shared memory를 쓰는 것이 훨씬 빠르다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;NVIDIA Fermi&lt;/h2&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;16개의 SM, 총 512개의 core가 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;631&quot; data-origin-height=&quot;725&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dA9304/btszbW3Z5uN/mNEDQ42QhsOlvEzB8G4sL0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dA9304/btszbW3Z5uN/mNEDQ42QhsOlvEzB8G4sL0/img.png&quot; data-alt=&quot;NVIDIA Fermi SM&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dA9304/btszbW3Z5uN/mNEDQ42QhsOlvEzB8G4sL0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdA9304%2FbtszbW3Z5uN%2FmNEDQ42QhsOlvEzB8G4sL0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;502&quot; height=&quot;577&quot; data-origin-width=&quot;631&quot; data-origin-height=&quot;725&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;NVIDIA Fermi SM&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;각 SM은 위 그림과 같이 생겼다. warp scheduler는 2개, 각 warp에는 32개의 thread가 있다. SM에는 2개의 exeuction unit, 32개의 core가 있다.&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;여기서 core 내부에 register가 있는 것이 아니라 외부에 register가 있기 때문에 각 core는 연산을 하기 위해 register에서 값을 가져오고, register에 값을 쓰는 연산을 했다. core 내부에는 floating point unit과 integer unit 2개가 같이 있었다.&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;또 shared memory외 L1 cache의 역할을 둘 다 하는 memory가 하나 있다. 현대의 GPU들은 L1 cache용 memory와 shared memory용 memory가 따로 있지만, 그 당시에는 이렇게 사용했다.&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;Fermi의 Thread Scheduler&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;chip level : thread block을 SM에 할당한다. (thread block scheduler)&lt;/li&gt;
&lt;li&gt;sm level : warp와 warp의 exeuction unit에 대해 작동한다. (warp scheduler)&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;warp scheduler는 한 cycle에 warp에서 하나의 instruction을 가져온다. 이 방식은 매 cycle마다 다른 warp에서 instruction을 가져오기에 fine grained multithreading이다. 이 방식을 사용하면 thread instruction 간의 dependency를 신경쓰지 않아도 된다는 장점이 있다.&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;Fermi Memory Hierarchy&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;398&quot; data-origin-height=&quot;465&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7k1UQ/btsy9NzOt0Z/BAbWhaHyNwIUa8Zt3xuZak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7k1UQ/btsy9NzOt0Z/BAbWhaHyNwIUa8Zt3xuZak/img.png&quot; data-alt=&quot;fermi memory hierarchy&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7k1UQ/btsy9NzOt0Z/BAbWhaHyNwIUa8Zt3xuZak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7k1UQ%2Fbtsy9NzOt0Z%2FBAbWhaHyNwIUa8Zt3xuZak%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;398&quot; height=&quot;465&quot; data-origin-width=&quot;398&quot; data-origin-height=&quot;465&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;fermi memory hierarchy&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;host memory는 CPU의 memory이며, host memory는 device memory와 연결되어 있다. 따라서 CPU에서 사용한 값을 GPU에서 사용하기 위해서는 host memory에서 device memory로 옮기는 과정이 필수적이다.&lt;/li&gt;
&lt;li&gt;L2 cache는 모든 thread에 의해 공유되며, 모든 SM이 이 memory에 있는 값을 볼 수 있다. synchornization variable이 L2 cache에 있으면 더 빨리 쓸 수 있기 때문에 필요하다.&lt;/li&gt;
&lt;li&gt;모든 SM에는 L1 cache가 있다. 앞서 언급했듯 이 시대의 L1 cache와 shared memory는 하나로 합쳐져 작동했다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;여기서 shared memory는 user-managed cache이며, 사용자가 값을 caching할 memory이다. 특정 값을 저장하기 위해 programmer가 explicit하게 값을 저장하는 공간이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Register는 위 그림에서는 분리되어 있는 것처럼 보이지만 실제로는 매우 큰 register가 SM별로 할당되어 있는 것이다.&lt;/li&gt;
&lt;li&gt;한 SM에 대해 active한 warp의 수를 신경써야 한다. GPU의 경우 register에 context switching을 위한 정보를 넣는데, 한 SM당 너무 많은 warp를 사용하면 이 register가 부족해지기 때문이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;Kepler (2014)&lt;/h2&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;double precision floating point 연산 속도를 향상시켰다.&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;4개의 warp scheduler, dual instruction dispatch unit이 있다. ILP한 방식으로 indepdendent한 warp를 가져와서 2개의 independent instruction을 실행시킨다. 만약 못 찾으면 그냥 하나만 실행한다. - 즉, ILP, TLP를 모두 실행하는 방법이다.&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;Pascal (2016)&lt;/h2&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;NVLink를 사용한다. NVIDIA GPU 전용으로 구성된 link이며 속도가 매우 빠르다. 여러 개의 GPU를 사용할 때를 고려한 기술이다.&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;unified memory를 지원한다. pascal 이전의 unified memory는 software가 사용하는 방식이었으며, CPU memory와 GPU memory 사이에 데이터를 옮길 필요 없이 사용하는 통합된 memory였다. 때문에 CPU와 GPU 둘 다에서 모두 사용할 수 있었다. 이 memory는 unified virtual memory space를 제공했다.&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;Volta (2017)&lt;/h2&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;tensor core를 사용한다는 점이 제일 큰 차이점이다. 때문에 더 유연한 thread scheduling을 할 수 있게 되었다. 이 때부터는 각 thread의 execution state가 저장되므로 각각의 PC와 call stack을 저장한다. 이 방식을 사용한다고 SIMT가 아닌 것은 아니다. 실제 실행은 같은 PC를 가진 thread만 실행하기 때문이다.&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이전에는 barrier가 thread block level로 작동했는데, 여기부터는 sub-block과 multiblock에서도 작동하게 되었다.&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;Tensor Core&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;925&quot; data-origin-height=&quot;249&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FjaVq/btszb4U7hmq/rnjpaD3QelVwZK6G1Gp0aK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FjaVq/btszb4U7hmq/rnjpaD3QelVwZK6G1Gp0aK/img.png&quot; data-alt=&quot;tensor core&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FjaVq/btszb4U7hmq/rnjpaD3QelVwZK6G1Gp0aK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFjaVq%2Fbtszb4U7hmq%2FrnjpaD3QelVwZK6G1Gp0aK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;756&quot; height=&quot;204&quot; data-origin-width=&quot;925&quot; data-origin-height=&quot;249&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;tensor core&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;tensor core는 행렬 계산을 위한 특별한 연산 단위이다. 각 tensor cores는 cycle 하나에 4 by 4 행렬의 곱셈과 덧셈 연산을 수행한다.&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;810&quot; data-origin-height=&quot;342&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/p2k9B/btszbV47GXN/kB7poi76SjSr0MR7k95b8k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/p2k9B/btszbV47GXN/kB7poi76SjSr0MR7k95b8k/img.png&quot; data-alt=&quot;tensor core의 사용&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/p2k9B/btszbV47GXN/kB7poi76SjSr0MR7k95b8k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fp2k9B%2FbtszbV47GXN%2FkB7poi76SjSr0MR7k95b8k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;810&quot; height=&quot;342&quot; data-origin-width=&quot;810&quot; data-origin-height=&quot;342&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;tensor core의 사용&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;각 warp가 16 by 16의 결과를 계산한다고 하자. tensor core는 4 by 4 크기이기에, 4개의 4 by 4 size로 입력을 나눈다. 그러면 각각의 4 by 4 행렬은 하나의 cycle에서 tensor core가 실행하고 결과를 만들 수 있다.&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 block을 어떻게 thread block으로 나누는지는 이후에 다룬다. input이 shared memory에 저장된 경우, 하나의 thread block당 여러 개의 tile을 계산할 수 있다. 이 방법도 나중에 다룬다.&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;660&quot; data-origin-height=&quot;322&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bz4ENe/btszcI5jbis/4xloNfinZ5gSUp5IxUyaSk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bz4ENe/btszcI5jbis/4xloNfinZ5gSUp5IxUyaSk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bz4ENe/btszcI5jbis/4xloNfinZ5gSUp5IxUyaSk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbz4ENe%2FbtszcI5jbis%2F4xloNfinZ5gSUp5IxUyaSk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;660&quot; height=&quot;322&quot; data-origin-width=&quot;660&quot; data-origin-height=&quot;322&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;기존 방식의 경우, 행렬 연산을 위해서는 warp 내에 있는 32개의 thread가 병렬로 작동하고, 이후에 barrier가 작동하고 연산 결과를 tensor core가 작동하면서 취합하는 방식이었다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;Hopper (2023)&lt;/h2&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;여기서부터는 tensor core gpu라고 부른다. tensor core를 위해 fetch 속도를 더 빠르게 했다.&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;thread block과 thread block hierarchy에 대해 새로운 수준의 parallelism을 도입했다. (thread - thread block - thread block cluster - grid 순서) 이를 통해 SM끼리 data를 더 쉽게 공유할 수 있게 되었다.&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;기술적 진화&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;interconnect와 memory bandwidth 확장으로 인해 CPU-GPU, GPU-GPU 간 병목을 줄였다. 이외에도 NVLink(point to point)나 NVSwitch(bus)가 도입되었다.&lt;/li&gt;
&lt;li&gt;tensor core, RT cores를 사용해 heterogeneity가 증가했다.&lt;/li&gt;
&lt;li&gt;더 flexible해졌다. thread끼리 더 fine-grained synchronization해졌고, thread block끼리 synchronization이 되고, 이를 지원하는 scheduler가 있다.&lt;/li&gt;
&lt;li&gt;unified memory와 hardware coherence를 지원해서 host-device data communication이 더 쉬워졌다.&lt;/li&gt;
&lt;li&gt;GPU virtual machine을 위한 보안 기능도 추가되었다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;기술적 트렌드&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;transistor가 점점 더 작아지고 있고, 면적당 계산력이 늘어나기 때문에 core가 더 늘어날 것이다.&lt;/li&gt;
&lt;li&gt;성능을 위해서 Tensor Core 등의 기능이, Programmability를 위해서 cooperative group이나 indepedent thread scheduling, threadd block cluster 등이 추가되고, security를 위해 VM support 등이 추가될 것이다.&lt;/li&gt;
&lt;li&gt;deep learning을 위해 더 큰 cluster로 scale out되고 있다. NVLink나 shared memory, NVSwitch 등의 기능이 생기고 있고, CPU와 GPU, DPU들끼리 integration이 일어나고 있다.&lt;/li&gt;
&lt;li&gt;한계점은 GPU core가 더 빨라짐에 따라 memory bandwidth가 한계점으로 맞은 상황이다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;잘못된 내용이나 오탈자에 대한 지적, 질문&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;b&gt;등은 언제나 환영합니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>CS/Parallel Computing</category>
      <author>hyelie</author>
      <guid isPermaLink="true">https://hyelie.tistory.com/669</guid>
      <comments>https://hyelie.tistory.com/entry/%EC%9D%B4%EC%A2%85%EB%B3%91%EB%A0%AC%EC%BB%B4%ED%93%A8%ED%8C%85-GPU-architectures-NVIDIA#entry669comment</comments>
      <pubDate>Wed, 25 Oct 2023 23:26:14 +0900</pubDate>
    </item>
    <item>
      <title>[이종병렬컴퓨팅] GPU architectures - SIMT와 SIMD</title>
      <link>https://hyelie.tistory.com/entry/%EC%9D%B4%EC%A2%85%EB%B3%91%EB%A0%AC%EC%BB%B4%ED%93%A8%ED%8C%85-GPU-architectures-SIMT%EC%99%80-SIMD</link>
      <description>&lt;blockquote style=&quot;text-align: left;&quot; data-ke-style=&quot;style2&quot;&gt;이 글은 포스텍 성효진 교수님의 이종병렬컴퓨팅(CSED490C) 강의를 기반으로 재구성한 것입니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 글에서는 다음과 같은 내용들을 살핀다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;GPU의 실행 모델인 SIMT와 SIMD&lt;span style=&quot;text-align: left;&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;Heterogeneous Computing&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;758&quot; data-origin-height=&quot;380&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4yO7o/btsy8miCmec/BWuIvtf2ubU34jPRKb1k10/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4yO7o/btsy8miCmec/BWuIvtf2ubU34jPRKb1k10/img.png&quot; data-alt=&quot;heterogeneous computing&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4yO7o/btsy8miCmec/BWuIvtf2ubU34jPRKb1k10/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4yO7o%2Fbtsy8miCmec%2FBWuIvtf2ubU34jPRKb1k10%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;758&quot; height=&quot;380&quot; data-origin-width=&quot;758&quot; data-origin-height=&quot;380&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;heterogeneous computing&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;순서가 있는 instruction은 CPU에, parallel하게 돌릴 수 있는 instruction은 GPU에 할당해서 돌리는 것이 heterogeneous computing이다.&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;GPU Architecture의 개요&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;CPU vs GPU&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;CPU는 하나의 thread에서 발생하는 &lt;b&gt;latency를 최소화하는 데 목적&lt;/b&gt;이 있으며, 때문에 cache가 매우 크다. 따라서 일반적으로 core의 개수가 적으며, 여러 종류의 instruction을 수행할 수 있는 &lt;b&gt;general&lt;/b&gt;한 목적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;GPU는 &lt;b&gt;모든&lt;/b&gt; &lt;b&gt;thread의 throughput을 최대화&lt;/b&gt;하는 것이 목적이며, multithreading으로 인해 latency를 숨길 수 있다. 따라서 또한 core가 수행하는 연산이 더 간단하기 때문에 core의 개수가 매우 많으며, cache의 크기가 작다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;CPU는 훨씬 복잡하고, latency가 훨씬 적다. 반면 GPU는 훨씬 작고 간단하지만 훨씬 많은 core가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;GPU는 어떻게 높은 성능을 내는가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;GPU는 graphics processing unit인 만큼, 동일한 작업을 계속 수행하므로 parallel하게 구성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;549&quot; data-origin-height=&quot;80&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cv0xAN/btszbSHnxek/jOwaPzVvyV1GawvTcEQQck/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cv0xAN/btszbSHnxek/jOwaPzVvyV1GawvTcEQQck/img.png&quot; data-alt=&quot;CPU의 control&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cv0xAN/btszbSHnxek/jOwaPzVvyV1GawvTcEQQck/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcv0xAN%2FbtszbSHnxek%2FjOwaPzVvyV1GawvTcEQQck%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;549&quot; height=&quot;80&quot; data-origin-width=&quot;549&quot; data-origin-height=&quot;80&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;CPU의 control&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;584&quot; data-origin-height=&quot;114&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cwreB3/btsy8j7fvXh/CcSmLuyQSPrKevRQLS4sqk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cwreB3/btsy8j7fvXh/CcSmLuyQSPrKevRQLS4sqk/img.png&quot; data-alt=&quot;GPU의 control&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cwreB3/btsy8j7fvXh/CcSmLuyQSPrKevRQLS4sqk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcwreB3%2Fbtsy8j7fvXh%2FCcSmLuyQSPrKevRQLS4sqk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;584&quot; height=&quot;114&quot; data-origin-width=&quot;584&quot; data-origin-height=&quot;114&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;GPU의 control&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;CPU와 비교했을 때 나타나는 이러한 GPU의 효율성은 &lt;b&gt;control overhead를 최소화&lt;/b&gt;하는 데서 나온다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;여기서 control overhead란 flow of control에서 발생하는 overhead이다. 기본적으로 instruction을 fetch하고 어떤 instruction을 실행할지 결정하는 것이 flow of control이다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;CPU는 모든 context에 별개의 program counter가 있으며, 하나의 core에서도 context가 여러 개 있기도 하다. 반면 GPU는 여러 개의 core를 그룹화하기 때문에 core 각각의 program counter가 없다. 한 group은 program counter 나 call stack 등의 정보를 공유하는 방식을 통해 각 group을 한 번에 관리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;GPU의 모든 core는 여러 개의 thread를 independent하게 실행해야 하므로 자체적인 control logic이 필요한데, 여기서 &lt;b&gt;GPU는 core들끼리 이 control logic을 공유&lt;/b&gt;하는 방식을 택한다. (때문에 모든 thread가 program counter 나 stack pointer를 공유한다.) 이것이 GPU의 핵심 실행 모델이며, 본질적으로는 hardward가 실행하는 &lt;b&gt;SIMD&lt;/b&gt; 방식이다. 이러한 방식으로 control overhead를 최소화한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이렇게 하나의 그룹으로 묶인 thread set을 &lt;b&gt;warp&lt;/b&gt;라고 한다. &lt;b&gt;warp의 모든 thread set은 동일한 instruction을 실행&lt;/b&gt;하고, SIMD 작업이지만 GPU scheduler가 수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;SIMD, Single Instruction Multiple Data&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;279&quot; data-origin-height=&quot;175&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FdCWC/btszbvyS0Si/HDGwDD0jLIc0af7eTQxAek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FdCWC/btszbvyS0Si/HDGwDD0jLIc0af7eTQxAek/img.png&quot; data-alt=&quot;SIMD&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FdCWC/btszbvyS0Si/HDGwDD0jLIc0af7eTQxAek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFdCWC%2FbtszbvyS0Si%2FHDGwDD0jLIc0af7eTQxAek%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;279&quot; height=&quot;175&quot; data-origin-width=&quot;279&quot; data-origin-height=&quot;175&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;SIMD&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;b&gt;여러 개의 data&lt;/b&gt;, 일반적으로 vector&lt;b&gt;에 대해 하나의 instruction을 수행&lt;/b&gt;하는 것을 말한다. 오직 하나의 instruction으로 여러 개의 data를 처리할 수 있다. 이 경우, &lt;b&gt;programming model과 execution model 모두 SIMD&lt;/b&gt;로 작동한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;SIMD를 사용하기 위해서는 SIMD가 작동할 때 한 번에 몇 개의 data를 처리할지에 대한 지표인 &lt;b&gt;width&lt;/b&gt;를 알고 있어야 한다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;예를 들어 width를 512로 설정하면, instruction 하나로 512개의 data를 처리할 수 있는 것이 vector instruction - SIMD이다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;기본적으로 &lt;b&gt;GPU 내부는 SIMD&lt;/b&gt;로 구현되어 있다. program counter를 공유하는 같은 core에 대해 같은 instruction이 실행하기 때문에, SIMD처럼 작동해야만 한다. 그렇지만 programming model이 SIMD가 아니기 때문에 프로그램에서 vector instruction을 볼 수 없고, 일반적은 parallel program처럼 thread로 작성한다. - 즉, GPU의 &lt;b&gt;프로그래밍 모델(software)과 실행 모델(hardware)이 분리&lt;/b&gt;되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Programming Model vs Hardware Execution Model&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;programming model&lt;/b&gt; : 프로그래머가 코드를 작성하는 방식이다. 예를 들어 sequential, data parallel, dataflow, multi thread 등이 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;execution model&lt;/b&gt; : hardward가 실제로 코드를 실행하는 방식이다. out of order execution, vector processor, multiprocessor 등이 있다.&lt;/li&gt;
&lt;li&gt;이 둘은 다른 것이기 때문에, 둘이 지향하는 패러다임에서 매우 큰 차이가 날 수 있다. 예를 들어 programming model은 sequential이지만 execution model이 out or order일 수도 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;GPU : SPMD 프로그래밍 모델과 SIMD 실행 모델&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;SPMD, Single Program Multiple Data&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;때문에, GPU는 GPMD programming model과 SIMD execution model이라 불리기도 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;SPMD의 작명에서 알 수 있듯 하나의 프로그램이 여러 개의 data를 돌리는 방식이다. &lt;b&gt;SPMD&lt;/b&gt;는 &lt;b&gt;programming model&lt;/b&gt;이며, MIMD의 subclass로 좀 더 제약이 걸린 버전이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;각 thread는 같은 kernel을 수행하지만, data의 다른 부분을 실행한다. 각 thread는 각각의 context가 있으며, 때문에 독립적으로 동작한다. 각 program은 서로 다른 control flow path를 가질 수 있다. 이러한 동작은 불필요한 overhead를 없애기 때문에 더 빨리 동작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;SIMT, Single Instruction Multiple Thread&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;359&quot; data-origin-height=&quot;188&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BiUNZ/btszbsPIGMy/iOKzDrIp6Bmg96EclIBlak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BiUNZ/btszbsPIGMy/iOKzDrIp6Bmg96EclIBlak/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BiUNZ/btszbsPIGMy/iOKzDrIp6Bmg96EclIBlak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBiUNZ%2FbtszbsPIGMy%2FiOKzDrIp6Bmg96EclIBlak%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;359&quot; height=&quot;188&quot; data-origin-width=&quot;359&quot; data-origin-height=&quot;188&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;b&gt;코드&lt;/b&gt;를 작성하는 단계에서는 &lt;b&gt;single instruction&lt;/b&gt;을 사용하지만 실제로 &lt;b&gt;실행&lt;/b&gt;되는 방식은 &lt;b&gt;mulitple thread&lt;/b&gt;인 형식이다. 이 때 instruction은 vector instruction이 아니라, &lt;b&gt;scalar instruction&lt;/b&gt;이다! 한편 thread들은 warp라는, 동적으로 형성된 group으로 묶이며, 각 thread는 scalar instruction을 사용한다.&amp;nbsp;hardware가 warp를 그룹화하며, 각 warp는 동일한 instruction을 실행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;warp의 크기는 하드코드된 값을 사용한다. 예를 들어 32라고 설정하면 총 32개의 thread를 하나의 warp에 넣고, 64라고 설정하면 64개의 thread를 하나의 warp에 넣는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;만약 너무 작은 숫자를 사용한다면 SIMD exeuction engine을 사용하는 이점이 사라진다. 반면 너무 큰 숫자를 사용하면 해당 warp를 채우기 힘들뿐더러 가장자리 부분에 cache miss가 날 수 있다. 따라서 이러한 경우를 모두 고려해 memory를 효율적으로 사용할 수 있는 숫자를 사용해야만 한다. 지금은 일반적으로 32를 쓴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;SIMD vs SIMT&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;SIMD&lt;/b&gt; : 하나의 instruction이 여러 개의 data를 처리하는 방식이다. 예를 들어 VLD, VLD, VADD, VST, VLEN 등이 있다. vector length를 알고 있어야 한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;SIMT &lt;/b&gt;: &amp;nbsp;scalar instruction의 multiple instruction stream이다. 같은 명령어를 동시에 실행하는 여러 개의 thread가 hardward에 의해 warp로 묶인 것이다. 예를 들어 LD, LD, ADD, ST, NumThreads 등이 있다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SIMD의 경우 single thread이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;SIMT는 크게 2가지 장점이 있는데,&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SIMT는 multi thread이므로 thread를 개별적으로 처리할 수 있다. 즉, 각 thread가 독립적으로 실행할 수 있기 때문에 MIMD processing을 할 수 있다.&lt;/li&gt;
&lt;li&gt;thread를 동적으로 warp로 묶을 수 있다. 즉, 동일한 instruction을 실행해야 하는 thread를 warp로 묶어 SIMD processing의 이점을 최대화할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Warp와 Warp-Level FGMT&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;657&quot; data-origin-height=&quot;196&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bsMBDy/btszcolGNCo/XzLgQpMBUA4dGtFKQHKSCK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bsMBDy/btszcolGNCo/XzLgQpMBUA4dGtFKQHKSCK/img.png&quot; data-alt=&quot;warp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bsMBDy/btszcolGNCo/XzLgQpMBUA4dGtFKQHKSCK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbsMBDy%2FbtszcolGNCo%2FXzLgQpMBUA4dGtFKQHKSCK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;657&quot; height=&quot;196&quot; data-origin-width=&quot;657&quot; data-origin-height=&quot;196&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;warp&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;warp의 각 thread는 &lt;b&gt;program counter , call stack을 공유&lt;/b&gt;하고, &lt;b&gt;register만 다른 값&lt;/b&gt;을 가진다. 따라서 같은 프로그램이더라도 다른 warp는 다른 program counter 를 가질 수 있다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;일반적으로 GPU는 매우 큰 register를 가지며, 이를 적당히 나눠 각 thread에게 제공한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위 그림의 오른쪽은 SIMD pipeline을 의미하는데, 여기에는 실행할 준비가 끝난 warp들이 들어가며 여기서 실행하기로 한 warp가 SIMD pipeline에 들어간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;High Level View of GPU&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;568&quot; data-origin-height=&quot;348&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/clICk4/btsy9Mt8TXc/OScduDoOnS6qL6h49ndANk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/clICk4/btsy9Mt8TXc/OScduDoOnS6qL6h49ndANk/img.png&quot; data-alt=&quot;GPU 구조 개요&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/clICk4/btsy9Mt8TXc/OScduDoOnS6qL6h49ndANk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FclICk4%2Fbtsy9Mt8TXc%2FOScduDoOnS6qL6h49ndANk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;568&quot; height=&quot;348&quot; data-origin-width=&quot;568&quot; data-origin-height=&quot;348&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;GPU 구조 개요&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;sharder core는 앞에서 설명한 core라 받아들이면 된다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각 core에는 scalar pipeline이 있으며, SIMD execution이 일어나는 곳이다.&lt;/li&gt;
&lt;li&gt;PC, mask는 warp의 실행 정보가 담기는 부분이다. PC는 program counter이고, warp가 같은 program counter를 공유하므로 필요하다. mask의 경우 warp의 thread가 분기로 인해 다른 warp로 바뀔 때 적용하는 값이다. mask를 사용해 다른 program counter를 실행하는 warp를 묶는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;아래쪽의 GDDR3는 GPU memory를 의미하는데, NVIDIA나 AMD GPU는 GPU 내부의 memory, GPU memory 또는 device memory가 있다. ARM GPU의 경우 CPU와 RAM을 공유하기도 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Latency Hiding with Warp Level FGMT&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;322&quot; data-origin-height=&quot;385&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BYOVR/btszcN6CcsB/GYrbebm9cU1kYWJnxbuuV1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BYOVR/btszcN6CcsB/GYrbebm9cU1kYWJnxbuuV1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BYOVR/btszcN6CcsB/GYrbebm9cU1kYWJnxbuuV1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBYOVR%2FbtszcN6CcsB%2FGYrbebm9cU1kYWJnxbuuV1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;322&quot; height=&quot;385&quot; data-origin-width=&quot;322&quot; data-origin-height=&quot;385&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;제일 윗부분은 hardware scheduler가 있는 부분이다. 실행할 준비가 된 warp들이 들어간다.&lt;/li&gt;
&lt;li&gt;이후 SIMD pipeline에서는 하나의 warp를 실행한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SIMD pipeline을 보면 fetch, decode는 하나만 있고, ALU만 여러 개가 있다. - 즉 하나의 instruction을 thread끼리 공유한다는 뜻이다. (SIMD)&lt;/li&gt;
&lt;li&gt;cache miss가 있는 경우 memory를 확인해야 하는데, 값을 가져오기 위해 context switch를 한다. 이 context switch의 경우 CPU와는 조금 다르다. CPU의 경우 PC, register, stack pointer 등을 저장하고 context switch를 한다. 반면 &lt;b&gt;GPU의 경우 모든 thread가 크기가 큰 개인용 register를 가지기 때문에&lt;/b&gt; 여기다가 PC, register, stack pointer 등을 저장하므로 값을 저장하고 꺼내오는 데에 대한 &lt;b&gt;overhead가 매우 적고&lt;/b&gt;, 따라서 warp의 실행을 잠시 보류하기만 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;b&gt;fine-grained multithreading&lt;/b&gt;을 사용하기 때문에 매 cycle마다 hardward scheduler가 서로 다른 warp로부터 instruction을 수행한다. 이 과정은 interlocking이 없이, pipeline에 있는 thread마다 하나의 instruction을 &lt;span style=&quot;text-align: start;&quot;&gt;실행한다는 것이다. context switch를 할 때, 모든 thread의 register 값들은 register file에 저장되며, context switching overhead가 매우 낮기 때문에 &lt;b&gt;I/O를 기다리는 warp를 hardware scheduler가 scheduling하기만 하면 되는 이 효과로 인해 latency가 숨겨진다&lt;/b&gt;. &lt;/span&gt;따라서 FGMT는 &lt;b&gt;긴 latency를 허용&lt;/b&gt;한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Warp Instruction Level Parallelism&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;316&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AzapB/btszbWwdELN/fuig2FRuv5TV7L5cDWgE2K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AzapB/btszbWwdELN/fuig2FRuv5TV7L5cDWgE2K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AzapB/btszbWwdELN/fuig2FRuv5TV7L5cDWgE2K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAzapB%2FbtszbWwdELN%2Ffuig2FRuv5TV7L5cDWgE2K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;316&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;316&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;여러 개의 instruction을 겹칠 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위 그림은 warp당 32개의 thread가 있고, 8개의 core이 있는 상황이다. 이 경우 각 core는 한 cycle에 8개의 thread만 처리할 수 있으므로, 각 warp가 처리되기 위해서는 4 cycle이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위 예시에서는 load, multiply, add unit 3가지로 나뉘어 있는데, 때문에 동시에 3개의 unit을 처리할 수 있다. 즉&amp;nbsp;cycle당 24개의 operation을 수행할 수 있게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Control Flow Problem in GPU&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;같은 warp에 있는 모든 thread는 같은 program counter를 실행한다. 그러나 branch를 만나 조건이 다르면 다른 program counter를 가지게 된다. 때문에 branch divergence - 다른 실행 경로를 가지는 문제가 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;326&quot; data-origin-height=&quot;290&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BSVHL/btszbSm5iTy/ozUBWqbEgkkQqHoy9kEWI0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BSVHL/btszbSm5iTy/ozUBWqbEgkkQqHoy9kEWI0/img.png&quot; data-alt=&quot;branch divergence problem&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BSVHL/btszbSm5iTy/ozUBWqbEgkkQqHoy9kEWI0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBSVHL%2FbtszbSm5iTy%2FozUBWqbEgkkQqHoy9kEWI0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;326&quot; height=&quot;290&quot; data-origin-width=&quot;326&quot; data-origin-height=&quot;290&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;branch divergence problem&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이를 해결하기 위해 mask를 사용하며, mask를 사용해 다른 branch로 들어간 thread들을 묶을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위 예시에서 branch를 기준으로 실행하는 thread가 달라진 모습이다. 몇몇 thread는 path A로, 몇몇 thread는 path B로 간다. path A로 간 thread들은 1로 mask하고, path B로 간 thread들은 0으로 mask한다. 이후 branch가 끝난 후 converge하는 방식으로 branch divergence 문제를 해결한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이러한 방식으로 같은 instruction을 실행하는 thread를 동적으로 warp로 묶으며, 이를 통해 낭비되는 cycle을 줄여 SIMD process의 이점을 최대화한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;정리&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;중요한 점: 각 thread는 independent이다!&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;SIMT는 2가지 장점이 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;각 thread를 독립적&lt;/b&gt;으로 처리할 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;thread를 warp로 유동적&lt;/b&gt;으로 묶을 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;만약 thread가 매우 많다면&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;같은 program counter를 가진 독립적인 thread를 찾고,&amp;nbsp;&lt;/li&gt;
&lt;li&gt;이 thread를 warp로 묶는다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 과정은 divergence를 없애주기 때문에 SIMD utilization을 향상시킨다. 단, nested if문이 있는 경우에는 divergence가 발생하기 때문에 cycle이 낭비된다. if문이 하나일 때는 cycle 하나이지만 중첩될수록 많은 cycle이 낭비된다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;최신 기술은, 이러한 nested if를 허용하기 위해 각 thread별로 PC를 가진다. 앞서 warp의 모든 thread가 같은 PC를 가진다고 했는데 조금 모순되지 않나?라 생각할 수도 있다. 각 thread는 각각의 PC를 가지되, 같은 PC를 가진 thread만 한 번에 실행하는 형식이다. - 그래서 SIMT를 유지한다.&lt;br /&gt;이를 통해 divergence로 인해 생기는 cycle 낭비를 조금이나마 막을 수 있다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Warp Based SIMD vs Tranditional SIMD&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;traditional SIMD의 경우 single thread이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;sequential instruction execution이기 때문에 SIMD instruction에서 lock이 필요하다.&lt;/li&gt;
&lt;li&gt;programming model도 SIMD이다. 따라서 software가 vector 길이를 알아야 한다.&lt;/li&gt;
&lt;li&gt;ISA는 vector instruction이 SIMD instruction을 포함하고 있어야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;반면 warp based SIMD의 경우, SIMD 방식으로 실행되는 여러 개의 scalar thread들로 구성되어 있다. (모든 thread가 같은 instruction을 실행한다)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;따라서 lock이 필요없다.&lt;/li&gt;
&lt;li&gt;각 thread는 독립적으로 취급받는다. 즉, &lt;b&gt;programming model은 SIMD가 아니다&lt;/b&gt;. 때문에 software는 vector 길이를 몰라도 되며, dynamic하게 thread를 grouping할 수 있다.&lt;/li&gt;
&lt;li&gt;ISA는 scalar이므로 dynamic하게 구성될 수 있다.&lt;/li&gt;
&lt;li&gt;근본적으로 SPMD programming model이 SIMD hardware에 구현된 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;요약&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;GPU는 SPMD를 parallelism을 사용하고, 이에 최적화된 hardware를 설계한다. portability와 programmability를 위해 SPMD programming model을 유지하며, 복잡한 contol logic은 SIMD hardware로 대체한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;GPU execution model은 ILP, DLP, TLP 3가지를 모두 지원한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;DLP의 경우 여러 개의 data를 처리하는 것이고, TLP는 find-grained multithreading으로 여러 개의 thread를 실행하며, ILP는 모든 warp더라도 모든 instruction이 별개로 + 동시에 돌아가기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;잘못된 내용이나 오탈자에 대한 지적, 질문&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;b&gt;등은 언제나 환영합니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>CS/Parallel Computing</category>
      <author>hyelie</author>
      <guid isPermaLink="true">https://hyelie.tistory.com/668</guid>
      <comments>https://hyelie.tistory.com/entry/%EC%9D%B4%EC%A2%85%EB%B3%91%EB%A0%AC%EC%BB%B4%ED%93%A8%ED%8C%85-GPU-architectures-SIMT%EC%99%80-SIMD#entry668comment</comments>
      <pubDate>Wed, 25 Oct 2023 23:25:27 +0900</pubDate>
    </item>
    <item>
      <title>[이종병렬컴퓨팅] Parallel Computing Basics</title>
      <link>https://hyelie.tistory.com/entry/%EC%9D%B4%EC%A2%85%EB%B3%91%EB%A0%AC%EC%BB%B4%ED%93%A8%ED%8C%85-Parallel-Computing-Basics</link>
      <description>&lt;blockquote style=&quot;text-align: left;&quot; data-ke-style=&quot;style2&quot;&gt;이 글은 포스텍 성효진 교수님의 이종병렬컴퓨팅(CSED490C) 강의를 기반으로 재구성한 것입니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 글에서는 다음과 같은 내용을 살핀다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;parallel computing의 기본적인 개념
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;parallelism의 종류&lt;/li&gt;
&lt;li&gt;parallel architecture 분류&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;parallel program을 어떻게 작성하고 어떤 기준으로 평가하는지&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Parallelism의 종류&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;크게 3가지의 parallelism이 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;ILP, Instruction Level Parallelism&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;instruction들끼리 &lt;b&gt;independent&lt;/b&gt;하다.&lt;/li&gt;
&lt;li&gt;hardware는 instruction window size 내내에서 implicit하게 찾을 수 있다.&lt;/li&gt;
&lt;li&gt;complier들은 window 내에 있는 명령어들이 independent할 수 있도록 찾아서 순서를 바꾼다. &lt;b&gt;independent한 순서로 재배열 한 후 실행&lt;/b&gt;하는 방식이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;TLP, Thread Level Parallelism&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;complier와 programmer가 program을 &lt;b&gt;명시적&lt;/b&gt;으로 나눈다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;DLP, Data Level Parallelism&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;TLP의 variation 중 하나로, 같은 instruction을 실행하는 여러 개의 thread가 다른 data에 대해 동작하는 방식이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;예시&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1193&quot; data-origin-height=&quot;514&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJlwiH/btsy9la7lxV/34QWKxb5eokS2kVuwnl3V0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJlwiH/btsy9la7lxV/34QWKxb5eokS2kVuwnl3V0/img.png&quot; data-alt=&quot;parallelism의 종류&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJlwiH/btsy9la7lxV/34QWKxb5eokS2kVuwnl3V0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJlwiH%2Fbtsy9la7lxV%2F34QWKxb5eokS2kVuwnl3V0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1193&quot; height=&quot;514&quot; data-origin-width=&quot;1193&quot; data-origin-height=&quot;514&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;parallelism의 종류&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위쪽 부분은 programmer view를, 아래쪽 부분은 실제 작동 방식을 나타낸다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;ILP&lt;/b&gt;의 경우 independent한 instruction을 적당히 동시에 실행시킨다. hardware가 수행하기에 programmer는 이에 대해 알 수 없다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;TLP&lt;/b&gt;의 경우 programmer가 instruction을 나누어 배치했기 때문에 해당 instruction들끼리는 parallel하게 실행되는 모습이다. programmer가 instruction을 나누었기 떄문에 이를 인지하고 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;DLP&lt;/b&gt;의 경우 같은 종류의 data는 같은 instruction이 실행되는 모습이다. TLP의 variation인 만큼 programmer도 이를 인지하고 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Flynn's Taxonomy&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1032&quot; data-origin-height=&quot;531&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/o5saf/btsy8rCDTi5/E3mNd6kZf4hFWTRoGZkhq0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/o5saf/btsy8rCDTi5/E3mNd6kZf4hFWTRoGZkhq0/img.png&quot; data-alt=&quot;flynn's taxonomy&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/o5saf/btsy8rCDTi5/E3mNd6kZf4hFWTRoGZkhq0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fo5saf%2Fbtsy8rCDTi5%2FE3mNd6kZf4hFWTRoGZkhq0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1032&quot; height=&quot;531&quot; data-origin-width=&quot;1032&quot; data-origin-height=&quot;531&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;flynn's taxonomy&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;single instruction stream은 single thread를, multiple instruction stream은 multiple thread를 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;multiple data streams &amp;amp; single instruction stream - (1, 0)에 있는 것 - 은 DLP를, multiple data streams &amp;amp; multiple instruction streams - (1, 1)에 있는 것 - 은 TLP를 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Hardware가 parallelism을 달성하는 방법&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;ILP&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Superscalar&lt;/b&gt; : single thread에서 여러 개의 instruction을 실행하는 방식이다. instruction dependency가 없는 instruction만 하나의 cycle에 동시에 실행되기 때문에 dependency에 크게 영향을 받는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;TLP&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Find Grain Multithreading&lt;/b&gt; : thread 간 context switching이 빠르다. 매 cycle마다 다른 thread를 교체해서 실행하는 방식이다. (single core)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;SMT, Simultaneous MultiThreading&lt;/b&gt; : 같은 cycle에 여러 개의 thread의 instruction을 실행하는 방식이다. (multi core)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;CMP, Chip Multi Processors&lt;/b&gt; : 하나의 chip에 여러 개의 core를 탑재하고 각 thread를 다른 core에서 실행하는 방식이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;DLP&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Vector Processors&lt;/b&gt; : data의 여러 부분에 작동하는 single instruction을 실행하는 방식이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Parallel vs Concurrency&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;b&gt;Parallel&lt;/b&gt;은 &lt;b&gt;여러 개의 resource&lt;/b&gt;(CPU나 core 등)을 사용해 single prcessor에서보다 더 빨리 푸는 방식이다. 예를 들어 merge-sort의 경우 각 thread가 data의 각 부분을 정렬하고, 합치는 parallel을 생각할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;b&gt;Concurrency&lt;/b&gt;는 &lt;b&gt;여러 개의 execution flow를 동시에 실행하는 것처럼 보이는 방식&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Parallel Algorithm Design&lt;/h2&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;concurrency를 달성하기 위해서는 programmer가 concurrency를 인지하고 아래 3가지를 잘 다뤄야 한다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;dependency&lt;/b&gt;를 올바르게 관리&lt;/li&gt;
&lt;li&gt;concurrency 관리로 발생하는 &lt;b&gt;overhead 최소화&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;balanced&lt;/b&gt;한 방식으로 work를 나누기&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;때문에 parallel program을 설계하는 방식은 크게 아래와 같다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;parallel&lt;/b&gt;하게 수행할 수 있는 &lt;b&gt;작업 식별&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;task, data 분할&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;data access, &lt;b&gt;communication, synchronization 관리&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이를 통해 program을 parallel하게 실행해서 &lt;b&gt;성능을 향상&lt;/b&gt;시키는 것이 목적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;Speedup(P processors) = $\frac{\text{Time(1 processor)}}{\text{Time(P processors)}}$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;373&quot; data-origin-height=&quot;389&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b6OgGW/btsy3havUMg/HvsEx1xPRU7KD5h5ZGYQoK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b6OgGW/btsy3havUMg/HvsEx1xPRU7KD5h5ZGYQoK/img.png&quot; data-alt=&quot;parallel program 설계 단계&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b6OgGW/btsy3havUMg/HvsEx1xPRU7KD5h5ZGYQoK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb6OgGW%2Fbtsy3havUMg%2FHvsEx1xPRU7KD5h5ZGYQoK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;373&quot; height=&quot;389&quot; data-origin-width=&quot;373&quot; data-origin-height=&quot;389&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;parallel program 설계 단계&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 단계를 그림으로 표현하면 위와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;decomposition은 work를 subwork로 나누는 것, assignment는 core에 work를 할당하는 것, orchestration은 communication을 하는 부분을 말한다. mapping은 hardward에 mapping하는 과정으로, 이 과정은 보통 OS가 수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;Parallel Algorithm의 목표&lt;/h3&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;제일 좋은 방식은 아래 3가지를 모두 달성하는 것이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Maximize parallelism&lt;/li&gt;
&lt;li&gt;Minimize communication&lt;/li&gt;
&lt;li&gt;Minimize load imbalance&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그러나 실제로는 이런 3가지를 모두 달성하기는 매우 어렵다. 아래와 같은 conflict에서 적당한 위치를 찾아야 한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;parallelism vs communication : 이 둘은 tradeoff이다.&lt;/li&gt;
&lt;li&gt;load imbalance vs communication&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;text-align: left;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 이 둘은 tradeoff이다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;architectural contraints의 한계&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;Parallel Algorithm의 구성 요소&lt;/h3&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;parallel algorithm은 위 그림에서 언급되었듯 크게 3가지로 나뉜다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Task Decomposition&lt;/b&gt;:&amp;nbsp;문제를 동시에 실행할 수 있는 작업으로 나누는 것&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Mapping &amp;amp; Scheduling&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;:&amp;nbsp;task를 여러 computing unit에 할당하고 input/output/중간 data를 배포하는 과정&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Communication &amp;amp; Synchronization&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: parallel execution의 다양한 지점에서 task를 동기화&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Task Decomposition&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;task를 개별적인 작업으로 나누어 사용하는 방법. &lt;b&gt;execution unit이 최대한 busy&lt;/b&gt;하게 두면서 &lt;b&gt;dependency를 최소화&lt;/b&gt;하는 방법이 기본적인 골자이다. 이 때,&amp;nbsp;task를 지나치게 잘게 쪼개면 thread의 생성/관리에 필요한 overhead가 더 커지기 떄문에 적당히 작은 크기로 쪼개는 것이 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;나누는 방법은 다음과 같은 방법들이 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;어떻게 나누는지
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;domain &lt;/b&gt;decomposition : operation을 나누는 것이 아니라 &lt;b&gt;data만 나누어서 같은 연산을 수행&lt;/b&gt;하는 방식이다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;functional &lt;/b&gt;decomposition : 문제의 분류에 따라 나누는 방식이다. 다른 작업으로 나뉘기에 다른 연산을 수행한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;언제 나누는지
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;static&lt;/b&gt; decomposition : &lt;b&gt;계산하기 전&lt;/b&gt;에 decomposition을 결정하는 방식이다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;dynamic&lt;/b&gt; decomposition : &lt;b&gt;입력에 따라 decomposition을 결정&lt;/b&gt;하는 방식으로, 나눌 때 발생하는 overhead가 있다. 예시로 sparse matrix multiplication이 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;어떠한 크기로 나누는지
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;coarse-grained task&lt;/b&gt; : 크게크게 나누는 방법. communication overhead가 적지만 load imbalance가 발생한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;fine-grained task&lt;/b&gt; : 세밀하게 나누는 방법. communication overhead가 크지만 load balance하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;parallel execution overhead는 `communication/synchronization cost + idling + excess work`의 3가지로 표현된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 때, 너무 세밀하게 나누면 overhead가 너무 커지고, 너무 크게 나누면 load imbalance가 너무 커지기 때문에&amp;nbsp;overhead와 load imbalance가 균형을 이루는 지점을 잘 잡아 task decomposition해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Mapping and Scheduling&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 단계는 decompose한 task를 어떤 processing unit에게 할당하고(mapping), 언제 실행할지(scheduling) 결정하는 단계이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;static&lt;/b&gt; mapping and scheduling
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;task가 &lt;b&gt;실행 전에 processing unit에 할당&lt;/b&gt;되는 방식이다.&lt;/li&gt;
&lt;li&gt;때문에 task size가 고정된 경우에 쓸 수 있다.&lt;/li&gt;
&lt;li&gt;overhead가 적다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;dynamic&lt;/b&gt; mapping and scheduling
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;task가 &lt;b&gt;실행 중에 동적으로 processing unit&lt;/b&gt;에 할당되는 방식이다. scheduler가 다음에 실행할 작업을 결정하고 core에 할당한다. 때문에 task queue 등 자료구조가 필요하다.&lt;/li&gt;
&lt;li&gt;task 크기를 모르는 경우에 써야 한다.&lt;/li&gt;
&lt;li&gt;overhead가 높지만, load imbalance를 줄일 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;예시&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;430&quot; data-origin-height=&quot;395&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/shme8/btsy47d9Lma/69YEUltR4OZKv8KNVvJUs1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/shme8/btsy47d9Lma/69YEUltR4OZKv8KNVvJUs1/img.png&quot; data-alt=&quot;mapping and scheduling 예시 - locality가 나쁜 경우&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/shme8/btsy47d9Lma/69YEUltR4OZKv8KNVvJUs1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fshme8%2Fbtsy47d9Lma%2F69YEUltR4OZKv8KNVvJUs1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;430&quot; height=&quot;395&quot; data-origin-width=&quot;430&quot; data-origin-height=&quot;395&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;mapping and scheduling 예시 - locality가 나쁜 경우&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이러한 예시가 있다고 하자. Tn은 n번째 row를 처리한다고 하자. 위 경우, static decomposition, static mapping을 사용했다.&amp;nbsp;이 경우 locality가 나쁘다! T1, T5, T9가 하나의 processing unit에서 사용되는데, 서로 다른 cache를 보기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;421&quot; data-origin-height=&quot;403&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cEaE7R/btsy4so5Lkn/C1EJ1vTkuhebV75btlOAok/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cEaE7R/btsy4so5Lkn/C1EJ1vTkuhebV75btlOAok/img.png&quot; data-alt=&quot;mapping and scheduling 예시 - locality를 보완한 경우&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cEaE7R/btsy4so5Lkn/C1EJ1vTkuhebV75btlOAok/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcEaE7R%2Fbtsy4so5Lkn%2FC1EJ1vTkuhebV75btlOAok%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;421&quot; height=&quot;403&quot; data-origin-width=&quot;421&quot; data-origin-height=&quot;403&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;mapping and scheduling 예시 - locality를 보완한 경우&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;locality를 개선하면 위 그림과 같다. T1, T2, T3가 같은 processing unit에 속하므로 cache를 좀 더 효율적으로 쓸 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;433&quot; data-origin-height=&quot;430&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0HQTL/btsy9TyGVGu/kCFlCdBnW9VxI3wxrgIPS0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0HQTL/btsy9TyGVGu/kCFlCdBnW9VxI3wxrgIPS0/img.png&quot; data-alt=&quot;mapping and scheduling 예시 - 딱 나눠떨어지지 않는 경우&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0HQTL/btsy9TyGVGu/kCFlCdBnW9VxI3wxrgIPS0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0HQTL%2Fbtsy9TyGVGu%2FkCFlCdBnW9VxI3wxrgIPS0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;433&quot; height=&quot;430&quot; data-origin-width=&quot;433&quot; data-origin-height=&quot;430&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;mapping and scheduling 예시 - 딱 나눠떨어지지 않는 경우&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 상태에서 위와 같은 상황이 되면 어떨까? processing unit 1에 추가적인 load가 생기기 때문에 load imbalance가 생긴다. 때문에 T13을 끝낼 때 까지 cycle을 낭비하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;420&quot; data-origin-height=&quot;423&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/chkN7t/btsy6ghB7Y7/lrWMkoxpunjmxuAMPcHot1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/chkN7t/btsy6ghB7Y7/lrWMkoxpunjmxuAMPcHot1/img.png&quot; data-alt=&quot;mapping and scheduling 예시 - fine grained task&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/chkN7t/btsy6ghB7Y7/lrWMkoxpunjmxuAMPcHot1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FchkN7t%2Fbtsy6ghB7Y7%2FlrWMkoxpunjmxuAMPcHot1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;420&quot; height=&quot;423&quot; data-origin-width=&quot;420&quot; data-origin-height=&quot;423&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;mapping and scheduling 예시 - fine grained task&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이를 해결하는 방법 중 하나가 fine-grained task이다. task를 좀 더 쪼개면 cycle 낭비가 있더라도 그 낭비를 더 줄일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;428&quot; data-origin-height=&quot;427&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/U5gHf/btsy47FdtkJ/spO0hmbkwvEw0YbwW7xhN1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/U5gHf/btsy47FdtkJ/spO0hmbkwvEw0YbwW7xhN1/img.png&quot; data-alt=&quot;mapping and scheduling 예시 - dynamic mapping&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/U5gHf/btsy47FdtkJ/spO0hmbkwvEw0YbwW7xhN1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FU5gHf%2Fbtsy47FdtkJ%2FspO0hmbkwvEw0YbwW7xhN1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;428&quot; height=&quot;427&quot; data-origin-width=&quot;428&quot; data-origin-height=&quot;427&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;mapping and scheduling 예시 - dynamic mapping&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;다른 한 가지 해결 방법은 dynamic mapping이다. task를 dynamic하게 나눠서 다른 processing unit이 task를 끝낼 때 쯤 다른 task를 할당하는 방식이다. 이 방법은 앞에서 설명했듯 granularity를 설정하기 위한 scheduling overhead가 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Overhead 최소화 방법&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;overhead를 최소화하는 방법은 아래와 같은 방법들이 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;independent task를 다른 core에 할당&lt;/b&gt;&amp;nbsp;: 만약 depedent task를 다른 core에 할당하는 경우 core끼리 통신하기 위해 barrier나 log가 필요하다. 때문에 independent task를 다른 core에 쓰는 것이 좋다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;parallelism을 최대화한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;critical path를 최대한 빨리 할당&lt;/b&gt;&amp;nbsp;: 다른 작업들보다 오래 걸리는 작업은 먼저 수행하는 것이 좋다. 이 작업을 나중에 수행하면 다른 core들이 idling하기 떄문이다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;load imbalance를 최소화한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;communication 최소화&lt;/b&gt;&amp;nbsp;: 첫 번째 것과 관련이 있는 내용으로, communication을 최소화하면 최대한 independent task를 다른 core에 할당해야 한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;overhead를 최소화한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이러한 기준들은 서로 충돌할 수 있다. 예를 들어 indepedent task의 개수를 찾을 수 없을 수도 있고, 다른 core에 dependent task를 할당해야 할 수도 있다. 때문에 balance를 잘 맞춰야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Communication and Synchronization&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;parallel task 사이에서 data를 공유하는 부분을 의미하며, thread끼리의 communication, synchronization에 관한 것이기 때문에 그 자체로 race condition을 만든다. 따라서 parallel thread로 동작하는 경우, communication cost는 sequential program에 동작하지 않는 순수한 overhead이다. lock contention의 경우 network를 사용한다는 문제점도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;communication은 문제에 따라 필요할 수도 있고, 그렇지 않을 수도 있다. 문제가 communication이 거의 없는 경우 그냥 바로 하면 된다. 앞서 matrix add 예시가 그것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이를 사용하는 방법은 크게 2가지가 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;message passing model&lt;/b&gt; : communication이 &lt;b&gt;explicit&lt;/b&gt;한 방식이다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;더 많은 programming을 해야 하고, flexible하지 않지만 이해하기 쉽다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;shared memory model&lt;/b&gt; : communication이 &lt;b&gt;implicit&lt;/b&gt;한 방식이다. coherence protocol 등 software나 hardward의 communication support가 있어야 한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로그래밍 자체는 쉽지만 어디서 버그가 났는지 찾기 어렵다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Communication의 종류&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;크게 collective, point-to-point가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Barrier Synchronization (collective)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;먼저 collective이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;1 to all, all to all을 수행하는 broadcast(multicsat)와 all to one을 수행하는 reduction이 있다. 별개로 scatter/gatter로 하기도 한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;467&quot; data-origin-height=&quot;390&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/drLhzZ/btsy79I4JJI/kodAh9y3y763rSH3vuNqk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/drLhzZ/btsy79I4JJI/kodAh9y3y763rSH3vuNqk1/img.png&quot; data-alt=&quot;collective synchronization&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/drLhzZ/btsy79I4JJI/kodAh9y3y763rSH3vuNqk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdrLhzZ%2Fbtsy79I4JJI%2FkodAh9y3y763rSH3vuNqk1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;467&quot; height=&quot;390&quot; data-origin-width=&quot;467&quot; data-origin-height=&quot;390&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;collective synchronization&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위 그림과 같이 parallel program은 barrir로 의해 단계가 나눠진다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;제일 처음에는 broadcast / scatter로 parallel하게 나눠진다.&lt;/li&gt;
&lt;li&gt;각 단계에서는 parallel하게 실행되고, barrier에서는 communicate가 실행된다.&lt;/li&gt;
&lt;li&gt;마지막에는 reduction / gather가 실행된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;barrier를 사용하는 경우 depedency를 사용하는, coarse-grained한 방식이다. 각 단계의 연산은 이전 단계의 연산(이전 단계의 barrier에서 완료된)에 의존한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Point to Point Synchronization&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;171&quot; data-origin-height=&quot;298&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cUwDUw/btsy5fQBeQK/IdnSevxTdBTfhVuI26ndTK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cUwDUw/btsy5fQBeQK/IdnSevxTdBTfhVuI26ndTK/img.png&quot; data-alt=&quot;point to point synchronization&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cUwDUw/btsy5fQBeQK/IdnSevxTdBTfhVuI26ndTK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcUwDUw%2Fbtsy5fQBeQK%2FIdnSevxTdBTfhVuI26ndTK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;171&quot; height=&quot;298&quot; data-origin-width=&quot;171&quot; data-origin-height=&quot;298&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;point to point synchronization&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;다음으로 point to point로, lock을 사용하는 방식이다. 때문에 하나의 thread만 critical section에 진입할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 경우 fine grained이기 때문에 더 빠르며, lock이 필요한 각각의 thread가 1대1로 통신하기 때문에 통신에 대한 overhead도 더 적다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Communication Overhead&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;415&quot; data-origin-height=&quot;400&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xKUdw/btsy37MgUu6/oqkEtK20ygjbkhcmeXWLWk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xKUdw/btsy37MgUu6/oqkEtK20ygjbkhcmeXWLWk/img.png&quot; data-alt=&quot;task를 하나의 iteration로 나눈 경우&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xKUdw/btsy37MgUu6/oqkEtK20ygjbkhcmeXWLWk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxKUdw%2Fbtsy37MgUu6%2FoqkEtK20ygjbkhcmeXWLWk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;415&quot; height=&quot;400&quot; data-origin-width=&quot;415&quot; data-origin-height=&quot;400&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;task를 하나의 iteration로 나눈 경우&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위 그림과 같은 경우 communication overhead = synchronization time + data communication time이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;415&quot; data-origin-height=&quot;394&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bcijyH/btsy4j6Y8If/xzNNREVbPKIqIRy55X6uQ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bcijyH/btsy4j6Y8If/xzNNREVbPKIqIRy55X6uQ1/img.png&quot; data-alt=&quot;task를 n/4 iteration으로 나눈 경우&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bcijyH/btsy4j6Y8If/xzNNREVbPKIqIRy55X6uQ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbcijyH%2Fbtsy4j6Y8If%2FxzNNREVbPKIqIRy55X6uQ1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;415&quot; height=&quot;394&quot; data-origin-width=&quot;415&quot; data-origin-height=&quot;394&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;task를 n/4 iteration으로 나눈 경우&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;task를 위 그림과 같이 n/4 iteration으로 나눈 경우, distributed reduction이다. 이 경우 communication overhead = reduction time + data communication time이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위 경우에서는 distributed reduction을 한 경우가 좋은데, 항상 그런 것은 아니다. 예를 들어 core가 1개, 또는 2개인 경우 task를 reduction하고 합치는 시간이 더 많이 들어가기 때문에 전자의 방식이 더 좋다. 일반적으로는 core가 4개 이상일 때 distributed reduction을 하는 것이 좋다고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;현대로 올수록 computation 속도가 매우 빨라졌기 때문에 communication cost보다 communication cost가 훨씬 더 크다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;data locality 최대화&lt;/li&gt;
&lt;li&gt;data exchange 최소화&lt;/li&gt;
&lt;li&gt;communication 최소화&lt;/li&gt;
&lt;li&gt;contention 최소화&lt;/li&gt;
&lt;li&gt;computation과 communication 겹치기 (non-blocking communication의 경우)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Performance Modeling&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;실행 시간 $T_p$ : p개의 processor가 문제를 해결하는 데 걸린 시간&lt;/li&gt;
&lt;li&gt;전체 overhead $T_0 = pT_{p} - T_s$&lt;/li&gt;
&lt;li&gt;speedup $S_p = \frac{T_s}{T_p}$ : sequential 실행 시간 / parallel 실행 시간&lt;/li&gt;
&lt;li&gt;효율 E = $\frac{S_p}{p} = \frac{T_s}{p \times T_p}$
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;parallel execution으로 얼마나 더 많은 task를 했는지에 대한 지표.&lt;/li&gt;
&lt;li&gt;만약 E가 1보다 큰 경우 superlinear라 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Amdahl's Law&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;변경으로 인해 f 부분이 K배 향상된 경우, 기존 실행 시간이&amp;nbsp;T = (1-f)T + fT일 때, 향상된 시간은&amp;nbsp;$T_K$ = (1-f)T + $\frac{f}{K}$T이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;parallel programming에서 f는 parallel하게 실행되는 부분이다. 즉슨, (1-f) 부분은 sequential한 부분이라는 뜻이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그러면 speedup은 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;speedup S = $\frac{T}{T_K} = \frac{1}{(1-f) + \frac{f}{K}}$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 수식 아래서 최대한 값을 키우는 것이 목적이므로 아래 2가지 방법을 모두 사용한다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;f를 크게&lt;/b&gt; 만들어야 한다: &lt;b&gt;parallel한 부분의 비중을 높여야&lt;/b&gt; 한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;f가 0.5인 경우 2배의 speedup이지만, 0.95인 경우는 20배의 speedup이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;K를 크게&lt;/b&gt; 만들어야 한다: core 개수를 늘려야 한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;data parallel computation의 경우 K를 키우기만 하면 된다. (core 개수만 늘이면 된다.)&lt;/li&gt;
&lt;li&gt;task parallel computation의 경우, parallel task는 고정되어 있기 때문에 K를 늘이기 쉽지 않다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Scalability&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;b&gt;$E_p$가 problem size가 변해도 계속 유지된다면 scalable&lt;/b&gt;하다고 한다. 일반적으로 p가 늘어나면 synchronization cost가 증가하기 때문에, core를 늘이더라도 동일한 효율을 얻을 수 없는 경우가 생긴다. $E_p$에 영향을 미치는 것은 communication과 synchronization이 대부분이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;strong scaling&lt;/b&gt; : problem size를 고정한 채로 K만 늘이는 방법. 예를 들어 n size의 문제를 1개, 2개, 4개, ...의 core로 해결할 때 speedup을 보는 방식이다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;weak scaling&lt;/b&gt; : core가 담당하는 problem size를 고정한 상태로 problem size를 늘이는 방법. 일반적으로 이야기하는 scalability이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Parallelization Techniques&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;따라서, parallel한 프로그램을 짜고 싶다면 다음과 같은 단계를 거쳐야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Parallel Algorithm 설계&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;parallel하게 해결하고 싶은 문제를 식별하고, parallel하게 풀 수 있는지 확인한다.&lt;/li&gt;
&lt;li&gt;병목을 식별하고, 해당 부분을 parallelism을 적용한다. 이 때 overhead와 load imbalance를 최소화해야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Computation&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;dependence graph를 사용해 task/data dependence를 분석한다.&lt;/li&gt;
&lt;li&gt;critical path를 최소화한다. 이는 load imbalance를 줄이는 효과도 있다.&lt;/li&gt;
&lt;li&gt;data dependency를 최소화한다. dependency가 있는 부분은 parallel의 효과가 떨어지기 때문이다. 특히 loop를 사용하는 부분을 잘 보아야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Synchronization and Load Imbalance&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;centralized 대신 distributed 방식을 사용해서 공유 정도를 줄인다.&lt;/li&gt;
&lt;li&gt;lock-free와 synchronization-free 알고리즘을 사용한다.&lt;/li&gt;
&lt;li&gt;coarse-grained task decomposition을 지양한다.&lt;/li&gt;
&lt;li&gt;critical path에 더 높은 priority를 부여한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Communication&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;data locality를 신경써야 한다.&lt;/li&gt;
&lt;li&gt;communication과 computation을 최대한 겹쳐야 한다.&lt;/li&gt;
&lt;li&gt;몇몇 경우, communicate보다 다시 계산하는 것이 더 빠를 수도 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;잘못된 내용이나 오탈자에 대한 지적, 질문&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;b&gt;등은 언제나 환영합니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>CS/Parallel Computing</category>
      <author>hyelie</author>
      <guid isPermaLink="true">https://hyelie.tistory.com/666</guid>
      <comments>https://hyelie.tistory.com/entry/%EC%9D%B4%EC%A2%85%EB%B3%91%EB%A0%AC%EC%BB%B4%ED%93%A8%ED%8C%85-Parallel-Computing-Basics#entry666comment</comments>
      <pubDate>Wed, 25 Oct 2023 03:15:48 +0900</pubDate>
    </item>
    <item>
      <title>[Maude] Token Ring 검증</title>
      <link>https://hyelie.tistory.com/entry/Maude-Token-Ring-%EA%B2%80%EC%A6%9D</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 글에서는 Designing Reliables Distributes Systems, 225p에 있는 Token Ring을 maude를 사용해 formal modeling하고 원하는 검증을 진행합니다. 원 프로젝트는 RAFT consensus algorithm을 모델링하는 것이지만, 그 전에 손풀기 느낌으로 좀 더 쉬운 token ring을 모델링합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Token Ring&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;In the &amp;ldquo;token ring&amp;rdquo; mutual exclusion algorithm, the nodes logically form a &amp;ldquo;ring&amp;rdquo; structure, as shown in Figure 13.1 where a node only knows the next node in this ring.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;The algorithm works as follows: there is one &amp;ldquo;token,&amp;rdquo; and only the node that holds the token may enter its critical section. The node then holds on to the token during its execution in the critical section, and passes the token to the next node in the ring when it exits its critical section. If a node that is not waiting to enter its critical section receives the token, it just passes the token to the next node.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;코드&lt;/h2&gt;
&lt;pre id=&quot;code_1698067504724&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;--- module in textbook
mod MESSAGE-CONTENT is
    sort MsgContent . --- message content, application-specific
endm

omod MESSAGE-WRAPPER is
    including MESSAGE-CONTENT .
    op msg_from_to_ : MsgContent Oid Oid -&amp;gt; Msg [ctor] .
endom
---

omod OBJECT-TOKEN-RING is
    including MESSAGE-WRAPPER . --- object들 간의 message 처리

    class Node | state : MutexState, next : Oid . --- 각 Node : state와 next를 가짐.

    sort MutexState .
    ops normal waiting acquire : -&amp;gt; MutexState [ctor] . --- state 정의

    --- prev, current, next
    vars P C N : Oid .

    op token : -&amp;gt; MsgContent [ctor] .

    --- current node가 normal이면 waiting으로 변경
    rl [AccessRequest] :
            &amp;lt; C : Node | state : normal &amp;gt;
        =&amp;gt;  &amp;lt; C : Node | state : waiting &amp;gt; .

    --- message를 받았을 때 current node가 normal이면 다른 state로 바뀌지 않고, next node에게 전달
    rl [SkipToken] :
            (msg token from P to C)     &amp;lt; C : Node | state : normal, next : N &amp;gt;   
        =&amp;gt;  (msg token from C to N)     &amp;lt; N : Node | &amp;gt; .

    --- message를 받았을 때 current node가 waiting이면 acquire로 변경 (critical section에 진입)
    rl [Access] :
            (msg token from P to C)     &amp;lt; C : Node | state : waiting &amp;gt;   
        =&amp;gt;                              &amp;lt; C : Node | state : acquire &amp;gt; .

    --- critical section에서 나감
    --- current node state가 acquire에서 normal로 변경, next node로 message 전달
    rl [Release] :
            &amp;lt; C : Node | state : acquire, next : N &amp;gt;
        =&amp;gt;  &amp;lt; C : Node | state : normal &amp;gt;    (msg token from C to N) .
endom

omod OBJECT-TOKEN-INIT is
    including OBJECT-TOKEN-RING .

    ops a b c d : -&amp;gt; Oid [ctor] .
    op init : -&amp;gt; Configuration .
    
    eq init = (msg token from d to a) --- token이 전달된 상황
    &amp;lt; a : Node | state : normal, next : b &amp;gt;
    &amp;lt; b : Node | state : normal, next : c &amp;gt;
    &amp;lt; c : Node | state : normal, next : d &amp;gt;
    &amp;lt; d : Node | state : normal, next : a &amp;gt; .
endom

***(
    load ./object-token-ring.maude
    set trace on .
)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;실행&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;아래 명령어로 실행할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1698067514610&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;load ./object-token-ring.maude
set trace on .&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;검증&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Normal State 검증&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;아래 명령어는 특정 2개의 node의 state가 normal인 상태를 찾는 명령어입니다.&lt;/p&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;search [1] init =&amp;gt;* C:Configuration
    &amp;lt; O1:Oid : Node | state : normal, next : O1N:Oid &amp;gt;
    &amp;lt; O2:Oid : Node | state : normal, next : O2N:Oid &amp;gt; .&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;실행 결과, Solution이 나옵니다. 즉, 2개의 node가 normal인 state가 존재함을 알 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;Solution 1 (state 10)
states: 11  rewrites: 11 in 0ms cpu (1ms real) (~ rewrites/second)
C:Configuration --&amp;gt; &amp;lt; c : Node | state : normal, next : d &amp;gt; &amp;lt; d : Node | state : normal, next : a &amp;gt;
O1:Oid --&amp;gt; b
O1N:Oid --&amp;gt; c
O2:Oid --&amp;gt; a
O2N:Oid --&amp;gt; b&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Mutual Exclusion&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;아래 명령어는 2개의 node가 acquire인 상태를 찾는 명령어입니다.&lt;/p&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;search [1] init =&amp;gt;* C:Configuration &amp;lt; C:Oid : Node | state : acquire &amp;gt; &amp;lt; C:Oid : Node | state : acquire &amp;gt; .&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;실행 결과, No Solution이 나옵니다. 한 번에 2개 이상의 node가 acquire하는 state가 존재하지 않음을 알 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;No solution.
states: 432  rewrites: 865 in 120ms cpu (168ms real) (7208 rewrites/second)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Critical Section 진입&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;아래 명령어는 특정 node의 state가 acquire이 되는 상태를 찾는 명령어입니다.&lt;/p&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;search [1] init =&amp;gt;* C:Configuration &amp;lt; C:Oid : Node | state : acquire, next : N:Oid &amp;gt; .
search [1] init =&amp;gt;* C:Configuration &amp;lt; a : Node | state : acquire, next : b &amp;gt; .
search [1] init =&amp;gt;* C:Configuration &amp;lt; b : Node | state : acquire, next : c &amp;gt; .
search [1] init =&amp;gt;* C:Configuration &amp;lt; c : Node | state : acquire, next : d &amp;gt; .
search [1] init =&amp;gt;* C:Configuration &amp;lt; d : Node | state : acquire, next : a &amp;gt; .&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;실행 결과, solution이 나옵니다. 즉, node가 token을 acquire하는 state가 존재함을 알 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;Solution 1 (state 10)
states: 11  rewrites: 11 in 0ms cpu (1ms real) (~ rewrites/second)
C:Configuration --&amp;gt; &amp;lt; b : Node | state : normal, next : c &amp;gt; &amp;lt; c : Node | state : normal, next : d &amp;gt; &amp;lt; d : Node | state : normal, next : a &amp;gt;
C:Oid --&amp;gt; a
N:Oid --&amp;gt; b&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Project/Model Checking</category>
      <author>hyelie</author>
      <guid isPermaLink="true">https://hyelie.tistory.com/664</guid>
      <comments>https://hyelie.tistory.com/entry/Maude-Token-Ring-%EA%B2%80%EC%A6%9D#entry664comment</comments>
      <pubDate>Mon, 23 Oct 2023 22:26:17 +0900</pubDate>
    </item>
    <item>
      <title>[Model Checking] Designing Reliable Distributed Systems</title>
      <link>https://hyelie.tistory.com/entry/Designing-Reliable-Distributed-Systems</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;TODO&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Ch. 2 : 자연수, 정수, list, binary tree, multiset 등 data type 정의&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Ch. 3 : equational specification이 만족하는지 확인&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Ch. 8 : rewrite logic을 사용해서 concurrent behavior 작성 방법을 설명&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Ch. 9 : rewriting logic을 사용해서 system의 동작 하나를 분석하는 방법&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FREE&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Ch. 10 : concurrent&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Ch. 11 : communication&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Ch. 12 : TCP와 같은 transport protocol 모델링&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Ch. 13 : distributed DB 모델링&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Ch. 2 Data Types&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Module&lt;/h4&gt;
&lt;pre id=&quot;code_1696674373535&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;fmod MODULE_NAME is
    MODULE_BODY
endfm&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;op : 연산자 정의&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;protecting은 다른 module 가져오는 것&lt;/li&gt;
&lt;li&gt;sort는 새로운 data type 정의&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ctor : 생성자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;var : 변수 선언&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;eq : 등식&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;data type import (sort)&lt;/li&gt;
&lt;li&gt;op 정의하기 : 생성자와 해당 연산&lt;/li&gt;
&lt;li&gt;var 정의하기 : 변수 정의하기&lt;/li&gt;
&lt;li&gt;eq 정의하기 : 어떤 연산의 결과가 어떤 것인지 정의하기&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Ch. 3 Equational Specification&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;equational specificaion은 specification이 실행될 수 있는 방법을 설명한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Ch. 8 Modeling Distributed System in Rewriting Logic&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;state가 바뀌는, dynamic system을 modeling하고 analysis하는 방법을 살핀다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;한 예시로, 변화는 잘 안바뀐다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;distributed system은 shared variable에 읽고 쓰거나, 서로 messaage를 주고받는 등 여러 개의 component로 이루어져 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;distributed system은 대부분 non-deterministic하다!&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Interleaving&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;interleaving system은 한 번에 하나의 component만 실행할 수 있는 system에 적합하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Rewriting Logic&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;state t에서 t'로 0번 이상의 rewriting logic을 사용해 t'로 변경할 수 있다. 이것들은 nondeterministic하게 이뤄진다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;crl : 조건이 있는 rewriting logic&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;rl : 조건 없는 rewriting logic&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Concurrency&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;rl로 적으면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Ch. 9 Executing Rewriting Logic Specifications in Maude&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;equational specification은 더 이상 적용할 수 없을 때까지 해당 specification을 실행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;반면 rewriting logic은 dynamic system의 모든 가능한 behavior를 정의하며, 종료되지 않을 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;rewriting logic과 equational이 같은 변수에 의해 적용될 경우, equational을 먼저 rewriting logiㅊ 형태로 바꾼다. 이를 위해서는 rewriting logic의 좌항에는 생성자가 와야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1696781943178&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;set trace on .&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;으로 규칙 적용과 rewriting 과정을 기록한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;rew, frew&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;b&gt;rew&lt;/b&gt;나 &lt;b&gt;frew&lt;/b&gt;를 사용해 rewriting을 할 수 있다. rew는 leftmost에만 적용, frew는 가능한 것들에 적용.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;search&lt;/h4&gt;
&lt;pre id=&quot;code_1696782525955&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;search t arrow pattern .
search t arraw pattern such that cond .&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;search 명령어를 사용해 모델 검증을 할 수 있다. 그 결과로 특정 속성을 만족하는지, 다음 state를 검증할 때 쓸 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;=&amp;gt;1 : t에서 한 step으로 도달할 수 있는 state&lt;/li&gt;
&lt;li&gt;=&amp;gt;* : t에서 0개 이상의 step으로 도달할 수 있는 state&lt;/li&gt;
&lt;li&gt;=&amp;gt;+ : t에서 1개 이상의 step으로 도달할 수 있는 state&lt;/li&gt;
&lt;li&gt;=&amp;gt;! : 더 이상 rewrite될 수 없는 state&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;show path&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;특정 state의 path를 출력한다. 만약 =&amp;gt;!가 무한루프라면 상한을 걸어서 둘 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Ch. 10 Concurrent Objects in Maude&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;distributed system의 각 component를 object로 모델링하고, 각&amp;nbsp; component는 message를 주고받아서 communicate할 수 있다. 여기서는 rewriting logic을 사용해 concurrent logic을 모델링하는 방법을 살펴본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Ch. 11 Modeling Comunications in Maude&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;ch. 10에서는 concurrent system이 concurrent object들의 multiset으로 표현하는 방법을 살펴봤었다. 이를 위해서는 model을 abstraction해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;communication은 sync일 수도 이쏙, async일 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;rewriting은 communication primitive에 대한 뭔가를 제공하지 않기 때문에 통신 그 자체를 rewriting logic을 사용해 모델링해야만 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;일단 비동기식을 쓸 때는, 메시지를 보내고, 받은 사람이 해당 메시지를 받지 않고 자신의 action을 취한 이후 받은 메시지를 처리하는 경우에, 문제가 발생하기 때문에 이 경우를 처리할 수 있는 로직을 작성해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Ch. 13 Distributed Algorithm&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;ch. 12는 생략했다. TCP를 굳이 쓸 필요는 없을 것 같아서.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;2-phase commit, mutual exclusion, leader election, concencus algorithm을 살핀다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이제 직접 해 보자!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Project/Model Checking</category>
      <author>hyelie</author>
      <guid isPermaLink="true">https://hyelie.tistory.com/655</guid>
      <comments>https://hyelie.tistory.com/entry/Designing-Reliable-Distributed-Systems#entry655comment</comments>
      <pubDate>Mon, 9 Oct 2023 21:41:47 +0900</pubDate>
    </item>
    <item>
      <title>기타 면접대비 질문</title>
      <link>https://hyelie.tistory.com/entry/%EA%B8%B0%ED%83%80-%EB%A9%B4%EC%A0%91%EB%8C%80%EB%B9%84-%EC%A7%88%EB%AC%B8</link>
      <description>&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;nbsp;간단한 자기소개&lt;/li&gt;
&lt;li&gt;지원동기&lt;/li&gt;
&lt;li&gt;장단점&lt;/li&gt;
&lt;li&gt;나의 비전 : 사람들에게 긍정적인 영향을 줄 수 있는 사람이 되는 것.&lt;/li&gt;
&lt;li&gt;프로젝트 하면서 어려움 극복 : bizkicks 얘기?&lt;/li&gt;
&lt;li&gt;회사에 대해 궁금한 점&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;협업 방식&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;각 프로젝트&lt;/li&gt;
&lt;li&gt;인성&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;개발지식&lt;/h2&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;객체지향&lt;/h3&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333;&quot; data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정의&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;class vs instance&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특징 4가지&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장단점&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333;&quot; data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체들의 상호작용으로 프로그램을 구현하는 방법.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체는 어떤 개념을 추상화하고 모델링한 요소. state와 behavior를 가짐.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 추상화란 불필요한 정보는 숨기고 중요한 정보만을 보여주는 것(컴퓨터 과학)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;class : 설계도, instance : class로 만들어진 메모리에 올라간 실체.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특징&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;abstraction : OOP에서 abstraction은 불필요한 정보는 private으로 숨기고 중요한 정보를 public으로 노출하고, 공통된 부분을 상위 class로 추출하는 것.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;encapsulation : information hiding&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;inheritance : 공통 부분을 추출&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;polymorphism : static (overloading) - 이름만 같은 함수로 사용하는 방법, dynamic (overriding) - 부모 class method를 자식 class에서 재정의&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장단점&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드 재사용률이 높아짐&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단점으로는 객체들이 상호작용하기 때문에 느리다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;SOLID&lt;/h3&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333;&quot; data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5가지 + 각각의 예시&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333;&quot; data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;single responsibility principle&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 class가 하나의 책임을 가짐, 변경의 이유가 하나뿐.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지키지 않으면 한 책임의 변경에 의해 다른 책임이 변경될 수 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;open closed principle&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확장에는 열려 있고 수정에는 닫혀 있음. 코드 변화를 적게 하면서 기능 변화/확장할 수 있게.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확장 시 변경으로 인한 영향을 최소화하는 것이 목적.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시로 분기문 : polymorphism이나 map을 사용해 적용 가능.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;liskov substitution principle&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;subtype이 supertype으로 치환할 수 있음. supertype 자리에 subtype 넣어도 수행에는 변화가 없어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;parent class method의 동작 의도를 크게 수정하면 안되는 것이 목적.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시 : 직사각형/정사각형. - 명확한 관계가 있을 때만 상속을 써야 함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;interface segregation principle&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;object는 자신이 사용하지 않는 method를 포함한 interface에 의존하면 안 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용하지 않는 method를 가진 interface에 의존하는 경우, 사용하지 않는 method가 변경되어도 재컴파일되어야 하기 때문.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;dependency inversion principle&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;dependency를 가지는 경우, 구현체가 아니라 추상화에 의존해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;inversion인 이유는, 기존에는 상위 모듈이 하위 모듈에 의존하고 있었다. 그러나 interface를 사용하면서 하위 모듈이 상위 모듈(abstraction)에 의존하게 되었다. 때문에 inversion.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지키지 않는 경우, 요구사항 하나의 변화로 인해 dependency가 걸린 모든 것들을 다 바꾸어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시 : dependency injection (upcasting), abstract factory&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;디자인패턴&lt;/h3&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333;&quot; data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정의, 예시&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;template&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;strategy&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;abstract factory&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;factory&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;singleton&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;adapter&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;proxy&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;composite pattern&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;decorator pattern&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;facade&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;observer&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333;&quot; data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소프트웨어 설계 시 많이 사용하는 구조들을 모아놓은 패턴.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 사용한 것들&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;template&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;customize한 부분과 invariant한 부분을 구분하는 방법. invariant는 abstract class에서 정의, customize는 concrete class 에서 정의. 객체에서 abstraction이고, 공통 부분만 빼내는 방식.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;strategy&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 알고리즘의 여러 변형본이 필요할 때 사용하는 방법. 같은 것을 인자로 받는 if-else문을 분리하기 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시로 내가 사용한 if-else를 변경한 것.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;abstract factory&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;concrete class 없이 연관된 객체를 생성하는 방법&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단점으로는 새로운 것을 넣기 힘들다는 것.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fafafa; color: #333333; text-align: start;&quot;&gt;내가 사용한 부분은 auth client와, converter에서 authclient가 필요했다. 그러나 이를 explicit하게 생성하면 의존성이 드러난다. 때문에 이를 숨기고, auth client와 converter를 같이 생성하게 했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;factory&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;class 생성자를 subclass에 맡김.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시로는 attribute 값 할당을 subclass에서 해 줘야 할 때 사용.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;singleton&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fafafa; color: #333333; text-align: left;&quot;&gt;단 하나의 instance만 사용하게 하는 방법. 메모리 낭비 방지.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;private 생성자 + static 변수를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fafafa; color: #333333; text-align: left;&quot;&gt;DB connection pool 등이 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;adapter&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서로 호환되지 않는 interface를 작동시키는 패턴. target의 행동에 추가적인 로직을 처리해 기존 interface가 동작하게 하는 방식. 예시로는 n2t에서 네이버 형식을 티스토리 형식으로 바꾸기 위해 중간 자료구조를 하나 두고, 이를 사용한 방식이다. 직접적으로 class는 아니지만 . . .&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;proxy&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실 연산을 다른 객체로 위임하는 패턴.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시로실 객체를 생성하기 전 caching을 하거나 호출 시점에 만들 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;composite pattern&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체-부분 관계를 가질 때 object 관계를 정의. 내가 사용한 부분은 n2t scrapper에서 모든 type을 처리하기 위해 if-else를 변형한 strategy를 사용했는데, 이렇게 쓰니 sectionparser가 test module parsing + section parsing 2가지 역할을 가지게 되었다. 그러나 이런 tree 구조의 경우 composite pattern을 사용했으면 더 좋지 않았을까 싶다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;decorator pattern&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;composite와 유사. object를 dynamic하게 확장할 때 사용.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카페 음료수 예시&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;facade&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;복잡한 subsystem에 대한 interface를 만드는 방법&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;observer&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 object가 다른 여러 개의 object에게 영향을 끼칠 때 object끼리의 direct coupling 만드는 대신 observer를 두고, observer가 해당 object들에게 대신 전달하는 방식. 이를 통해 coupling 줄일 수 있음. ex) front에서 listener&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;Docker&lt;/h3&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333;&quot; data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개념 (container)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;virtualize vs containerize&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;image&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333;&quot; data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 프로그램의 실행에 필요한 모든 파일을 포함한 패키지를 image라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 image를 실행한 것이 container.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;vm은 운영체제까지 띄우는 방식&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;container는 코드, 실행 환경을 포함해 띄우는 방식.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가상화의 정도가 다름. vm은 hw까지 가상화하지만 container는 sw만 가상화함. 즉, container는 os를 공유.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;vm은 host os 위에 hypervisor(os의 resource 관리해주는 도구)를 올리고, hypervisor가 각각의 os를 올림. hw를 가상화하기 때문에 각 os는 완전히 independent하지만 무겁다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;container는 host os 위에 docker engine이 올라가고, docker engine이 각각의 container를 관리함. host는 os는 공유하되, process들의 격리 환경을 만듬.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;Kubernetes&lt;/h3&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333;&quot; data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개념&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333;&quot; data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;container application들의 deploy, scaling 등을 제공하는 관리 툴.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;scaling은 CPU나 memory같은 자원 등등의 metric에 따라서 scaling한다. scaling은 pod 개수나 resource 할당량 등을 조절할 수 있다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;Git 전략&lt;/h3&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333; text-align: start;&quot; data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;Git Flow&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;master, develop, feature, release, hotfix&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;master에서 develop 분기, develop에서 feature 분기.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;develop에서 feature가 다 merge되었다면 qa를 위해 release 분기. 이후 버그 수정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최후에는 release를 develop과 main으로 merge.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;hotfix는 main에서 나옴.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;bizkicks의 경우 develop과 release를 하나로 합쳐 사용했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;Github Flow&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;main은 항상 배포가 가능한 상태로 유지 / 최신 / stable을 유지해야 한다. 이외 feature가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;merge 전에 테스트를 해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;gitlab&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;배포만을 담당하는 production branch를 하나 두고, pre-production (배포 전 테스트) branch를 하나 둔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;git flow와 github flow를 쓰면서 느낀 차이점은, develop branch가 있냐 없냐이다. git flow는 develop branch를 하나 두고 github flow는 그게 없다. git flow의 경우 develop branch에 변경사항이 생겼을 때 문제가 생기더라도 부담감이 덜하다. main이 아니기 때문에.. 그리고 release를 위해 별개의 branch를 생성하기 때문에 각 branch의 역할 분담이 확실하고, 오류가 났을 때도 대처가 편한 것 같다. 그러나 복잡도가 높아 PR이 많이 생성된다는 단점이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 github flow에서 merge를 할 때 충분한 테스트를 하지 않으면 main이 망가질 수 있다는 위험?부담이 좀 있는 것 같다. 그렇지만 훨씬 간단하기 때문에 작은 프로젝트에서 진행하기 좋을 것 같다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;MySQL, MariaDB&lt;/h3&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;왜 사용했는지&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;어떤 특징이 있는지&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333;&quot; data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;왜 사용했는지&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;어떤 특징이 있는지&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;RDB에 대한 선택 이유 : 데이터가 정형화되어 있기 때문에 이해하기 쉽다. 비정형화되어 있는 NoSQL보다 이해하기가 더 쉽고, NoSQL을 사용할 만큼 트래픽이 많지 않을 거라 생각했기 때문에 RDB를 선택했다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;RDB에는 MySQL, PostgreSQL, Oracle 정도가 있다고 생각한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;oracle은 과금을 해야 해서 선택지에서 아웃.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;데이터를 자주 읽는 경우 mysql, 자주 쓰는 경우 postgres&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;mysql : write lock을 사용해 concurrency 구현. 때문에 쓸 때 느림. 러닝커브가 낮음&lt;/li&gt;
&lt;li&gt;PostgreSQL : 객체 저장 가능(배열 등). write lock을 걸지 않고 mvcc를 사용함.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fafafa; color: #333333; text-align: start;&quot;&gt;MariaDB는 MySQL에서 fork되어 나온 것. &lt;/span&gt;mariadb 사용한 이유는 mysql은 영리 목적으로 하면 라이센스 비용을 내야 하는 것으로 알고 있어서 해커톤 했을 때는 이거 나중에 채팅앱으로 만들자~ 해서 mariadb 사용하기로 했다. 어차피 둘 다 거의 유사한 db라서 그렇게 사용하기로 함.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;격리 수준&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;mysql : repeatable read&lt;/li&gt;
&lt;li&gt;oracle : read commited&lt;/li&gt;
&lt;li&gt;postgresql : read commited&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;MVC&lt;/h3&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333;&quot; data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;model-controller-view&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;model : 데이터 저장&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;controller : model, view 변경&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;view : 사용자에게 보이는 부분&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;spring의 경우, model은 repository(db), controller는 controller, service, view는 jsp가 처리.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;mvc1의 경우 jsp가 controller, view 둘 모두를 수행하고 있었다. 때문에 현대 spring은 mvc 2 model을 사용한다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;Async / Sync / Block / Non-Block&lt;/h3&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333;&quot; data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각각의 정의&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333;&quot; data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sync 여부 : 작업이 동기화 되었는지 여부&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sync : 작업이 동기화 된 방식. A가 B를 call했을 때 B의 결과가 A로 바로 들어가면 sync.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;async : 작업이 동기화되지 않은 방식. A가 B를 call했을 때 A가 B의 결과를 요청해야 하면 async. async의 경우 대기 시간이 필요한 작업을 효율적으로 다룬다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;blocking 여부 : 제어권을 넘기는지 여부. 작업이 blocking되는지 여부라고 보면 된다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;blocking : A가 B를 call했을 때 B가 자신의 작업이 끝나기 전까지 제어권을 넘겨주지 않는 방식. A의 작업이 block된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;non-blocking : A가 B를 call했을 때 B가 제어권을 바로 넘겨주는 방식. A의 작업이 block되지 않는다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Blockchain&lt;/h3&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;블록체인은 안전하게 데이터를 저장하고, 운영할 수 있게 하는 P2P 네트워킹 기술이다. 누군가가 블록체인에 트랜잭션을 추가하려고 하면 이 요청은 네트워크에 전송되며, 공개 트랜잭션이 승인된 후에만 블록체인에 추가할 수 있다. 이를 통해 중앙 시스템 없이 모든 거래에 대해 모든 사용자들이 알 수 있는 P2P 네트워크가 구축된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 네트워크에 전파&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 트랜잭션 검증 (잔고 충분한지, 서명 유효한지, 중복되지 않았는지)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 채굴 : 블록의 해시값이 해당 블록체인의 기준에 맞는 결과가 나올 때까지 해시값을 반복적으로 계산해 해당 조건이 만족하는 블록을 블록체인 끝에 추가하고 네트워크로 전송. (만약 2개 이상의 노드가 다른 결과값을 블록체인 결과가 분기되는데, 네트워크 과정에서 더 긴 체인을 사용함.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- 채굴 : 해시값을 계산하기 위해 많은 시도가 필요하며, 때문에 공격자가 네트워크를 공격하기 위해 거대한 리소스를 사용해야 한다. 또한 채굴은 누구나 할 수 있기 때문에 탈중앙화를 꾀할 수도 있음. + 보상 시스템으로 유통도 처리함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 한편 해싱 결과를 찾기 때문에, 찾는 것은 어렵지만 검증은 매우 빠름.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;체인 : 각 트랜잭션이 체인 형태로 엮여 있음. 좀 더 자세하게는, 이전 블록의 해시값을 현재 블록에 넣어 사용함. 때문에 이전 블록을 조작하는 경우 현재 블록의 해시값이 크게 바뀌게 됨.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;아키텍처&lt;/h3&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아키텍처 간단히(pipelined CPU 동작 간단히) + 베릴로그 언어 &lt;br /&gt;HW가 lock을 어떻게 처리하는지&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RISC-V&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;R(arithmetic), I(load), S(store), B(branch), J(jump)&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;636&quot; data-origin-height=&quot;213&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cx5Suy/btsHth0WlDE/pZHNODlbDNX431diewEQuK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cx5Suy/btsHth0WlDE/pZHNODlbDNX431diewEQuK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cx5Suy/btsHth0WlDE/pZHNODlbDNX431diewEQuK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcx5Suy%2FbtsHth0WlDE%2FpZHNODlbDNX431diewEQuK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;636&quot; height=&quot;213&quot; data-origin-width=&quot;636&quot; data-origin-height=&quot;213&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1) IF : instruction fetch&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2) ID : instruction decode and operand fetch&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3) EX : ALU/execute&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4) MEM : memory access&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5) WB : write-back&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;single-cycle&amp;nbsp;CPU는&amp;nbsp;비효율성&amp;nbsp;때문에&amp;nbsp;사용하지&amp;nbsp;않는다.&amp;nbsp;모든&amp;nbsp;insturction이&amp;nbsp;가장&amp;nbsp;느린&amp;nbsp;instruction만큼&amp;nbsp;걸리기&amp;nbsp;때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;464&quot; data-origin-height=&quot;288&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mGwPN/btsHu6XyEaT/J7pYKeTbTmMAkceh0xy2r0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mGwPN/btsHu6XyEaT/J7pYKeTbTmMAkceh0xy2r0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mGwPN/btsHu6XyEaT/J7pYKeTbTmMAkceh0xy2r0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmGwPN%2FbtsHu6XyEaT%2FJ7pYKeTbTmMAkceh0xy2r0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;464&quot; height=&quot;288&quot; data-origin-width=&quot;464&quot; data-origin-height=&quot;288&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;structure hazard : resource가 busy인 것. 여기서는 memory에 값을 쓸 때 + 읽을 때가 동시에 오는 경우 발생함. 이를 해결하기 위해 clock을 절반으로 나눠, 앞쪽 절반에서는 read, 뒤쪽 절반에서는 write 하는 식으로 함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;data hazard : 이전 instruction 실행 결과를 기다려야 하는 것. stall(실행 멈춤)하거나 forwarding(실행 결과를 앞단에 넣어줌)한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;control hazard : 이전 instruction 실행 결과에 따라 다음 instruction이 결정되는 것. 예측을 통해 해결. +4(다음 instruction)을 수행하다가, 분기 결과에 따라 해당 instruction을 멈출지 결정.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;hw의 lock 처리 방법&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;atomic instruction의 경우 hw가 중간에 다른 명령어를 실행시키지 않는 방식으로 구현함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;memory에 대한 접근이기에, memory bus에서 해당 memory address로 접근하는 요청에 대해 lock을 건다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;async 방식으로 프로그래밍을 하는 거의 첫 경험이라 개념 잡는 데도 많은 시간이 걸렸고, instruction 종류도 많아 구현하는 데 어려웠다. 이전에는 sequential programming 형식으로, 각 module의 연산 결과를 다른 module에서 바로바로 받아와 쓸 수 있었는데, 베릴로그는 모든 상태 변화가 clock 신호와 동기화되어 있기 때문에 기존 프로그래밍 패러다임과 차이가 있어서 어려웠다. 해결은 동기/비동기에 대한 개념을 명확하게 잡고, &lt;span style=&quot;background-color: #fafafa; color: #333333; text-align: start;&quot;&gt;결국 시간 많이 쓰니까 해결 되더라.&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;Async / Await&lt;/h3&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333;&quot; data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;await는 promise에서 then을 쓰는 것과 동일하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉.. promise의 값이 나올 때까지 await한다는 것.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;let a = await func();을 통해 async func()의 promise 결과를 바로 받을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실상 코드 이쁘게 만드는 방법. (더 직관적인 이해 가능)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오류 코드도 간편해지고 sync style이랑 코드가 비슷해짐. callback도 사용하지 않아도 됨. 여러 개의 promise도 사용할 수 있음.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;javascript 동작 방식&lt;/h3&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333;&quot; data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;asynchronous event loop model ?&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;event가 발생할 때마다 이를 처리하기 위한 함수를 등록하고, event 발생 시 해당 함수 실행하는 방식.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;javascript는 &lt;span style=&quot;background-color: #fafafa; color: #333333; text-align: start;&quot;&gt;single thread 기반의, event loop model.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;내부적으로 call stack에다가 코드를 넣어서 동작시킨다. 만약 바로 실행할 수 없는 코드의 경우(timeout, event listener 등등), browser web API 내에 해당 코드들을 넣어 둔다. (일종의 대기실, 새로운 thread가 생성되어 작업을 함)&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #fafafa; color: #333333; text-align: start;&quot;&gt;내부적으로 동작 과정에서 async하게 background thread를 사용한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fafafa; color: #333333; text-align: start;&quot;&gt;&amp;nbsp;- 즉,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #fafafa; color: #333333; text-align: start;&quot;&gt;async 작업이 발생하면 해당 작업을 background thread pool로 보내 새로운 thread를 생성해 사용한다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fafafa; color: #333333; text-align: start;&quot;&gt;browser web API에서 작업이 끝나면 바로 call stack으로 들어가서 코드가 실행되는 게 아니라, callback queue로 들어간 이후에 하나씩 call stack으로 올려보낸다. (단, call stack이 빌 때만 올려준다. - 때문에&amp;nbsp; &lt;span style=&quot;background-color: #fafafa; color: #333333; text-align: start;&quot;&gt;I/O가 많은 작업에 유리하며 CPU 작업이 많은 서버인 경우 불리하다.&lt;/span&gt;)&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;Node.js vs Spring&lt;/h3&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333;&quot; data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;node.js&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- asynchronous event loop model을 사용하므로, I/O 작업을 비동기로 처리하기 때문에 I/O가 많은 작업에 유리함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- CPU 작업이 많은 경우에는 callback queue에 있는 것이 실행되지 않기 때문에 좋지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 가볍다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;spring&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- spring은 CPU 작업이 많은 경우가 좋다. &lt;span style=&quot;background-color: #fafafa; color: #333333; text-align: start;&quot;&gt;연산이 많은 경우 thread를 사용해 명시적으로 처리할 수 있기 때문에 효율적.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- type-safe하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 실행에 오래 걸린다. (JVM, GC)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 플랫폼 독립. (jvm 위에서 돌 수 있음)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- thread 생성 위해서는 개발자의 관리가 필요함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;성능상 큰 차이는 없을 것 같다. spring에서 thread 생성해서 오래 걸리는 작업을 multi thread로 돌리면 되는 것 아닌지?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;spring은 thread pool을 사용해 thread를 관리함. 내부적으로 몇 개의 thread를 미리 생성해 둠. 이후 필요한 작업에 할당했다가 돌려 받음. (thread를 생성/삭제하는 게 OS, JVM에 로드를 많이 주고, 무한히 생성할 수도 있기 때문.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 초기에 정해진 크기만큼 thread 생성함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 사용자 요청이 들어오면 queue에 담아두고, idle 상태(놀고 있는) thread가 있으면 queue에서 꺼내서 작업을 thread에 할당함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- idle인 thread가 없다면 작업은 queue에서 대기, 만약 queue가 가득 차면 thread 새로 생성.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- task 완료 시 thread는 idle 상태로 돌아가고, queue가 비고 thread가 초기 개수보다 더 많다면 destroy.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;=&amp;gt; 미리 만들어 놓고, 필요한 작업에 할당했다가 돌려받음.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;JPA &amp;amp; Transactional &amp;amp; Test annotation&lt;/h3&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333;&quot; data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;@transational&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;해당 메소드가 transation이 되게 보장해줌. 여러 DB 쿼리가 있으면 이것들을 transaction으로 묶음. 하나라도 문제 발생 시 롤백. 종료 시 commit().&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fafafa; color: #333333; text-align: start;&quot;&gt;여러 개의 transactional이 있는 경우, 격리 수준을 사용해서 해당 리소스에 접근. 순서는 jvm 스케쥴링에 따름.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;rollbackfor option : 기본적으로 unchecked exception만 롤백하기 때문에 exception도 롤백하게 지정&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;readonly option:&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;(일반적으로) entity가 영속성에 영속될 때, 해당 entity의 상태를 snapshot으로 남긴다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #fafafa; color: #333333; text-align: start;&quot;&gt;snapshot과 entity 상태를 비교해 변경된 내용만 update query를 모아 DB로 날린다.(이게 dirty checking이다!)&amp;nbsp;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #fafafa; color: #333333; text-align: start;&quot;&gt;transactional이 붙은 method는 트랜잭션 commit 시 DB에 flush함.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;readonly true : dirty checking 발생 X. read 한 이후 DB로 flush하지 않는다. (snapshot을 찍지 않기 때문에 변경사항 감지 X. 때문에 메모리 절약도 가능) 때문에 변경사항 반영 안 되는 것으로 알고 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;---&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;slice test : 특정 계층만 처리 가능. @springboottest : 전체, @webmvctest: controller, 등&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;@test&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;junit에서 test annotation 다 모아서 테스트 돌려줌.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;@springboottest&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;spring에서 bean 등록한 것들 &quot;다&quot; 모아서 injection해줌.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;테스트 코드에서 @transactional 쓰면 쿼리 날린 것 다 롤백해 줌. (안붙이면 롤백안됨)&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;bean 등록한거에서 가져오고 싶으면 @autowired 쓰면 됨&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;@webmvctest&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;controller 관련만 로드함. @mockbean 만들고 리턴값 정의해서 써야 함.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;플젝&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;SW개발병&lt;/h3&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333;&quot; data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;육군본부 운영지원과에서 내부 WAS를 유지보수하는 일을 했다. 주로 추가 요구사항 구현, 버그 픽스, 보안 취약점 수정 등을 진행함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 일을 했는지, 내부 구성은 어땠는지, 어떤 일이 제일 기억에 남았는지&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 내가 어떤 과에서 뭘 유지보수했고, 어떤 걸 유지보수했으며, 어떤 이슈가 있었고, 어떻게 고쳤는지.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;육군본부 내부에서 사용하는 웹 서버를 유지보수 했습니다. 서버 자체는 내부에서 띄워주고, 어떤 방식으로 띄우는지는 알 수 없었다. 주로 추가 요구사항 구현, 버그 픽스, 등등 업무를 진행했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내부 코드는 전자정부프레임워크 3.2를 사용한다. 2014년도에 나왔네..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;직접적으로 티켓같은 걸 맡지는 않고 파견 형식이라 처리하는 방식.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로컬 환경 구성은 조금 어지러운데, 내부 규칙으로 인해서 java 파일을 올리지 못한다. 그래서 컴파일된 .class 파일들만 다 올라가 있는데, 그것들을 디컴파일해서 오류 수정하고 처리했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;젤 힘들었던 것 : 로컬 구성 + 인수인계 + git 없음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;+ 소마는 왜 했는지 - SW개발병 가려고 했다고 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쿼리 최적화는 어떤 거 했는지 :&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://hyelie.tistory.com/entry/Troubleshooting-%EC%BF%BC%EB%A6%AC-%EC%B5%9C%EC%A0%81%ED%99%94-Subquery-JOIN&quot;&gt;쿼리 최적화 포스트&lt;/a&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;타이밍 + 권한이 없어서 추가 분석 못한 게 아쉬움.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MVC 구조는 뭐고, 어떻게 수정했는지, 얼만큼 수정했는지 기존 코드는 controller에 모든 코드가 다 있어서 service repository로 분리했다고 했더니, 굳이 나눌 필요가 있나요? 라는 질문이 들어왔다. 유지보수하기 편하게 하려고 그렇게 했다고 답했다. 하나에 다 몰아두면 뭐가 뭔지 구분하기 힘드니까.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;보안&lt;/h4&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;XSS, SQL Injection, path traversal, web shell&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;XSS는 동적 웹 페이지의 입력 폼에 javascript 명령어를 넣는 등의 방식을 통해 해당 페이지에 접근하는 사용자의 브라우저에서 악의적인 스크립트를 실행시키는 공격 방법이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장&amp;nbsp;쉬운&amp;nbsp;해결방법은&amp;nbsp;모든&amp;nbsp;입력값에&amp;nbsp;대해&amp;nbsp;&amp;lt;,&amp;nbsp;&amp;gt;,&amp;nbsp;&quot;,&amp;nbsp;'와&amp;nbsp;같은&amp;nbsp;script에서&amp;nbsp;사용하는&amp;nbsp;특수문자를&amp;nbsp;HTML&amp;nbsp;character&amp;nbsp;entity&amp;nbsp;refernce로&amp;nbsp;바꾸는&amp;nbsp;방법으로&amp;nbsp;바꾸는&amp;nbsp;것이다.&amp;nbsp;&amp;lt;&amp;nbsp;&amp;rarr;&amp;nbsp;&amp;amp;lt;&amp;nbsp;&amp;gt;&amp;nbsp;&amp;rarr;&amp;nbsp;&amp;amp;gt;&amp;nbsp;&quot;&amp;nbsp;&amp;rarr;&amp;nbsp;&amp;amp;quot;&amp;nbsp;'&amp;nbsp;&amp;rarr;&amp;nbsp;&amp;amp;#39;&amp;nbsp;이&amp;nbsp;경우,&amp;nbsp;모든&amp;nbsp;사용자&amp;nbsp;입력값에&amp;nbsp;대해&amp;nbsp;검증해야&amp;nbsp;하기&amp;nbsp;때문에&amp;nbsp;코드가&amp;nbsp;난잡해질&amp;nbsp;수&amp;nbsp;있으며&amp;nbsp;사용자가&amp;nbsp;html을&amp;nbsp;입력할&amp;nbsp;수&amp;nbsp;없다는&amp;nbsp;단점이&amp;nbsp;있다.&amp;nbsp;그러나&amp;nbsp;난잡한&amp;nbsp;코드는&amp;nbsp;servlet&amp;nbsp;filter로&amp;nbsp;대체할&amp;nbsp;수&amp;nbsp;있고,&amp;nbsp;사용자가&amp;nbsp;직접&amp;nbsp;html을&amp;nbsp;입력하게&amp;nbsp;하는&amp;nbsp;대신&amp;nbsp;개발자가&amp;nbsp;설정한&amp;nbsp;특정&amp;nbsp;태그만&amp;nbsp;white-list로&amp;nbsp;열어두던지,&amp;nbsp;아니면&amp;nbsp;정해진&amp;nbsp;format으로만&amp;nbsp;출력되게&amp;nbsp;하는&amp;nbsp;방법이&amp;nbsp;있을&amp;nbsp;것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SQL Injection은 사용자가 입력한 값이 필터링이나 이스케이핑 없이 DB로 들어가는 경우에 할 수 있는 공격 방법이다. 변수가 바로 SQL문에 들어가는 경우 주석처리나 OR 등의 연산자를 통해 원하는 SQL을 실행할 수 있게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장&amp;nbsp;쉬운&amp;nbsp;해결방법은&amp;nbsp;prepared&amp;nbsp;statement를&amp;nbsp;사용하는&amp;nbsp;것이다.&amp;nbsp;prepared&amp;nbsp;statement가&amp;nbsp;무엇인지&amp;nbsp;설명하려면&amp;nbsp;그것만으로도&amp;nbsp;포스팅&amp;nbsp;하나가&amp;nbsp;나오니,&amp;nbsp;간단하게만&amp;nbsp;설명하자면&amp;nbsp;&quot;SQL&amp;nbsp;캐싱을&amp;nbsp;통해&amp;nbsp;사용자&amp;nbsp;입력을&amp;nbsp;순수&amp;nbsp;문자열로만&amp;nbsp;치환해서&amp;nbsp;SQL을&amp;nbsp;날리는&amp;nbsp;방법&quot;입니다.&amp;nbsp;SQL&amp;nbsp;Injection을&amp;nbsp;막아줄&amp;nbsp;뿐만&amp;nbsp;아니라&amp;nbsp;캐싱해두기&amp;nbsp;때문에&amp;nbsp;같은&amp;nbsp;SQL을&amp;nbsp;실행시킬&amp;nbsp;때&amp;nbsp;시간이&amp;nbsp;단축된다는&amp;nbsp;장점도&amp;nbsp;있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Path&amp;nbsp;Traversal은&amp;nbsp;파일&amp;nbsp;다운로드&amp;nbsp;시&amp;nbsp;파일&amp;nbsp;이름을&amp;nbsp;이용한다는&amp;nbsp;것에&amp;nbsp;착안하여&amp;nbsp;window의&amp;nbsp;경우&amp;nbsp;../이나,&amp;nbsp;unix의&amp;nbsp;경우&amp;nbsp;..\와&amp;nbsp;같이&amp;nbsp;상위&amp;nbsp;폴더로&amp;nbsp;움직여&amp;nbsp;시스템의&amp;nbsp;중요한&amp;nbsp;정보를&amp;nbsp;탈취할&amp;nbsp;수&amp;nbsp;있는&amp;nbsp;공격&amp;nbsp;기법이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장&amp;nbsp;쉬운&amp;nbsp;해결방법은&amp;nbsp;사용자&amp;nbsp;입력에&amp;nbsp;../,&amp;nbsp;./,&amp;nbsp;.|,&amp;nbsp;..|,&amp;nbsp;..\,&amp;nbsp;.\,&amp;nbsp;||와&amp;nbsp;같은&amp;nbsp;특수문자가&amp;nbsp;있으면&amp;nbsp;지워버리거나&amp;nbsp;접근을&amp;nbsp;차단하는&amp;nbsp;방법이다.&amp;nbsp;단순히&amp;nbsp;../를&amp;nbsp;공백으로&amp;nbsp;치환해버린다면&amp;nbsp;....//와&amp;nbsp;같이&amp;nbsp;여러&amp;nbsp;번&amp;nbsp;중첩해서&amp;nbsp;사용할&amp;nbsp;경우&amp;nbsp;뚫릴&amp;nbsp;위험이&amp;nbsp;있기&amp;nbsp;때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Upload Attack은 첨부파일과 같이 사용자가 파일을 서버로 업로드하는 기능을 악용해 서버에서 실행되는 .jsp나 .php와 같은 스크립트를 업로드하고, 해당 스크립트를 통해 서버측의 권한을 탈취할 수 있다. 여러 방어를 우회하기 위해 a.jsp.jpg와 같이 확장자를 2개 붙이기도 하고, a.jSp와 같이 대소문자를 바꿔넣기도 하고, a.jsp%00.jpg와 같이 null 문자열을 중간에 넣기도 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 쉬운 해결방법은 업로드한 파일 이름의 뒤에서부터 검사하고, white-list 방식을 사용하는 것이다. 다른 방법은 파일 이름을 저장할 때 내부 저장값으로 인코딩/디코딩 하는 것이다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot;&gt;쿼리 최적화&lt;/h4&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333;&quot; data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;어떤 쿼리를 최적화했으며, 어떻게 바꿨고, 왜 그렇게 바꿨고, 왜 그렇게 생각했는지.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://hyelie.tistory.com/entry/Troubleshooting-%EC%BF%BC%EB%A6%AC-%EC%B5%9C%EC%A0%81%ED%99%94-Subquery-JOIN&quot;&gt;쿼리 최적화 포스팅&lt;/a&gt;에 있는 내용대로 말했다. 일단 실행계획 봤더니 nested loop으로 도는 것 확인해서 nested for loop 3중이라서 오래 걸렸고, 때문에 join으로 바꿨다고 말했다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;왜 join을 선택했냐는 질문에는 일단 nested loop를 없애야 한다고 생각했고, join 이후 필터링한 값을 사용하면 불필요한 반복문이 생기지 않고, + table size가 작아 join overhead가 크지 않을 것이라 생각했다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;시간복잡도는 제껴두고, nested for loop의 경우 A join B면 A + AB이고, hash join같은 경우는 A + B로 처리된다. 3중이니까 A join B join C면 A + A(B+BC)니까 A + AB + ABC인데, join이면 A+B+C로 처리된다. 이런 page 개념으로 시간복잡도를 설명.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;쿼리 옵티마이저가 어떤 방식으로 동작하는지도 물어봤다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;DB 정규화&lt;/h4&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333; text-align: start;&quot; data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 77px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;접수번호&lt;/td&gt;
&lt;td&gt;주민번호&lt;/td&gt;
&lt;td&gt;종류&lt;/td&gt;
&lt;td&gt;상해등급&lt;/td&gt;
&lt;td&gt;민원등급&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;******-*******&lt;/td&gt;
&lt;td&gt;민원&lt;/td&gt;
&lt;td&gt;null&lt;/td&gt;
&lt;td&gt;3급&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;******-*******&lt;/td&gt;
&lt;td&gt;의무조사&lt;/td&gt;
&lt;td&gt;1급&lt;/td&gt;
&lt;td&gt;null&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;******-*******&lt;/td&gt;
&lt;td&gt;민원&lt;/td&gt;
&lt;td&gt;null&lt;/td&gt;
&lt;td&gt;4급&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;DB 정규화 &amp;amp; 리팩토링&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;요구사항 : 보통전공상심사관리체계 - 기존 현역/공익 타입으로 입력을 받고 있었는데, 입력 페이지가 1개였다. 현역인 경우 상해등급에 값이 있고, 민원등급인 경우 민원등급에 값이 있다. 그러나 입력 페이지가 1개여서, 현역인 경우에는 민원등급에 값을 비워 쓰고, 민원등급인 경우 현역등급에 값을 비워 쓰고 있었다. 이게 불편하다는 요구사항이 있었다.&lt;/li&gt;
&lt;li&gt;정규화 한 이유 : &lt;span style=&quot;background-color: #fafafa; color: #333333; text-align: start;&quot;&gt;종류가 상해등급/민원등급의 null을 결정.&lt;/span&gt; 또한 DB 컬럼이 매우 많고, 자주 사용하는 것들만 사용하기 때문에 overhead가 너무 클 것이라 생각. 실제로 로딩에 오래 걸리기도 하고.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 테이블 - 접수번호가 다른 것 모든 것을 결정.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 테이블은 0NF. null 값이 존재하기 때문. 그것만 없으면 모든 값이 원자값이고, key가 접수번호이기 떄문에 부분적 함수 종속이 없다. 그러나 이행적 함수 종속 (pk id -&amp;gt; 접수번호 -&amp;gt; 나머지) 가 있기 때문에 1NF.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 null값이면 1NF를 만족하는지 여부는 논쟁 중이긴 하지만, 나는 그렇게 생각한다. 여기서는 반면 접수번호로 종류, 종류로 상해등급의 null 여부를 알 수 있다. 반면 종류가 민원이냐, 의무조사에 따라 식별하지는 못하지만 null값이 되는 column이 있기 때문에 이를 고쳐야 한다고 생각했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;때문에 [접수번호, 종류, 주민번호]와 같이 공통된 부분을&amp;nbsp;묶고, 나머지를 따로 떼서 다른 테이블을 만들었다. vertical partitioning의 방법을 사용해 anomaly를 없앤 것이라 보면 될 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;id를 long으로 두는 이유는, 비즈니스 정보를 id로 두는 경우 해당 정보가 수정될 수 있는 상황이 올 수도 있기 때문. 또한 long으로 두면 auto_increment로 관리하기 쉽고, id는 숫자이기 때문에 문자보다 비교 속도도 더 빠르기 때문.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;리팩토링&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;정규화와 이어지는 부분. DB가 바뀌었기에 repository 역할 하는 XML도 변경, XML 사용하는 service도 변경. controller에서는 기존 페이지 구조를 같게 해야 한다는 요구사항이 있어 controller는 하나로 둠.&lt;/p&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333; text-align: start;&quot; data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 URL로 두 종류의 값을 받아야 하는 것이 기본 요구사항.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. controller에서는 super DTO를 사용해 두 종류의 값을 모두 받는다. 아래와 같은 느낌이다. 여기서 if-else문을 strategy pattern으로 해결했다. return url도 같은 방식으로 map에 넣어서 해결.&lt;span style=&quot;background-color: #fafafa; color: #333333; text-align: start;&quot;&gt;&amp;nbsp;내부 들어가는 것에 대한 처리는 service 내부에서 해 줬다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. service는, 공통 양식 로직을 처리하는 DutyService, 이를 상속하는 ActiveDutyService와 ReserveDutyService 2개로 나누어, 세부 구현을 맡겼다. template method pattern이다. 변하지 않는 공통 부분은 묶고, 변하는(type에 따라 다른) 부분만 위임해서 해당 repository를 호출하는 식으로.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. template method는 abstract class와 inheritance와 override를 사용하는 방식, strategy는 interface와 polymorphism을 사용하는 방식이다. strategy pattern은 동적으로 type을 선택하기 위함, template method는 공통 부분을 사용하기 위함. 여기서는 abstract class를 사용하긴 했지만, polymorphism을 썼다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;공통 부분을 가져오는 로직이 있는데, interface를 쓰면 이 부분이 중복되기 때문에 abstract class를 썼고 - 따라서 template method가 적용된 것이고, 각 type별로 넣는 부분이 있는데, 이 부분은 type별로 따로 써야 하니까 processDuty()를 override하는 방식으로 - 따라서 template method가 적용된 것이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;strategy pattern은 type을 동적으로 가져오기 위해 사용했다. - controller에서 쓰인 것. controller가 service를 호출하는 것이 역할이라고 생각해서 service 내부에서 service 호출하지 않고, controller에서 service 호출하게 함.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;pre id=&quot;code_1716015451046&quot; class=&quot;reasonml&quot; style=&quot;text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;@PostMapping(&quot;/processPerson&quot;)
public ResponseEntity&amp;lt;String&amp;gt; processPerson(@RequestBody PersonDTO personDTO) {
    if (&quot;ActiveDuty&quot;.equals(personDTO.getType())) {
        activeDutyService.processDuty((ActiveDutyDTO) personDTO);
    } else if (&quot;ReserveDuty&quot;.equals(personDTO.getType())) {
        reserveDutyService.processDuty((ReserveDutyDTO) personDTO);
    }
    return ResponseEntity.ok(&quot;Input processed successfully&quot;);
}

@RestController
public class YourController {
    private final Map&amp;lt;String, DutyProcessingService&amp;gt; dutyProcessingMap;
 
    public YourController() {
        dutyProcessingMap = new HashMap&amp;lt;&amp;gt;();
        dutyProcessingMap.put(&quot;ActiveDuty&quot;, new ActiveDutyProcessingService());
        dutyProcessingMap.put(&quot;ReserveDuty&quot;, new ReserveDutyProcessingService());
    }
 
    @PostMapping(&quot;/processPerson&quot;)
    public ModelAndView processPerson(@RequestBody PersonDTO personDTO) {
        DutyProcessingService dutyProcessingService = dutyProcessingMap.get(personDTO.getType());
        if (dutyProcessingService != null) {
            PersonDTO resultPersonDTO = dutyProcessingService.processDuty(personDTO);
            ModelAndView modelAndView = new ModelAndView();
            modelAndView.setViewName(&quot;/.../&quot; + dutyProcessingService.getViewName());
            modelAndView.addObject(&quot;person&quot;, resultPersonDTO);
            return modelAndView;
        } else {
            // 유효한 유형이 아닌 경우 처리
            return new ModelAndView(&quot;error&quot;); // 에러 페이지로 리다이렉트 또는 에러 처리를 할 수 있습니다.
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;위 코드는 접은글 1번의 코드.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;소마&lt;/h3&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333;&quot; data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;프로젝트 : 법인의 임직원들이 복지 용도로 사용할 수 있는 통합 공유킥보드 플랫폼&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;무슨 프로젝트를 했고 어떤 과정이 있었는지, 어떤 기술을 썼는지, 쓰면서 힘든 점은 없었는지, 팀 빌딩은 어떻게 했고 주제는 어떻게 골랐는지 : 소마 회고록에 있는 내용 거의 다 말했던 것 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CRUD 말고 API 몇개 정도 만들었는지 : 통계 3개, 계약, 사용 CRUD정도 말했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;기술 선택은 어떻게 했고 jenkins pipeline은 어떻게 구성했는지, 막힌 점은 없었는지, yaml 파일 쓰면서 어려운 건 없었는지 : hook 따오고 gradle build, gradle test, sonarqube 분석, gcr image push, clean까지 했었다고 답변했다. CI를 하지 않은 이유는, push를 한 시점 == 배포 시점이 되면 안될 것 같아서 배포를 위해서는 저장한 image를 GKE에서 명령어 쳐서 배포하는 방식으로 했다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;왜 GCP 썼는지 : 익숙해서 사용했다고 했다. 학교 딥러닝 프로젝트 하면서 그래픽카드가 필요했는데, 2019년도즘? GCP에서 GPU 포함 30만원 정도 크레딧을 줬다. (작년까지도 준 것으로 안다) 그래서 그때부터 잘 사용했다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;redis는 왜 썼는지 - login token을 jwt로 구현했는데, logout 검증하려고 사용했다. 토큰 기반 인증 사용한 이유는 pod 여러 개 두려고 사용했다고 했다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;추가 질문 - redis 날아가면 어떻게 처리할 건지 물어봤다.&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;고민해보지 못한 점. 원했던 건 분산처리나 백업이었던 것 같다. replica 만들던가, dual로 동작시키던가 하는 백업 방안을 원했던 것 같다. 그리고 나는 access token, refresh token 모두 redis에 뒀는데 refresh는 DB에 둬야할 것 같다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;k8s 사용하면서 어땠는지 - 배포 알아서 해주고 로드밸런싱 해주니까 좋았다고 답변했다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;사실상 새로운 기술을 사용하면서 느낀 점은 많이 편하다...가 거의 대부분이었는데, 더 질문이 들어와서 좀 어려웠다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot;&gt;Bizkicks에서 JWT/redis&lt;/h4&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333;&quot; data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;bizkicks에서는 access token + refresh token을 사용했다. redis를 사용한 이유는 scale out을 위해서인데 해당 부분은 구현하지 않았다. refresh token만 redis에 넣는다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;login 요청이 오면 access token + refresh token을 돌려준다.&lt;/li&gt;
&lt;li&gt;access token + refresh token으로 reissue한다.&lt;/li&gt;
&lt;li&gt;모든 접근에는 access token만 사용한다. 만약 만료될 경우 refresh token을 보내서 reissue한다. 만약 틀리다면 작동 안되는 식이다.&lt;/li&gt;
&lt;li&gt;refresh token은 redis에 들어가 있다. refresh token을 받아서 reissue하는 방식이기 때문에 자주 불릴 것이라 예측했고, 따라서 빠른 in memory DB인 redis에 넣었다. 또 만료 시간을 DB가 관리해 준다는 장점도 있었다. (DB의 부하 감소)&lt;/li&gt;
&lt;li&gt;refresh token이 없다는 것은 로그아웃된 상태임을 시사한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;meerkat에서는 access token만 사용했다. token 인증 방식이다.&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;redis의 사용 이유는&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;in memory DB라 속도가 빠르며, 사용자 token이라는 간단한 정보만 넣기 때문에 key-value store가 적당하다. 그리고 시간도 알아서 관리해 준다.&lt;/li&gt;
&lt;li&gt;정보가 많다.&lt;/li&gt;
&lt;li&gt;이후 확장할 때 pub/sub 구조 등을 활용해서 jwt 처리하기가 다른 DB보다 쉽다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단점&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;in memory DB이기 때문에 날아갈 수 있다. 그러면 reissue 로직에서 redis에 있는 값을 보는데, 없기 떄문에 로그아웃 처리가 된 것으로 했다. 따라서 access token의 만료 시간인 15분에&amp;nbsp;한 번씩 로그인해 주어야 한다는 단점이 있다. memory DB이기 때문에 불안정하다는 단점은 replica를 만들던가, dual로 동작시키던가 하는 방법이 있을 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가적으로 refresh token 관리&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 코드에서 redis에 access token, refresh token 둘 다 넣었는데 redis가 날아가는 상황을 고려하지 않았다. 좀 더 안정적인 서비스를 위해서는 refresh token을 DB에 넣는 것이 더 좋을 것 같다. 어차피 refresh token은 access token이 만료되는 상황에서만 불러지니까 그렇게 load도 심하지 않을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;탈취&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;access token 탈취 시 해킹에 의한 피해를 줄이기 위해 지소시간이 짧은 access token을 사용한다. 그러나 refresh token이 탈취당하는 경우에는 access token을 재발급할 수 있으므로 피해가 커질 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 해결하기 위해 refresh token rotation 기법을 사용하는데, access token이 만료되어 refresh받을 때 refresh token도 재발급하는 방법이다. 그러나 이 방법의 경우 해커가 먼저 refresh를 하는 경우에 문제가 발생한다. 때문에 DB에 저장할 때 user id : refresh token 이런 식으로 저장해서 refresh token이 단 하나만 존재할 수 있게 한다. 이후, refresh 요청을 보냈을 때 refresh token이 저장된 것과 다르다면 해킹 시도로 간주하고 로그아웃시키는 방법이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- 이 경우 해커가 refresh token을 탈취했을 때, 사용자보다 늦게 로그인하면 로그아웃 처리가 되고, 사용자보다 빨리 로그인하면 사용자가 로그인했을 때 로그아웃 처리가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- 그러나 사용자가 로그인하지 않는 경우에는 해커는 사용자 정보로 계속 활동할 수 있게 된다. 그러나 이는 토큰 방식의 필연적인 한계라 생각한다. 해결 방법으로는 refresh token의 유효기간을 두는 것. (로그인 시 7일 이후에는 새로 로그인해야 하는 등)&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;미어캣&lt;/h3&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333;&quot; data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;미어캣 : E2EE를 적용한 안전한 실시간 채팅 앱&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;누구랑 어떤 프로젝트 했는지, 왜 만들었는지, 기술 선택 이유는 무엇인지 - mariadb 쓴 이유는 뭔지, orm은 왜 썼는지, 어디까지 구현했는지, 채팅 어떻게 구현되었는지&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;ORM 얘기. 이거 좀 많이 했다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ORM을 왜 쓰는지 ? 확장성이나 유지보수성 + DB가 바뀌었을 때 쉽게 대처할 수 있다고 했다. 답변으로는 실서버에서는 DB를 바꾸는 일이 극히 드물다. 그래서 사실 DB를 바꿀 수 있어서에서 오는 장점은 거의 없고, 읽기 쉽고, 테이블이 단순해지고, query를 직접 안 쓰니까 코드 레벨에만 더 집중할 수 있다가 맞는 대답인 것 같다. 혹시나 DB를 바꾸는 상황이 되었을 때도 의존성을 줄일 수 있을 것이다.&lt;/li&gt;
&lt;li&gt;ORM을 왜 적용했는지 : 나는 시간 없어서 안쓰는 게 맞다고 했는데 다른 팀원들이 꼭 써야 한다고 해서 수긍했다고 했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;git 전략: rebase&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;docker 사용하면서 어땠는지 - 편하다고 했다. 살짝 마음에 안 든 것 같긴 하다. 추가로 이후로 모든 db나 환경 docker에 올렸다고 대답했더니 db도 올려 쓴거냐고 물어봐서 서비스당 각 db에 해당하는 걸로 올려썼다고 했다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;백만 했냐고 물어봤다 : 시간이 없어서 react로 친구 페이지, 채팅방 목록 부분을 내가 만들었다고 했다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;인증은 어떻게 했는지 : token으로 관리한다고 했다. + 모든 socket에 token을 넣어서 관리하는 식으로 했다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;E2EE?&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;비대칭키 암호화 : 공개키로 암호화 한 것은 개인키로만 복호화 가능. 그 역도 마찬가지. 내부적으로는 소인수분해 사용하는 RSA 쓰는 것으로 알고 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;암호를 만들 때 두 소수의 곱으로 만드는데, 이 때 사용한 두 소수를 찾기 힘들다. 숫자가 n일 때 n 아래의 소수를 찾는 데 nloglogn, 이는 모두 있다고 해도 O(n)이 걸림.&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;암호화&lt;/h4&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RSA 원리 &lt;br /&gt;디피헬만 간단히&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;salt&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333;&quot; data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소인수분해 문제 : RSA&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이산대수 문제 : 디피헬만&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RSA&lt;br /&gt;큰 정수의 소인수분해가 어렵다는 점을 활용. 비대칭키 방식. 공개키를 사용해 암호화/개인키를 사용해 복호화 또는 개인키를 사용해 암호화/공개키를 사용해 복호화 가능&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개인키로 서명해서 송신자가 인증한 것을 알 수 있음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공개키 암호화 공격 시나리오&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. A, B가 메시지를 교환할 때, 해커가 B의 공개키를 탈취해 A에게 C의 공개키를 보냄.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. A는 C의 공개키로 평문을 암호화해 B에게 보냄&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. C는 C의 개인키로 복호화하면 A의 평문을 습득할 수 있고, 이를 B의 공개키로 암호화해 B에게 보냄&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. B는 평문을 얻지만, 이미 탈취당함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;디피헬만&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대칭키 교환 시 사용. 키 값을 전달하는 것이 아니라 키 값을 만드는 방법을 전달함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일방향&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MD5&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SHA256 : 충돌 걱정을 안 해도 괜찮은 해싱 방법&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;salt&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비밀번호 암호화 시 평문을 그대로 저장하거나, 복호화할 수 있는 방법을 사용하지 않고 해싱으로 복호화 불가능하게 저장. 이 때 salt를 추가로 평문에 붙여서 해시 결과를 알 수 없게 하는 것.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;실시간 채팅&lt;/h4&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;채팅 구현 내용 : 채팅방에 속해있는 사람들은 어떻게 구현했는지, 실시간 채팅인지, socket 썼는지. 있는 그대로 얘기했다. &lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://github.com/osamhack2022/APP_Meerkat_IQDan/wiki/%EC%8B%A4%EC%8B%9C%EA%B0%84-%EC%B1%84%ED%8C%85&quot;&gt;자료&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;클라이언트에 2개의 socket 연결을 이용하는데, 하나는 실시간 메시징을 위해서이고 나머지 하나는 메시지 수신에 대한 알림을 위해서. 지금 생각하기로는 채팅방 목록에서 알림 오는 거는 클라가 요청을 보내지 않고, 응답을 받기만 하면 되므로 SSE 같은 걸 쓰면 좋았을 것 같다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;모든 사람이 자신이 속해있는 모든 socket room에 join하고, 메시지 보내거나 받는 event 발생 시 read event를 발생해 최신의 읽은 메시지를 갱신하고, DB에는 모든 사람이 어떤 방에 속했는지, 각 방에서 채팅내역을 모두 보낸다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;467&quot; data-origin-height=&quot;132&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/q4kiN/btsHsTlP8xP/K0pkD741mFuM06IjqEowZ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/q4kiN/btsHsTlP8xP/K0pkD741mFuM06IjqEowZ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/q4kiN/btsHsTlP8xP/K0pkD741mFuM06IjqEowZ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fq4kiN%2FbtsHsTlP8xP%2FK0pkD741mFuM06IjqEowZ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;467&quot; height=&quot;132&quot; data-origin-width=&quot;467&quot; data-origin-height=&quot;132&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그래서 실시간이고, E2EE 적용한 내용을 말했다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;그러면 사람마다 안 읽은 메시지 개수가 다를 수도 있지 않나? 음.. 그런 경우가 있나요? DB에서 메시지 관리하고, socket도 관리하는데, dual로 관리하는데 그런 일은 안 생길 것 같다고 답했다. 서버가 터지지 않는 이상!&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSE&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;polling : 클라가 서버로 일정 시간마다 데이터 요청을 보내는 방식.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;long polling : 클라가 요청을 보내고, 서버에 event 발생 시 클라에게 응답하는 방식. connection이 유지되어 있어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;websocket : 실시간 연결, event 방식&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;websocket과 유사, client가 server로부터 데이터를 받을 수만 있음.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;N2T&lt;/h3&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333;&quot; data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;naver to tistory 이사 프로그램&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;왜 구현했는지 : 왜 구현했는지, 어떻게 구현했는지, 왜 java 썼는지에 대해 질문받았다. 있는 그대로 답했다. 필요해서 만들었고, java 공부하고자 썼다고 대답했고, 왜 CLI 썼냐는 질문에는 front 잘 못해서 그냥 cli로 깔끔하게 처리하는 게 좋을 것 같아서 그랬다고 했다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;추가 질문으로는 text만 긁어서 올린거냐고 물어봤는데, style 등 전부 다 파싱해서 올렸다고 했다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;비공개 포스트는 어떻게? 이거 하려면 사용자에게 id/pw 요구해야 하는데, 너무 사짜같아서 비공개는 배제했다. 티스토리에 올리는 건 token만 있으면 되기 때문에 이건 괜찮을 거라 생각했다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;필요없는 주석들이 너무 많았다.&lt;/li&gt;
&lt;li&gt;try-catch문을 너무 많이 사용해 가독성이 떨어진다는 느낌을 받았다.&lt;/li&gt;
&lt;li&gt;concrete class에 의존한다. 때문에 확장성이 없다시피 했으며 유지보수도 힘들었다.&lt;/li&gt;
&lt;li&gt;if-else문으로 대부분의 로직을 처리한다.&lt;/li&gt;
&lt;li&gt;테스트 코드가 없다. 때문에 소스코드를 수정한 후 검증하는 과정이 오래 걸린다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; background-color: #ffffff; color: #000000; text-align: left;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li style=&quot;list-style-type: decimal;&quot;&gt;주석 삭제. 의도를 설명한 주석, 유지보수 시 참고가 될 만한 주석만 남기고 모두 삭제. JavaDoc은 필요한 public 함수에만 남기고자 했다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: decimal;&quot;&gt;예외처리를 간소하게. 곳곳에 퍼져 있는 try-catch문을 응집시키고자 했다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: decimal;&quot;&gt;최대한 의존성을 줄이고&lt;b&gt;&amp;nbsp;확장성 있게&lt;/b&gt;&amp;nbsp;코드를 작성하고자 했다&lt;/li&gt;
&lt;li style=&quot;list-style-type: decimal;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;테스트 코드 작성&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot;&gt;N2T 리팩토링&lt;/h4&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333;&quot; data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fafafa; color: #333333; text-align: start;&quot;&gt;&amp;nbsp;추후에 리팩토링했다는 이야기도 했다. 처음에는 if-else문이 너무 많게 구현했는데, 공부하다가 확장성 이슈를 크게 느껴서 implement로 구현했다고 했다. 그래서 기능 추가나, 목적지 블로그 추가해도 전체 구조는 동일하게 된다고 어필했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;http://hyelie.tistory.com/entry/N2T-리팩토링-기록&quot;&gt;N2T 리팩토링&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;EME&lt;/h3&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333;&quot; data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;intellij 마크다운 wysiwyg editor. 편집할 때 실제 렌더링된 결과를 보여줌. (like obsidian)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;easy markdown editor&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fafafa; color: #333333; text-align: start;&quot;&gt;해당 프로젝트 : agile을 배우는 게 주 목적이어서 주간 회의, story point 할당, 배분, 구현 같은 것을 했다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fafafa; color: #333333; text-align: start;&quot;&gt;intellij에서 마크다운 에디터를 구현하는 것이 목적. 기존 default로는 미리보기를 위해서는 화면이 반반 나뉘며 공간을 많이 차지한다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #fafafa; color: #333333; text-align: start;&quot;&gt;기존에 플러그인은 있지만, 문제점이 많았다. 탭 전환할 때 무한루프에 걸리는 건지 모르겠는데, 전환이 안 되었다. 컴퓨터 성능 모니터링 결과 성능을 크게 잡아먹는 것 같지는 않았지만, 몇몇 컴퓨터에서 이러한 문제가 발생했고 사용할 수 없는 문제가 있어 이를 해결하고자 했다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fafafa; color: #333333; text-align: start;&quot;&gt;주제 냈음,&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;초기 컨벤션 잡고, 전체적인 클래스 구성도 및 구현 방식 디자인, 로직 설계/작성, 테스트코드 작성, 등등 모든 부분에 관여함. 그런데 나만 이렇게 한 건 아니고 모든 팀원이 다 적극적으로 참여했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용한 패턴 : observer pattern, template pattern&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설명할 수 있을 정도로 준비&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1313&quot; data-origin-height=&quot;1041&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NiGpS/btsHntTgq9q/5SHdkSqU3VBkkjccIELRM0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NiGpS/btsHntTgq9q/5SHdkSqU3VBkkjccIELRM0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NiGpS/btsHntTgq9q/5SHdkSqU3VBkkjccIELRM0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNiGpS%2FbtsHntTgq9q%2F5SHdkSqU3VBkkjccIELRM0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;408&quot; height=&quot;323&quot; data-origin-width=&quot;1313&quot; data-origin-height=&quot;1041&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;전체 구조도. 각 block들이 blockmanager에 notify하면 blockmanager 내부에서 markdownEditor의 updateUI()를 호출하는 식으로 구현. 내부적으로는 각 block에서 마우스 클릭 이벤트 / 키보드 클릭 이벤트를 listen하고 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1111&quot; data-origin-height=&quot;879&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bmz95A/btsHmqDz4n3/hR1QpuPyUuYSzOVJ467JR0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bmz95A/btsHmqDz4n3/hR1QpuPyUuYSzOVJ467JR0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bmz95A/btsHmqDz4n3/hR1QpuPyUuYSzOVJ467JR0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbmz95A%2FbtsHmqDz4n3%2FhR1QpuPyUuYSzOVJ467JR0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;461&quot; height=&quot;365&quot; data-origin-width=&quot;1111&quot; data-origin-height=&quot;879&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;클래스 구조도. block은 abstract이고, 한 줄짜리로 처리 가능한 heading/horizontal line 같은 것을 처리하는 게 single line block. quote/codeblock/ul/ol 등을 처리하는 게 multiline block.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 마크다운을 여러 개의 block으로 나누고, focus된 block들만 markdown raw text로, 나머지는 html 렌더링. focus된 block에서 뭔가 이벤트가 발생한 경우, 각 케이스에 대해 처리를 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 다른 block이 클릭되는 경우 outfocus_click 이벤트를 날려 focus block을 변경&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 제일 상단에서 키보드 위 화살표나 제일 하단에서 키보드 아래 화살표가 눌리는 경우 outfocus_up, outfocus_down 이벤트를 날려 focus block 변경.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 제일 뒤에서 엔터를 누르는 경우 new_block 이벤트를 날려 새 블록 생성 후 포커스 변경&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 제일 앞에서 delete를 누르는 경우 해당 블럭을 이전 block과 merge&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 중간에서 엔터를 누르는 경우 내용을 판단해 transform_multi나 transform_single 이벤트를 날려 적당한 block으로 변환&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GUI 빼고 line coverage 90% 달성. 학점도 A+ 받음.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;인성면접&lt;/h2&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;개발자 선택 동기 : 대학원(AI, 시스템, 이론), 개발 시도하면서 개발동아리 하나 했는데, 하면서 적성에 잘 맞았다. 처음에는 주어진 API를 짜는 것 자체에서 재미를 느꼈는데, 하면 할수록 잘 만드는 것(요구사항을 잘 분리하고, 구조를 잘 짜고, 하는 것들)이 적성에 맞다고 느꼈다. 이후 소마, SW개발병 하면서 확신 들어서 결정.&lt;/li&gt;
&lt;li&gt;어떤 게 좋은 코드일까? :
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;함수는 - 잘 읽히고, 수정하기 좋은 코드. 보편적인 가독성 좋은 코드란? 너무 길지도, 너무 함축적이지 않은 변수명을 사용하면서 + 함수에 parameter도 너무 많지 않으면서 (많은 경우에는 객체 하나 써서 wrapping + 적절한 이름으로 네이밍) + 하나의 로직 내에 너무 깊은 depth가 없으면서, (많은 분기문&amp;amp;try-catch가 없으면서, 있더라도 depth 1칸, 최대 2칸 정도) + 다른 method를 호출할 때 method 이름도 너무 길지도, 너무 함축적이지 않은 이름을 사용하는 함수 + 너무 가로로 길지도, 너무 세로로 길지도 않으면서 + 각 문단이 잘 구분되어있는 함수가 좋은 코드라 생각함.&lt;/li&gt;
&lt;li&gt;전체적인 구조로는 - 전체적인 depth가 너무 깊지 않으면서 + 공통 모듈을 제외하고는 하나의 메소드가 너무 다양한 도메인에서 불리지 않으면서 (유지보수하기 힘듬) + 레이어의 구분이 잘 되어 있는? 구조가 좋은 코드라고 생각한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;레이어 : 도메인형 구조는 도메인 하나에서 사용하는 각각의 controller, service, repo, entity, dto 구분.&amp;nbsp; 그러나 도메인이 섞이는 경우 복잡. (join 등) layered는 layer별로 구분. 그러나 파일 많아지는 경우에 복잡.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;가장 어려웠던 경험(벽 느낀 경험) : 현실적으로는 비즈킥스 프로젝트 진행하면서. 코드 짜면서는 아키텍처.&lt;/li&gt;
&lt;li&gt;기억나는 수업 : OR, 객체지향, 자료구조, 아키, DB, 알고리즘&lt;/li&gt;
&lt;li&gt;자신의 장단점 :&amp;nbsp;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;집념? &lt;span style=&quot;background-color: #fafafa; color: #333333; text-align: left;&quot;&gt;목표 달성에 있어 집념있는 편. 어떤 일을 진행할 때, 해당 일의 큰 데드라인 + 자잘하게 일을 나눠서 목표를 설정한다. 목표 자체에 2일 정도의 여유 기간을 두긴 하지만 목표 자체를 달성하는 데 집념이 있다. 예를 들어 제일 힘들었던 과제인 pipelined CPU 구현 같은 경우, pair programming 했는데, 정말 잠만 자고 코딩만 했다. pair 끝나고 나서도 혼자서 추가로 구현하기도 했다.&lt;span&gt; 여러 일들을 겪으면서 &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #fafafa; color: #333333; text-align: left;&quot;&gt;최근에는 목표를 타이트하게 설정하고 스트레스 받아가면서 작업하니 남는 시간에 반동으로 더 휴식을 추구하게 되었다. 그래서 조금의 여유를 두고 지속할 수 있을 정도의 부담감만 느끼는 수준으로 목표를 설정하는 쪽으로 유연하게 변경 중이다.&lt;/span&gt;&lt;span style=&quot;background-color: #fafafa; color: #333333; text-align: left;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;한편, 이 집념이 내가 맡은 어떤 일과 목표의 완수, 즉 성장과 향상심에 대한 집념이지, 의사소통에 있어 고집이 세다 이런 쪽의 집념은 아니다. 오히려 이러한 의사소통이나 충돌 같은 것에 있어서는 정말 아니다로 생각하는 것을 제외하고는 대부분 접고 들어가는 편. 대표적으로 프로젝트 기술 스택 정했을 때 팀메이트는 orm 써야 한다였는데, 나는 아니었다고 한 것. 정말 아니다라 생각하는 것 중 하나는 컨벤션. 컨벤션이라는 게 결국 팀메이트들과의 약속인데, 이를 어긴다는 건 결국 사회적 약속을 깨는 거라 생각한다. 혼자 형식 안 맞는 것도 좀 그렇고. 다른 하나는 마무리 제대로 안 하는 것? 플젝 끝냈거나 어느 정도 완성되었다면 나머지 디테일을 챙겨야 한다고 생각한다. readme나 문서, 주석 정리 등등. 이런 것들은 오히려 좋아.. 내 맘대로 할 수 있어서&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;단 : 몰입할 때는 100% 집중하는 것 같은데, 듀데이트 등 심리적 방해요소가 있으면 잘 못한다. - 때문에 항상 시간관리를 열심히 한다. + 잠이 많다. - 8-9시간은 자야 한다고 생각하는 사람인데, 때문에 깨어있는 시간과 계획 관리를 철저하게 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;의견 충돌 시 어떻게 해결할 건지 : 결국 의견 충돌이 나는 이유는, 현재 상황과, 특정 해결책의 장단점가지고 논쟁인데, 평행선을 이루는 이유는 확실한 근거가 부족해서라고 생각한다. 이런 상황에서는 진짜 논쟁을 하면서, 각자가 생각하고 있는 edge case에 대한 각각의 해결책들의 handling이 충분한지를 논의해보면서 더 좋은 방안을 채용할 것 같다. 만약, 그래도 해결되지 않는다면 다른 case들을 좀 더 고민해 보고, 최후의 최후에는 팀 내에서 다수결을 해야 할 것 같다. 그러나&amp;nbsp;다 잘하는 사람들끼리 모인 만큼 다들 비슷한 의견을 낼 것 같아서 그럴 일은 없을 것 같다. 그리고 최근에는 수긍을 많이 해서...&amp;nbsp;&lt;/li&gt;
&lt;li&gt;어떤&amp;nbsp;개발자가&amp;nbsp;되고&amp;nbsp;싶은지&lt;/li&gt;
&lt;li&gt;회사에 대해 궁금한 점 : 테스트코드 쓰는 문화가 어떻게 되어 있는지, 코드리뷰 문화는 어떤지, 실제로 출근하는 분들이 많은지?&lt;/li&gt;
&lt;li&gt;스트레스 관리법 : 잔다. 물론 낮시간이거나, 너무 급박한 일이라면 그렇지 못하겠지만, 어떻게든 충분한 수면 시간은 마련할 것 같다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;취미(여가) : 운동하거나 웹소설. 시간 좀 많이 남으면 게임하고. 유튜브 좀 본다. 쇼츠같은 건 아니고, 게임 영상이나 개발 영상 위주로 본다.&lt;/li&gt;
&lt;li&gt;갈등관리 경험 (치어로, 포카풀) : 포카풀 일정관리 경험&lt;/li&gt;
&lt;li&gt;회사에 들어와서 어떻게 성장하고 싶은지 : 최종적으로는 팀을 편하게 할 수 있는 만능 6각형 개발자로 성장하고 싶다. 일단은 각 능력치들을 조금씩 키우기 위해서 기존 코드가 어떻게 돌아가고 - 특히 트래픽 같은 것을 어떻게 처리하는지 공부를 하면서 작은 걸로 팀에 기여하고, 차근차근 기여할 수 있는 부분을 늘일 것.&lt;/li&gt;
&lt;li&gt;원하지 않는 업무를 맡으면 어떻게 할지 : 백엔드가 아니라 아예 다른 작업을 시킨다면 고민을 많이 해보겠지만, 백엔드 메인 + 다른 일을 해야 하는 상황(어드민툴, 인프라 파이프라인 등)이라면 할 것 같다. 이런 일들은 원치 않는 업무라기보다는 해야 하는 일이라고 생각하기 때문. 나의 희망은 사용자와 가까운 도메인이지만, 처음부터 이런 부서에 배치를 받으면 나의 희망사항이어서 좋고, 사용자와 먼 도메인의 경우 오히려 이런저런 새로운 시도를 많이 해 볼 수 있을 것 같아서 오히려 좋다. 사용자와 가까울수록 코드 수정이 보수적으로 될 수 밖에 없다고 생각하기 때문.&lt;/li&gt;
&lt;li&gt;나를 왜 뽑아야 하는지 : 팀에 기여할 수 있기 때문. 작게는 업무 프로세스 개선에서 - 크게는 코드 작성까지, 부족한 부분을 채우고&amp;nbsp;&lt;/li&gt;
&lt;li&gt;집념? : 원하는 거 있으면 목표를 달성할 때까지 하나만 판다. 그 과정이 조금 힘들더라도? ex) 과제할 때 2주동안 그거만 한다던가, 미어캣 프로젝트 때 프로젝트 완결 &amp;amp; 수상이라는 목표를 위해서 원래 맡기로 했던 역할을 넘어 팀에 기여하는 방향을 선택한다던가. (나 개인의 목표. 팀의 목표는 언제나 변할 수 있다고 생각한다.)&lt;/li&gt;
&lt;li&gt;마감일 vs 퀄리티 : 퀄리티가 먼저라고 생각함. 일반적으로 마감일이라는 게 급박하게 주어지는 게 아니고, 결국 나도 처음에 동의한 내용이기 때문에 마감일을 맞추지 못하는 경우에는 삽질 등에 의해 지체되는 경우가 대부분일 것. 이 경우에, 중간중간 이러한 이유로 지체되고 있다는 것을 알리는 것이 먼저라고 생각. 프로젝트를 런칭했을 때 버그가 터져 사용자들이 불편함을 겪어 이탈하는 것과, 지연되어서 이탈하는 것을 비교하면 후자가 훨씬 나을 거라 생각. (물론 지연도 너무 많이 되면 안되겠지만)&lt;/li&gt;
&lt;li&gt;일하고 싶은 도메인 : 가능하다면 트래픽을 많이 다루고, 시니어들에게 많은 것을 배울 수 있는 부서. 도메인은 음.. 이번에 네이버/네이버 클라우드/네이버랩스/네이버페이 이렇게 있는 걸로 아는데. 사용자와 가까운 도메인에 있고 싶다. 네이버나 네이버페이쪽?&lt;/li&gt;
&lt;li&gt;같이 일하고 싶은 / 일하기 싫은 스타일 : 편하게 질문할 수 있는 사람.&amp;nbsp;어려움을 공유하고, 같이 고민하는 과정에서 더 빨리 문제를 해결할 수 있을 것이다. 일하기 싫은 스타일은 너무 퉁명한 경우.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;평상시에 어떻게 공부하는지 : 기술에 대한 agenda는 유튜브 알고리즘에 뜨는 신규 기술들이나, 또는 간혹 기술 채널들에 나오는 것들을 본다. 아니면 프로그래머스 같은 데서 나오는 개발자들의 기술 통계 같은 것을 보고. 그 중에서 재밌어 보이는 것 - 내가 느꼈던 어려움을 해결할 수 있는 기술들에 대해 흥미를 가지고 공부한다. 최근에는 blue green 봤고, 이전에는 spring 이외에도 node.js, nestjs 등등 공부하기도 함.&lt;/li&gt;
&lt;li&gt;꿈 : 사람들에게 긍정적인 영향을 주는 사람. 그것이 개발 내적이던, 개발 외적이던, 내가 만든 서비스를 사용하는 사람들이던 간에 나로 인해서 다른 사람들이 성장하거나, 정서적으로 편안함을 느낀다던가 등 긍정적인 영향을 주었으면 좋겠다.&lt;/li&gt;
&lt;li&gt;마지막 한마디&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>내가 하고싶은 것!/취준</category>
      <author>hyelie</author>
      <guid isPermaLink="true">https://hyelie.tistory.com/653</guid>
      <comments>https://hyelie.tistory.com/entry/%EA%B8%B0%ED%83%80-%EB%A9%B4%EC%A0%91%EB%8C%80%EB%B9%84-%EC%A7%88%EB%AC%B8#entry653comment</comments>
      <pubDate>Wed, 4 Oct 2023 10:36:18 +0900</pubDate>
    </item>
    <item>
      <title>자료구조 면접대비 질문</title>
      <link>https://hyelie.tistory.com/entry/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-%EB%A9%B4%EC%A0%91%EB%8C%80%EB%B9%84-%EC%A7%88%EB%AC%B8-1</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;Hash Table&lt;/h3&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정의&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;collision 해결 방식&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;hash function&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 key가 주어졌을 때 hash function으로 매핑하고, 거기에 값을 저장하는 key-value store.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메모리에 쓰는 경우, 값이 유한하기 때문에 collision이 발생한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;separate chaining : 해당 bucket에 linked list 추가하는 방식. 쏠릴 수 있어 worst O(n)&lt;/li&gt;
&lt;li&gt;open addressing : 해당 위치가 아니라 빈 공간을 사용하는 방식. 예외가 많아 어렵다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;hash function은 임의의 길이 data를 고정 길이 data로 매핑하는 함수.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Quick Sort&lt;/h3&gt;
&lt;div data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pivot 기준으로 왼쪽에는 작은 수, 오른쪽에는 큰 수 둔다. pivot은 적당한 값을 고른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;in-memory sort라서 평균적 O(nlogn)&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;Array, Stack, Queue, Tree, Heap, Linked List, Graph, Set&lt;/h3&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333;&quot; data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특징&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333;&quot; data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;array&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메모리에 연속적으로 저장됨. index를 사용해 O(1)로 접근 가능.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;연속적으로 할당되기 때문에, 앞/뒤에 넣는 경우에는 새로운 위치를 찾아야 하고, 중간에 들어가는 경우는 모든 요소를 미뤄야 하므로 O(n).&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;stack&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;last in first out&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;queue&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;first in first out&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;heap&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;complete binary tree + parent가 child보다 항상 작음. (min heap 기준)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;tree&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;graph의 일종으로, edge 개수가 vertex 개수보다 1개 적은 것. connected &amp;amp; acycle &amp;amp; undirected graph&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;linked list&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 element가 이전/이후에 오는 element를 알고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;삽입/삭제에 O(1).&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검색하는 데는 앞에서부터 순회해야 하므로 O(n)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;memory cache를 사용하지 못하기 때문에 느리다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;graph&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;vertex, edge로 이루어진 자료구조. edge는 vertex를 잇는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- adjacent matrix : O(n * n), 판별에는 O(1)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- adjacent list : O(V+E), 판별에는 O(deg(v))&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;set&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중복 허용 X&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;binary tree 종류&lt;/h3&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333;&quot; data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;binary search tree&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fafafa; color: #333333; text-align: start;&quot;&gt;complete binary tree&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;balanced binary tree&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;perfect binary tree&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333;&quot; data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;binary search tree&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;검색 위해 사용, binary tree인데, 정렬된 것. parent보다 작은 것들이 왼쪽, 큰 것이 오른쪽에 온다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;worst case O(n)이라서, balanced binary search tree 등으로 O(logn)을 유지할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;complete binary tree&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막 level 제외하고는 모든 level이 완전히 채워짐.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- min heap의 경우 parent가 child보다 항상 작다. 삽입 시 제일 끝에 넣고 swap, 삭제 시 root로 올리고 비교하며 내림.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;balanced binary tree&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;left child와 right child의 height 차이가 최대 1. avl이나 rb tree.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- avl에서 삽입/삭제 시 회전한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- rb tree에서 색을 사용해 균형 유지. avl보다 조금 빠르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;perfect binary tree&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;leaf node 제외, 모두 2개의 child 가짐&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;Trie&lt;/h3&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333;&quot; data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정의&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333;&quot; data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열에서 검색 빠르게 도와줌.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열을 tree로 만든 것.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>내가 하고싶은 것!/취준</category>
      <author>hyelie</author>
      <guid isPermaLink="true">https://hyelie.tistory.com/652</guid>
      <comments>https://hyelie.tistory.com/entry/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-%EB%A9%B4%EC%A0%91%EB%8C%80%EB%B9%84-%EC%A7%88%EB%AC%B8-1#entry652comment</comments>
      <pubDate>Wed, 4 Oct 2023 10:05:58 +0900</pubDate>
    </item>
    <item>
      <title>네트워크 면접대비 질문</title>
      <link>https://hyelie.tistory.com/entry/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EB%A9%B4%EC%A0%91%EB%8C%80%EB%B9%84-%EC%A7%88%EB%AC%B8</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;URL vs URI&lt;/h3&gt;
&lt;div data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;URL : uniform resource locator, resource의 위치&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;URI : uniform resource identifier, resource의 식별자&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;HTTP&lt;/h3&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정의&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특징&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;http request message / response message에 들어가는 것들&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;hypertext transfer protocol의 약자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;client-server model, TCP 사용, stateless(상태 저장 x) 등의 특징이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;stateless를 해결하기 위해 cookie나 session을 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTTP request message에는 request, header, body가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;method는 GET, POST, PUT, PATCH, DELETE 등이 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;GET의 경우 http body가 없고 url에 모든 정보를 담아 보낸다.&lt;/li&gt;
&lt;li&gt;POST는 http body에 값을 담아 보내는 방식이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTTP response message에는 status line, header line, response body가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;status code는 응답 상태를 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;200번대는 성공, 300번대는 redirect, 400번대는 client 오류, 500번대는 서버 오류&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;DNS&lt;/h3&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정의&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용 이유&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DNS hierarchy&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;name resolution&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DNS는 domain name system의 약자로, host name과 ip address mapping을 저장하는 distributed, hierarchical DB이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;domain의 사용 이유 : IP address는 외우기 힘들기 때문.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 domain의 최상위에는 root domain이 있다. DNS server는 hierarchy가 있다. 각 DNS server는 자신이 가지고 있는 domain의 바로 아래에 있는 DNS server의 IP address를 가지고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;name resolution : domain으로 IP address를 얻어오는 과정. iteration query, recursive query 2가지 방식이 있다. 여기선 iteration query 방식.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;local DNS server에 request를 날린다.&lt;/li&gt;
&lt;li&gt;local DNS server는 root DNS server에 request를 날린다.&lt;/li&gt;
&lt;li&gt;root DNS server는 하위 domain에 해당하는 DNS server의 IP address를 가지고 있으며, 이를 response한다.&lt;/li&gt;
&lt;li&gt;local DNS server는 하위 domain에 해당하는 DNS server의 IP address를 얻게 되는데, 여기에 request를 날린다.&lt;/li&gt;
&lt;li&gt;... 이를 반복해 &lt;span style=&quot;text-align: left;&quot;&gt;local DNS server가 &lt;/span&gt;목적지 DNS server IP address를 얻게 된다. local DNS server가 해당 IP address를 response한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DNS server&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;IP address&lt;/h3&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;32bit로 표현하며 network 번호와 host 번호 2가지로 나뉜다. netmask로 network 번호, host 번호를 구분한다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;TCP&lt;/h3&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정의, 특징&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동작 3가지&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;transmission control protocol.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;reliable data transfer, flow control, congestion control 등 기능 지원.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP는 3단계가 있다. 접속, 송/수신, 끊기가 그 3가지이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;접속 동작 - 3 way handshake&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;client가 server에게 SYN bit, sequence 초기값, client window 3가지 값을 server에게 보낸다.&lt;/li&gt;
&lt;li&gt;server는 client에게 &lt;span style=&quot;text-align: left;&quot;&gt;SYN&lt;span&gt; bit&lt;/span&gt;&lt;/span&gt;, sequence 초기값, server window, ACK bit 4가지 값을 client에게 보낸다.&lt;/li&gt;
&lt;li&gt;client는 server에게 ACK bit를 server에게 보낸다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 SYN, ACK는 연결 동작에서 사용하는 bit이며, SYN은 연결 요청, ACK는 수신 응답이다. 이를 통해 session이 생성된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 sequence는 난수로 설정하는데 이전 연결에 사용한 sequence값과 혼동되지 않기 위함이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;송/수신 동작&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;sender가 sequence 번호 + data를 보낸다.&lt;/li&gt;
&lt;li&gt;receiver는 ACK 번호 + server window를 보낸다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;reliable data transfer : 한 번에 보낼 수 있는 packet size가 한정되어 있기 때문에 packet을 분할해야 한다. 때문에 sequence가 필요하다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;text-align: start;&quot;&gt;필요 시 data를 분할하고, sequence 값을 사용해 해당 data가 어디부터 시작되는지 분할한다. 이를 통해 data가 사라졌는지 검증할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 ACK 번호는 어디까지 받았는지에 대한 정보이다. 여러 개를 받은 경우, 최적화를 위해 제일 최신의 ACK 번호만 응답한다. (3 duplicated ACK)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;flow control &lt;/span&gt;: window는 buffer overflow를 막기 위해 사용하는 값이며 receiver가 몇 byte까지 받을 수 있는지에 대한 정보이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;congestion control&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AIMD : window를 1씩 늘이고 문제 발생 시 절반으로 줄임&lt;/li&gt;
&lt;li&gt;slow start : 매 전송마다 window를 2배로 늘이고 문제 발생 시 1로 내림.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;연결 끊기 동작 - 4 way handshake&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;client가 server에게 FIN bit를 보낸다.&lt;/li&gt;
&lt;li&gt;server가 client에게 ACK bit를 보낸다.&lt;/li&gt;
&lt;li&gt;server가 client에게 FIN bit를 보낸다.&lt;/li&gt;
&lt;li&gt;client가 server에게 ACK bit를 보낸다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 과정에서 바로 socket을 삭제하면 문제가 발생할 수 있기 때문에 조금 기다렸다가 삭제한다. 예를 들어 server가 ACK bit를 못 받은 경우 재전송하라고 FIN bit를 보내는 경우나, client는 전송이 끝났지만 server는 보낼 것이 남아있는 경우.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;UDP&lt;/h3&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정의&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특징&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;user datagram protocol&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UDP는 TCP에 비해 간단하다. 그냥 보내기만 한다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;TCP vs UDP&lt;/h3&gt;
&lt;div data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP는 연결형이기 때문에 연결이 성공해야 통신할 수 있다. UDP는 비연결형이기 때문에 연결 없어도 통신할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP는 전송 순서를 보장하지만 UDP는 그렇지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP는 수신 여부를 확인하지만 UDP는 그렇지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP는 1:1 통신이지만 UDP는 n:m이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP는 문제 발생 시 재전송해서 신뢰성이 높지만 UDP는 그렇지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP는 느리지만 UDP는 빠르다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;ARP&lt;/h3&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정의&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;과정&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MAC address를 조사하는 과정. subnet에 IP address에 해당하는 기기의 MAC address를 받아오는 과정.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;router가 연결된 모든 router에게 arp query를 날린다.&lt;/li&gt;
&lt;li&gt;LAN의 모든 router가 해당 frame을 수신하고, 만약 자신에게 온 것이 있다면 응답하고, 그렇지 않다면 버린다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;ethernet&lt;/h3&gt;
&lt;div data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;packet 운반은 hub, router가 한다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;NAT&lt;/h3&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정의&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이유&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작동 방식&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;subnet 전체가 하나의 IP address를 사용하는 방식이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초기 IP addess는 고유한 값이어야 하지만 IP 사용자가 너무 많아지면서 수가 부족해졌고, 때문에 독립망과 같이 완전히 독립된 네트워크인 경우 같은 IP가 있어도 상관없어졌다. 때문에 주소 변환을 하며, router가 이 기능을 수행한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내부 - 외부&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;TCP packet에 있는 sender IP address와 port를 보고 적당한 값으로 변경한다.&lt;/li&gt;
&lt;li&gt;이후 인터넷으로 송출하면 회신 packet이 돌아온다. 그 packet은 변경한 값으로 설정되어 있다.&lt;/li&gt;
&lt;li&gt;변경된 값을 원 값으로 복구해 router가 독립망에 해당 packet을 보낸다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외부 - 내부&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내부에서 외부로 먼저 보내지 않는 한 외부에서 내부로 보낼 수 없다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Proxy&lt;/h3&gt;
&lt;div data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;cache server IP address를 DNS server에 등록한다. 그러면 client는 cache server에 요청을 보내게 된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;cache miss의 경우 cache server가 web server에게 request를 날린다.&lt;/li&gt;
&lt;li&gt;cache hit의 경우 바로 응답한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이게 기본 골자이고, 아래와 같은 것들이 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;client에 cache server를 두는 forward proxy&lt;/li&gt;
&lt;li&gt;server에 cache server를 두는 reverse proxy&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;OSI&lt;/h3&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;internet protocol&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;application layer : network applicatoin 지원, user message 생성
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;presentation layer : encryption, compression 등을 해제해서 application이 data를 읽을 수 있게 한다.&lt;/li&gt;
&lt;li&gt;session layer : synchronization, checkpointing, data 복구 등을 담당한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;transport layer : source process에서 destination process까지 data 전송. segment 사용. TCP/UDP.&lt;/li&gt;
&lt;li&gt;network layer : routing을 통해 host to host delivery 수행. packet 사용. ARP, IP.&lt;/li&gt;
&lt;li&gt;link layer : 하나의 edge를 건너가기 위해 bit를 전달. frame 사용.&lt;/li&gt;
&lt;li&gt;physical layer : bit를 wire에 전송. bit 사용.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Socket&lt;/h3&gt;
&lt;div data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;application layer와 transport layer의 interface&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IP address를 사용해 host를 식별하고, port를 사용해 socket을 식별한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;multiplexing : 여러 socket으로 부터 온 message를 통합/분류해 network layer에게 보내는 transport layer의 기능&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;demultiplexing : network layer가 수신한 packet header를 떼서 desination socket에게 분배하는 transport layer의 기능&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Domain에 뭔가 입력하면 생기는 일&lt;/h3&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DHCP&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ARP&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DNS&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTTP&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DHCP : dynamic host configuration protocol의 약자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;network에 연결할 때 IP address를 할당받는다. IP address의 초기값, first hop router address, local DNS server IP address 등을 받는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ARP : domain을 찾기 위해 DNS server에 query를 날려야 한다. 이 때 first hop router address만 알고 있지 MAC address는 모른다. 이를 알기 위해 ARP를 수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;name resolution : first hop router의 MAC address를 알게 되었다. DNS name resolution을 실행한다. 이를 통해 destination의 IP address를 알게 되었다. 이 과정에서는 UDP를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP handshake : HTTP이므로, TCP session을 생성한다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;client - server : SYN bit, sequence 초기값, client window를 보낸다.&lt;/li&gt;
&lt;li&gt;server - client : SYN bit, ACK bit, sequence 초기값, server window를 보낸다.&lt;/li&gt;
&lt;li&gt;client - server : ACT bit를 보낸다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTTP request, response : TCP session이 생성되었으면 TCP 송/수신 동작을 통해 data를 주고받는다. web browser가 HTTP request message를 생성하고, server는 해당 message를 받아 응답하고, web browser가 response를 렌더링한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 router들끼리는 routing algorithm (bellman ford, dijkstra를 수행한 결과)의 결과로 routing table을 가지고 있고, routing table 내부에 어떤 IP를 어떤 router에게 보내야 하는지 정보가 있다. 이후 ARP를 통해 link layer에서 보낸다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Cookie vs Session&lt;/h3&gt;
&lt;div data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;cookie는 client에 저장되는 값. server가 client에게 cookie를 주면 client는 이를 저장하고 있다가 다음 요청 때 server에게 cookie와 request를 같이 보낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보안에 약하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;종료되어도 보관하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;session은 server에 저장되는 값.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보안이 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;종료되면 바로 삭제한다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;HTTP vs HTTPS&lt;/h3&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTTPS는 HTTP에 SSL을 씌운 것. 더 안전하다. 암호화하기 때문에 더 안전하다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최초 1회에 대칭키를 교환하기 위해 비대칭키 암호화 사용, 이후부터는 대칭키 암호화 사용.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 최초 연결 시도 -&amp;gt; 서버는 공개키 넘김.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 클라이언트는 인증서 유효성을 검사한 후 대칭키 발급, 공개키로 암호화해 서버로 전송&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 서버는 개인키로 복호화해 대칭키 얻음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 대칭키를 사용해 암호화/복호화&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;세션 기반 인증 vs 토큰 기반 인증&lt;/h3&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;authentication vs authorization&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;session의 경우 작동 방식&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;token의 경우 작동 방식&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;차이점&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;401 Unauthorized : 인증 X, 403 Forbidden : 권한 X&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;authentication : login&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;authorization : 사용자에 대한 resource 접근 확인&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;session&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용자가 로그인 시 session ID가 server에 저장된다. 이후 이 값을 client에게 준다.&lt;/li&gt;
&lt;li&gt;이후 모든 요청 시 session ID와 함께 request를 보낸다.&lt;/li&gt;
&lt;li&gt;정보가 서버의 DB에 저장된다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;token&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;로그인 시 인증 정보를 만들어 client에게 준다.&lt;/li&gt;
&lt;li&gt;이후 모든 요청 시 token과 함께 request를 보내면 server는 해당 값을 decode해서 사용자를 검증한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;session은 안전하지만, token은 암호화되지 않는다. 그렇지만 확장성이 좋기 때문에 - 확장성을 쓰면 scale out을 쓰는데, session을 사용하는 경우 해당 session은 한 server에만 저장되기 때문에 정합성 문제가 생긴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쿠키와의 차이점: 쿠키는 서명이 안 되어 있으므로 조작이 쉽다. 때문에 인증용으로는 사용하지 않는다. 토큰은 서명되어 있으므로 조작이 힘들다. 쿠키는 브라우저에 자동으로 저장되고 전송해 줌.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;JWT&lt;/h3&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정의&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작동 방식&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;json web token의 약자.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;사용자가 로그인 시 access token을 server에 저장하고 client에게 준다.&lt;/li&gt;
&lt;li&gt;client는 access token을 cookie에 저장하고 모든 요청에 해당 cookie값을 전송한다.&lt;/li&gt;
&lt;li&gt;server가 모든 요청을 받으면 해당 값을 검증한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;header, payload, signature를 .으로 구분한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;header : type, 암호화 알고리즘에 관한 정보가 있음.&lt;/li&gt;
&lt;li&gt;payload : 담고자 하는 정보를 json 형식으로 담음. 이 경우 복호화하면 바로 정보가 나오기 때문에 중요한 정보는 넣으면 안 된다.&lt;/li&gt;
&lt;li&gt;signature : encoding / 유효성 검증 시 사용하는 암호화 코드&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;암호화, 비대칭키, 대칭키&lt;/h3&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비대칭키는 암/복호화에 다른 key 사용. public key로 암호화하고 private key로 복호화한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대칭키는 암/복호화에 같은 key 사용&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;HTTP 멱등성&lt;/h3&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정의&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 게 멱등성이 있는지&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요청을 여러번 해도 서버 state가 동일한 경우 method를 멱등성 method라 부른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GET, put, delete는 멱등성, post는 아님. patch는 맞을수도 아닐수도.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필요한 이유는, network에 문제가 발생했을 떄 여러 번 가도 문제가 없어야 하기 때문.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 POST 요청을 보냈는데 응답이 없는 경우, GET을 보내서 요청이 처리가 되었는지 안 되었는지 확인한 후 안 된 경우에만 다시 POST를 보내야 함.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;REST&lt;/h3&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정의&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;rest : http uri를 통해 resource를 명시하고, HTTP method를 통해 resource에 CRUD를 적용하는 아키텍처&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;restful : REST를 잘 따르는 시스템&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;WS vs WAS&lt;/h3&gt;
&lt;div data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ws : 항상 동일한 data를 줌. static&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;was : dynamic content 제공&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;CORS&lt;/h3&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;cross origin resource sharing&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서로 다른 도메인 간에 resource를 공유하는 것. 기본적으로 차단이라 허용해야 함. 한 출처에서 실행중인 web app이 다른 출처의 자원에 접근할 수 있는 권한 부여&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>내가 하고싶은 것!/취준</category>
      <author>hyelie</author>
      <guid isPermaLink="true">https://hyelie.tistory.com/649</guid>
      <comments>https://hyelie.tistory.com/entry/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EB%A9%B4%EC%A0%91%EB%8C%80%EB%B9%84-%EC%A7%88%EB%AC%B8#entry649comment</comments>
      <pubDate>Wed, 4 Oct 2023 09:29:30 +0900</pubDate>
    </item>
    <item>
      <title>OS 면접대비 질문</title>
      <link>https://hyelie.tistory.com/entry/OS-%EB%A9%B4%EC%A0%91%EB%8C%80%EB%B9%84-%EC%A7%88%EB%AC%B8</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;Floating Point&lt;/h3&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떻게 표현하는지&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;case 3개&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변환방식&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;rounding&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;2진수를 유효숫자 형태로 표현한 것&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;(-1)$^s$M2$^E$&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;s : sign bit. signed integer와 동일하게 0이면 양수, 1이면 음수이다.&lt;/li&gt;
&lt;li&gt;M : significand(유효숫자). 일반적으로 [1.0, 2.0)의 범위를 가진다.&lt;/li&gt;
&lt;li&gt;E : exponent(승수). 2의 승수를 나타낸다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;Floating Point to Number&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;수를 정해진 형식에 따라 sign bit, exp bit, frac bit로 분류한다.&lt;/li&gt;
&lt;li&gt;exp bit, frac bit을 이용해 normalized / denormalized / special 분류를 한다.&lt;/li&gt;
&lt;li&gt;exp bit로 E값을, frac bit로 M값을 구한다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;(-1)$^s$M2$^E$에 값을 넣어 수를 구한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;Number to Floating Point&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;수를 2진수의 급수 형태로 표현한다.&lt;/li&gt;
&lt;li&gt;(-1)$^s$M2$^E$ 형식으로 변환한다.&lt;/li&gt;
&lt;li&gt;정해진 bit의 개수에 따라 bias를 구하고, E와 bias를 이용해 exp bit를 구한다. 이 때 rounding을 하며, overflow 발생 시 exp값을 조정한다.&lt;/li&gt;
&lt;li&gt;M값으로 frac bit를 구한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Byte Ordering&lt;/h3&gt;
&lt;div data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;종류 2가지&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;byte ordering이란 컴퓨터가 메모리에 값을 저장하는 방식&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Big Endian :&amp;nbsp;&lt;b&gt;LSB가 high address에 저장&lt;/b&gt;되는 방식&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Little Endian :&amp;nbsp;&lt;b&gt;LSB가 low address에 저장&lt;/b&gt;되는 방식&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Calling Convention&lt;/h3&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;procedure P가 Q를 호출했을 때 일어나는 일&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;control, data, local data&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;caller-saved register의 값들을&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;text-align: left;&quot;&gt;caller stack frame에&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;저장한다. (push)&lt;/li&gt;
&lt;li&gt;return address를 caller stack frame에 저장한다. (push)&lt;/li&gt;
&lt;li&gt;%rbp, %rsp register의 값이 변경되었으므로 callee procedure로 제어권이 넘어간다.&lt;br /&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;callee saved register의 값들을&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;text-align: left;&quot;&gt;callee stack frame에&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;저장한다. (push)&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;text-align: left;&quot;&gt;&lt;span&gt;callee procedure를 실행한다.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;text-align: left;&quot;&gt;&lt;span&gt;...&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;text-align: left;&quot;&gt;&lt;span&gt;callee procedure 종료 시, callee saved register를 callee stack frame에서 가져온다. (pop)&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;text-align: left;&quot;&gt;&lt;span&gt;return value를 %rax에 저장한다.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;text-align: left;&quot;&gt;&lt;span&gt;caller stack frame에서 %rbp를 복구한다. (pop)&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;caller procedure로 복귀한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Buffer Overflow&lt;/h3&gt;
&lt;div data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;언제 발생하는지&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜 문제가 되는지&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떻게 막는지&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;데이터의 크기가 할당된 범위보다 더 클 때 원래 입력되어 있던 값들이 오염되는 현상을 buffer overflow라 한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;return address가 다른 값으로 변경될 수 있다. return address가 위치해 있는 assembly는 무조건 실행되기 때문에 segmentation fault가 나거나, 또는 사용자가 원하는 instruction을 실행시킬 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;stack canari 사용 : 특정 data를 return address 다음 stack에 집어넣어 이 값이 바뀌지 않았을 때만 코드를 계속 실행하는 방법.&lt;/li&gt;
&lt;li&gt;safe function 사용 : string 입력 시 길이 제한을 두는 방법. gets() 대신 fgets()를 쓰면 된다.&lt;/li&gt;
&lt;li&gt;non-executable code segment ; 각 memory section에 control bit를 추가해 data section code를 readonly로 바꾸어 코드를 실행 불가능하게 만드는 방법.&lt;/li&gt;
&lt;li&gt;randomized stack offset : stack의 시작 주소를 randomized해 return address를 바꾸지 못하게 하는 방법.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Cache&lt;/h3&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;locality 2종류&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;memory hierarchy와 cache의 개념&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;cache miss 3종류, cache&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;cache 구조 : set, line, tag, block&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;cache 접근 방식&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;set index가 가운데 있는 경우&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;종류 3가지&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;b&gt;locality는 프로그램이 최근에 참조한 주소와 그 근처의 주소에 반복적으로 참조하는 경향&lt;/b&gt;이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;temporal locality&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 최근에 참조된 주소를 반복적으로 참조하는 것을 의미한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;spatial locality&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 최근에 참조한 주소 근처의 주소를 참조하는 것을 의미한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;b&gt;cache는 level k의 memory를 level k+1 memory의 임시 저장소처럼 작동시켜 데이터에 더 빨리 접근할 수 있는 기법&lt;/b&gt;이며, locality와 memory hierarchy 때문에 작동한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;miss 종류&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;cold miss&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: cache가 비어있는 경우. 첫 번째 참조일 때 발생한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;capacity miss&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: d가 cache의 크기보다 더 큰 경우.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;conflict miss&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: cache의 크기는 충분한데도 eviction이 계속 발생해 miss가 계속 발생하는 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;788&quot; data-origin-height=&quot;488&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GoQ4o/btszlHeiKJb/Kc4hYVE7aJ1kURWPw3Koe1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GoQ4o/btszlHeiKJb/Kc4hYVE7aJ1kURWPw3Koe1/img.png&quot; data-alt=&quot;cache 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GoQ4o/btszlHeiKJb/Kc4hYVE7aJ1kURWPw3Koe1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGoQ4o%2FbtszlHeiKJb%2FKc4hYVE7aJ1kURWPw3Koe1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;788&quot; height=&quot;488&quot; data-origin-width=&quot;788&quot; data-origin-height=&quot;488&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;cache 구조&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dPOXmT/btszjEbRnOD/0K7GVawVhC0tSHTAyT0Vm0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dPOXmT/btszjEbRnOD/0K7GVawVhC0tSHTAyT0Vm0/img.png&quot; style=&quot;width: 47.1104%; margin-right: 10px;&quot; data-widthpercent=&quot;47.69&quot; data-filename=&quot;Group 2721 (1).png&quot; data-origin-height=&quot;253&quot; data-origin-width=&quot;695&quot; data-is-animation=&quot;false&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dPOXmT/btszjEbRnOD/0K7GVawVhC0tSHTAyT0Vm0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdPOXmT%2FbtszjEbRnOD%2F0K7GVawVhC0tSHTAyT0Vm0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;695&quot; height=&quot;253&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kHgxx/btszlZ6VuoN/28icTZjuwD2yLMDqqtu9gk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kHgxx/btszlZ6VuoN/28icTZjuwD2yLMDqqtu9gk/img.png&quot; style=&quot;width: 51.6671%;&quot; data-widthpercent=&quot;52.31&quot; data-filename=&quot;Group 2719.png&quot; data-origin-height=&quot;157&quot; data-origin-width=&quot;473&quot; data-is-animation=&quot;false&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kHgxx/btszlZ6VuoN/28icTZjuwD2yLMDqqtu9gk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkHgxx%2FbtszlZ6VuoN%2F28icTZjuwD2yLMDqqtu9gk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;473&quot; height=&quot;157&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;cache 구조&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&amp;nbsp;address가 M(=2$^m$)개의 bit로 이루어진다고 가정하자.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;cache는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;총 S(=2$^s$)개의 set&lt;/b&gt;으로 이뤄지며,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;각 set은 E(=2$^e$)개의 line&lt;/b&gt;으로 이루어져 있다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;하나의 line은 v bit + tag bit + B(=2$^b$)개의 block&lt;/b&gt;으로 구성되어 있으며,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;하나의 block은 1 byte&lt;/b&gt;의 정보를 담는다. 이 때 v는 해당 cache가 valid한지 여부이며, tag는 cache line의 식별자이다. 따라서 total cache size = S * E * B = 2$^{s+e+b}$이다.&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;접근 방식&lt;/h4&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;address를&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;tag&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;/&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;set index&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;/&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;block offset&lt;/b&gt;으로 크게 3부분으로 나누며 이를 통해 cache에 접근한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;set index bit를 이용해 cache set에 접근한다.&lt;/li&gt;
&lt;li&gt;set에 있는 모든 line에 대해 tag bit를 비교한다.&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모든 line에 대해 tag bit가 일치하는 line이 없는 경우 cache miss이다.&lt;/li&gt;
&lt;li&gt;있는 경우 valid bit가 1인지 살펴본다.&amp;nbsp;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;not valid라면 cache miss이다.&lt;/li&gt;
&lt;li&gt;valid라면 cache hit이다. block offset을 이용해 line에 있는 block에 접근한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;set index가 가운데인 이유 : 인접해 있는 memory block들이 같은 set에 들어가게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;종류&amp;nbsp;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;fully associative : 단 1개의 set만 존재&lt;/li&gt;
&lt;li&gt;E-way set associative : 각 set당 E개의 line이 존재&lt;/li&gt;
&lt;li&gt;direct mapped : 각 set당 1개의 line만 존재&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Exception의 종류&lt;/h3&gt;
&lt;div data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;async - interrupt, signal&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;interrupt : process 외부에서 발생하는 exception (time interrupt)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;signal : event call과 비슷한 개념.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sync - trap, fault, trap&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;trap : instruction 실행 중 의도적으로 발생하는 exception (system call)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fault : 의도적이지 않은 복구 가능한 에러에 의한 exception (page fault)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;abort : 의도적이지 않은 복구 불가능한 에러에 의한 exception&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Dynamic Memory Allocation&lt;/h3&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정의&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;fragmentation 2종류&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;fit 종류 3가지&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;segmentation vs paging&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;memory에 프로그램을 올리기 위해 memory를 관리하는 기술&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Fragmentation&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;internal fragmentation&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;할당된 block이 data보다 클 때&lt;/b&gt;를 의미한다. 보통 자주 발생하며, alignment로 인한 padding 등 다양한 원인으로 인해 발생하며 측정하기 쉽다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;external fragmentation&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: heap&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;memory에는 충분한 공간이 있지만 큰 하나의 free block이 없어 메모리를 할당할 수 없는 상황&lt;/b&gt;을 의미한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Fit&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;first fit : &lt;/b&gt;free block list를 처음부터 검색해서 할당할 block size보다 크기가 큰 첫 번째 free block에 할당한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;next fit : &lt;/b&gt;first fit과 유사하지만 free block list를 처음부터 찾는 대신 이전에 검색이 종료된 지점부터 검색을 시작한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;best fit : &lt;/b&gt;모든 free block을 검사해 할당할 block size보다 크기가 큰 block 중 제일 작은 free block에 할당한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;segmentation : address space 단위로 구분하는 방식. external fragmentation 비중 높아진다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;paging : page 단위로 관리하는 방식. internal fragmentation 비중 높아진다.&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Garbage Collection - Mark &amp;amp; Sweep&lt;/h3&gt;
&lt;div data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뭔지, 어떻게 작동하는지&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;application이 직접 free하지 않고&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;사용하지 않는 dynamic allocated space를 자동적으로 free해주는 기법&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;memory와 pointer를 하나의 graph로 보아 root로부터 reachable한 것은 아직 사용중인 allocated block이고, unreachable한 것은 더 이상 사용하지 않는 garbage로 보아 이러한 block들을 free하는 방식&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Process&lt;/h3&gt;
&lt;div data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정의&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;process의 address space 4가지 + 각각에 어떤 것들이 올라가는지&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PCB&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;concurrent&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;process state : ready / running / block&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정의 : &lt;span style=&quot;text-align: start;&quot;&gt;program의 instance이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;address space&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;stack - local variable, argument, return address 등이 올라간다.&lt;/li&gt;
&lt;li&gt;heap - 동적할당한 것들이 올라간다.&lt;/li&gt;
&lt;li&gt;global data/code - 초기화된 global variable이 올라간다.&lt;/li&gt;
&lt;li&gt;code - instruction들이 올라간다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PCB :&lt;span style=&quot;text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;process context를 저장하기 위한 자료구조. PID, state, PC, register 등 정보를 가진다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;state&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;running&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 현재 실행 중인 process&lt;/li&gt;
&lt;li&gt;&lt;b&gt;ready&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: CPU의 실행을 기다리고 있는 상태&lt;/li&gt;
&lt;li&gt;&lt;b&gt;block&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: I/O나 resource 대기 등의 이유로 event를 기다리고 있는 상태&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Process Context Switching&lt;/h3&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정의&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작동 방식&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;언제 쓰는지&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;cost 종류&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정의 : &lt;span style=&quot;text-align: start;&quot;&gt;processor가 실행하고 있는 process를 바꾸는 것&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;process P0에서 P1으로 switch하기 위해서는 P0의 모든 정보를 PCB0에 저장하고, PCB1의 모든 정보를 꺼내 와 실행해야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;context switching overhead는 매우 크기 때문에, I/O를 기다리는 상황 등 process를 실행하지 않는 상황에서 다른 process로 제어권을 넘긴다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;cost&lt;span style=&quot;text-align: start;&quot;&gt;는 크게 2가지, direct cost(save/restore)와 opportunity cost(cache miss cost)가 있다.&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Fragmentation&lt;/h3&gt;
&lt;div data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;internal fragmentation : 할당한 block이 data보다 클 때&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;external fragmentation : 하나의 큰 free block이 없어 할당하지 못하는 상황&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Process Scheduling&lt;/h3&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;process state transition을 관리하는 것.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;job, ready, wait 3가지의 queue가 있다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Thread&lt;/h3&gt;
&lt;div data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;정의&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 address space 가지는지&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장점&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;TCB&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;thread state&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;정의 :&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;text-align: start;&quot;&gt;process 내의 실행 단위&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;thread의 address space의 경우,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;text-align: start;&quot;&gt;stack만 고유하게 가지며, code, data, heap은 공유한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;process에 비해 고유하게 가지는 memory가 적기 때문에 더 가볍고, 공유하고 있는 자원이 많기 때문에 context switch가 더 빠르다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;TCB : thread context를 저장하기 위한 자료구조&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;thread state : init, ready, running, waiting, exit 5종류가 있다.&lt;/span&gt; &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Concurrency&lt;/h3&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동시에 실행되는 것처럼 보이는 기술.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Synchronization&lt;/h3&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정의&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;race condition&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;critical section&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;mutex lock&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;condition variable&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;semaphore&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;synchronization의 구현&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;682&quot; data-origin-height=&quot;218&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cKNXWU/btsHszVdPCa/tF1GydW19L8AdwwyclxLOk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cKNXWU/btsHszVdPCa/tF1GydW19L8AdwwyclxLOk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cKNXWU/btsHszVdPCa/tF1GydW19L8AdwwyclxLOk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcKNXWU%2FbtsHszVdPCa%2FtF1GydW19L8AdwwyclxLOk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;682&quot; height=&quot;218&quot; data-origin-width=&quot;682&quot; data-origin-height=&quot;218&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;synchronization : multi thread로 인해 발생하는 race condition을 막는 것.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;race condition : shared data에 접근할 때 순서에 따라 다른 결과가 나올 수 있는 상태&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;critical section : 오직 하나의 thread만 실행되는 것이 보장되는 영역&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;lock의 특징&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;safe &amp;amp; mutual exclusion : 해당 thread만 실행하는 것이 보장됨&lt;/li&gt;
&lt;li&gt;progress : 아무도 lock을 가지고 있지 않다면 바로 lock을 얻을 수 있다.&lt;/li&gt;
&lt;li&gt;bounded wait : 언젠가는 lock을 얻는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;mutex lock&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;acquire() : lock이 free될 때까지 기다리며, free가 되면 lock을 가져온다.&lt;/li&gt;
&lt;li&gt;release() : lock을 release하고 lock을 기다리는 thread를 깨운다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;condition variable : lock을 가진 도중 sleep할 수 없는 문제를 해결하기 위해 만들어진 것. lock의 보조 도구 느낌으로 쓴다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;wait() : [lock release + sleep]한 후, 누군가가 깨우면 lock을 얻는다.&lt;/li&gt;
&lt;li&gt;signal() : waiting thread가 있다면 깨운다.&lt;/li&gt;
&lt;li&gt;broadcast() : 모든 waiting thread를 깨운다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;semaphore : 일반화된 lock으로 여러 thread가 critical section에 접근할 수 있게 해 준다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;P() : 값이 양수가 될 때까지 기다리며, 양수가 되면 1을 뺀다. wait()와 유사.&lt;/li&gt;
&lt;li&gt;V() : 값에 1을 더하고 P()를 호출한 thread가 있다면 깨운다. signal()과 유사.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;text-align: start;&quot;&gt;만약 값을 1로 초기화하면 mutex처럼 쓸 수 있고, 0으로 초기화하면 coordination을 위해 쓸 수 있다. (scheduling constraints)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;synchronization 구현은, uniprocessor에서는 interrupt로 쓰면 좋다. 그러나 multiprocessor인 경우 &lt;span style=&quot;text-align: start;&quot;&gt;ready-modify-write instruction(memory에서 값을 읽고 수정하고 다시 쓰는 instruction)인 &lt;/span&gt;atomic function을 사용해 이를 구현한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;test-and-set : lock을 BUSY로 바꾸고 기존 lock 값을 가져오는 방법. 이 자체가 atomic instruction이고 HW에서 보장한다. 기존 lock이 FREE면 acquire하는 식이다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;test-test-and-set : lock을 얻기 위해 lock의 값을 확인하는 단계를 거친다. 때문에 성능상 실행 시간이 더 줄고, test-and-set은 cache를 계속 갱신하므로 overhead가 크다. test-test-and-set은 얻을 수 있을 때만 test-and-set을 하므로 그 간극을 줄일 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;java monitor는 하나의 lock과 0개 이상의 condition variable을 사용해 shared data에 대산 concurrent한 접근 관리를 위한, 높은 추상화를 가진 도구이다. shared object와 유사하지만 명시적으로 lock을 사용할 필요가 없어 더 쉽게 synchronization을 처리할 수 있다. lock은 mutual exclusion을 위해, condition variable은 scheduling을 위해 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;lock에 대한 방법 : queueing lock으로 구현하는 것이 효율적.&amp;nbsp;&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;661&quot; data-origin-height=&quot;292&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ACs1L/btsHtf9Grz2/0Wt5LG4knKRDFkWbDeCKlk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ACs1L/btsHtf9Grz2/0Wt5LG4knKRDFkWbDeCKlk/img.png&quot; data-alt=&quot;process lifecycle&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ACs1L/btsHtf9Grz2/0Wt5LG4knKRDFkWbDeCKlk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FACs1L%2FbtsHtf9Grz2%2F0Wt5LG4knKRDFkWbDeCKlk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;661&quot; height=&quot;292&quot; data-origin-width=&quot;661&quot; data-origin-height=&quot;292&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;process lifecycle&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;860&quot; data-origin-height=&quot;264&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cdr0Uw/btsHuO3HYUX/QYqNoZ8yYwiPR6Yeguyi60/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cdr0Uw/btsHuO3HYUX/QYqNoZ8yYwiPR6Yeguyi60/img.png&quot; data-alt=&quot;thread lifecycle&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cdr0Uw/btsHuO3HYUX/QYqNoZ8yYwiPR6Yeguyi60/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcdr0Uw%2FbtsHuO3HYUX%2FQYqNoZ8yYwiPR6Yeguyi60%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;860&quot; height=&quot;264&quot; data-origin-width=&quot;860&quot; data-origin-height=&quot;264&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;thread lifecycle&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Multiprocessor Synchronization&lt;/h3&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;lock contention&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;cache ping&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;cache coherence&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;mutliprocessor에서 test-and-set의 문제점과 해결방법&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;lock contention : 한 번에 하나의 thread만 lock을 가질 수 있기 때문에 경쟁이 더 심해진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;cache ping : context switch가 일어나며 shared variable의 cache가 여러 번 갱신되고, cache coherence를 유지하기 위해 해당 cache를 invalidate하기 때문에 cache 효율이 떨어진다.&lt;/span&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;cache coherence : 각 processor의 cache가 일관성있게 유지되어야 하는 것을 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;multiprocessor에서 test-and-set의 문제점&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;test-and-set은 shared variable의 값을 계속 cache에 값을 쓰기 때문에, 다른 processor들이 참조하고 있는 cache를 invalidate한다. 따라서 test-test-and-set을 쓰면 효율이 좀 더 좋아진다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;text-align: left;&quot;&gt;lock의 starvation이 일어날 수 있다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: left;&quot;&gt;&lt;span&gt;이를 해결하기 위해 ticket lock이나 MCS lock 등 해결 방법을 사용한다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ticket lock은 이름처럼 ticket을 뽑고, 자신의 차례가 올 때까지 기다리는 방식이다.&lt;/li&gt;
&lt;li&gt;MCS lock은 cache coherence 문제를 해결하는 방식으로, compare-and-swap과 queue로 lock을 관리하는 방식이다. lock 획득 시 이전 thread가 쓴 데이터를 읽고, 때문에 cache miss가 나도 오직 1개의 process에서만 나게 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Deadlock&lt;/h3&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정의&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조건 4가지&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처리 방식 3가지&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정의 : 2개 이상의 작업이 resource를 할당받기 위해 circular waiting을 하고 있는 상태. resource는 CPU, disk 공간, memory, lock 등.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조건&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;mutual exclusion : 한 번에 하나의 thread/process만 resource에 접근할 수 있다.&lt;/li&gt;
&lt;li&gt;non preemption : 다른 process의 resource를 뺏을 수 없다.&lt;/li&gt;
&lt;li&gt;hold and wait : resource를 가진 채로 다른 resource를 기다린다.&lt;/li&gt;
&lt;li&gt;circular wait : resource를 기다리는 것들이 circular해야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처리 방식&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;prevent : 발생 자체를 막는 방식&lt;/li&gt;
&lt;li&gt;avoid : deadlock이 발생할 수 있지만 순서를 조절하는 방식
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;banker algorithm : 현재 사용할 수 있는 resource, 각 process가 필요한 resource에 대한 정보들을 모아둔 후각 process에세 필요한 resource를 줘 보고 safe한지 검사하는 방식으로, simluation을 돌려보는 방식.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;detect &amp;amp; cover : 발생하면 감지하고 복구하는 방식&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Process Scheduling&lt;/h3&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;목표와 몇 가지 scheduling 방법&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;목표 : throughput 최대화, fairness&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;FIFO&lt;/li&gt;
&lt;li&gt;SJF : optimal but unfair&lt;/li&gt;
&lt;li&gt;RR : time quantum만큼 실행한 후 queue의 맨 뒤로 넣는 방식. 같은 길이 task 여러개면 오래 걸린다.&lt;/li&gt;
&lt;li&gt;MFQ : 여러 개의 RR queue를 두는 방식. 각 RR queue의 time quantum이 다르다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Virtual Memory&lt;/h3&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정의&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;segmentation&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;demand paging&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;replacement policy&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;thrashing&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;address space : OS가 제공하는 physical memory의 abstraction. 각 process는 같은 address space를 바라보는 것 처럼 보이지만, OS 내부에서 다른 physical memory를 가리키게 처리해 준다. (address translation)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;physical memory address를 사용하는 것이 아니라 virtual memory address를 사용해 유한한 크기의 memory를 무한하게 보이게 만드는 방법. 따라서 virtual address를 physical address로 바꾸는 address translation 과정이 필요하다. 여기서 cache 효율을 높이기 위해 TLB를 쓰기도 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;segmentation : base and bound를 사용해서 virtual memory의 address space를 segment로 나누고 translate하는 방식. + 권한 체크도&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 demand paging을 쓴다. demand paging은 page가 필요할 때만 disk에서 꺼내와 memory에 올리는 방식이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;process에서 특정 page가 필요하면, memory에 있는 page table을 본다. page table은 virtual page와 physical page를 매핑하는 자료구조.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;page hit라면 해당 page를 요청하고 받는다.&lt;/li&gt;
&lt;li&gt;page fault가 나면 page fault handler가 victim page를 선택해 교체한다.(필요 시 write back) 이후 disk에서 page를 memory에 올리고 page table을 갱신한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;교체 알고리즘&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;FIFO&lt;/li&gt;
&lt;li&gt;LRU : 제일 오래된 것 요체, optimal에 근사&lt;/li&gt;
&lt;li&gt;LFU : 제일 적게 사용된 것 교체&lt;/li&gt;
&lt;li&gt;Clock Algorithm : 2nd change - 2번 걸리면 교체하는 방식&lt;/li&gt;
&lt;li&gt;Nth change Algorithm : n번 걸리면 교체&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Thrashing : demand paging에서 page fault가 계속 발생해 I/O cost만 계속 사용하는 것. process가 너무 많거나, memory 크기보다 필요한 page 개수 크기가 더 많거나 등의 이유가 있다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;OS란?&lt;/h3&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HW와 SW 사이의 interface.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;HW를 abstract해서 SW가 사용하기 쉽게 만들어준다. CPU를 process/thread로, memory는 address space로, disk는 file로.&lt;/li&gt;
&lt;li&gt;resource를 관리한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;kernel?&lt;/h3&gt;
&lt;div data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Process vs Thread&lt;/h3&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정의&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공유 여부&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;cost&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;격리 여부&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정의&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;process는 program의 instance&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;thread는 instruction flow&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공유 여부&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;process는 process context를 저장하는 PCB, 4가지 address space를 가진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;thread는 stack만 별개의 것을 가지며 heap code data는 thread끼리 공유한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;때문에 process끼리 통신하기 위해서는 resource를 많이 써야 하는 반면 thread끼리는 resource를 적게 써도 된다. 때문에 process는 parallel하게, thread는 concurrent하게 쓰는 것이 일반적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;cost&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;stack의 경우 크기가 작으므로 context switching cost가 적고, 생성 cost가 더 적다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;격리 여부&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;process끼리는 완전히 격리되어 있어 하나의 process에서 문제가 생겨도 괜찮다. 반면 thread는 그렇지 않기 때문에&amp;nbsp;하나의 thread에서 오류가 생기면 다른 thread에 영향을 줄 수도 있다. (heap에 이상한 값 쓴다던가 등)&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Concurrent vs Parallel&lt;/h3&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정의&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;parallel은 실제로 작업이 동시에 실행되는 것을 말한다. 예를 들어 process가 2개의 processor(CPU core)에서 동시에 돌아가면 parellel이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;concurrent는 작업이 동시에 실행되지 않지만, 그렇게 보이는 것을 말한다. 실제로는 context switching이 일어나면서 동작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로 thread가 concurrent를 구현하고, process가 parallel을 쓴다.&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Zombie Process&lt;/h3&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정의&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;언제 생기는지&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떻게 해결해야 하는지&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;parnet process가 fork()를 call 하면 child process가 생긴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때 parent process가 wait()를 call하면 child process의 모든 자원을 정리한다. 그렇지 않고 parent process가 종료하는 경우 child process는 orphan process가 된다. 이후 init process가 종료될 때 모든 orphan process를 정리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나, orphan process가 가지고 있는 resource는 계속 메모리에 남아 있게 되며, 이러한 process를 zombie process라고 한다. 따라서 parent process는 wait()를 explicitly 호출해야 한다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>내가 하고싶은 것!/취준</category>
      <author>hyelie</author>
      <guid isPermaLink="true">https://hyelie.tistory.com/648</guid>
      <comments>https://hyelie.tistory.com/entry/OS-%EB%A9%B4%EC%A0%91%EB%8C%80%EB%B9%84-%EC%A7%88%EB%AC%B8#entry648comment</comments>
      <pubDate>Mon, 2 Oct 2023 05:55:23 +0900</pubDate>
    </item>
    <item>
      <title>DB 면접대비 질문</title>
      <link>https://hyelie.tistory.com/entry/DB-%EB%A9%B4%EC%A0%91%EB%8C%80%EB%B9%84-%EC%A7%88%EB%AC%B8</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;Transaction&lt;/h3&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;transaciton의 정의, 특성(ACID)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;commit / rollback&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;state&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;transaction :&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;DBMS의 상호작용 단위&lt;/b&gt;. transaction은 다음 4가지 성질을 가지고 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Atomicity&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;: transction은 실행되거나, 실행되지 않거나 둘 중 하나의 상태만 가진다. 중간에 끊기지 않는다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Consistency&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;: transaction의 실행 결과는 항상 일관성이 있다. (정해둔 규칙을 위배하지 않는다.)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Isolation&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;: transaction 사이에 다른 trasnaction이 낄 수 없다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Durability&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;: DBMS가 꺼져도 수행된 transaction은 반영되어 있어야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;commit은 모든 작업이 정상적으로 수행되었다는 명령이며, 실 DB에 반영하게 된다.&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;rollback은 crash가 난 경우, 해당 transaction의 변경을 취소하는 과정이다. 직전 commit까지만 복구한다.&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이를 통해 consistency를 보장할 수 있다.&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;active : transaction이 실행 중인 상태&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;failed : transaction에 오류가 발생해 중단된 상태&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;partially commited : 사용자의 commit 요청이 왔을 때 도착하는 상태. 아직 반영된 상태가 아니다.&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;commit 되면 commited로 가고, 그렇지 않으면 failed로 간다.&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;commited : transaction이 성공적으로 종료된 상태&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;aborted : rollback을 수행하고 있는 상태&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Index&lt;/h3&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;종류 2가지&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;B tree와 B+ tree의 차이&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;B+ tree의 특징 3가지, 시간복잡도&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;hash table과의 차이점, 각각&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;index를 사용할 때 장단점&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;clustered vs non-clustered&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 column을 쓰면 좋은지&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;종류는 hash table과 B+ tree가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;B tree는 tree의 모든 element에 entry가 저장됨&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;B+ tree는 leaf node는 data page에 대한 pointer를 가지고, non leaf node는 key와 node에 대한 pointer만 가짐, leaf node들끼리 pointer를 가짐. leaf node는 data의 pointer를 가짐.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;B+ tree의 특징&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;m-way balanced tree&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(각 node에는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;최대 m-1개의 element&lt;/b&gt;가 들어있고 각 node는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;최대 m개의 child&lt;/b&gt;를 가질 수 있다)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;balanced tree인 만큼, child의 height 차이가 1 이상 나지 않는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;모든&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;leaf node는 doubly linked list&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;space utilization이 50% 이상&lt;/b&gt;이어야 한다.&amp;nbsp;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;leaf node는 $\left \lfloor \frac{n+1}{2} \right \rfloor$개의 pointer가 있어야 하고,&lt;/li&gt;
&lt;li&gt;non-leaf node는 $\left \lceil \frac{n+1}{2} \right \rceil$개의 pointer가 있어야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;hash table은 접근, 수정, 삭제가 O(1)이지만 범위가 있는 접근을 못한다. hash 함수를 사용하기 때문에 pointer가 랜덤이기 때문. 반면 B+ tree는 접근/수정/삭제가 O(logn)이지만 tree이기 때문에 range하게 접근할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;장점 : index에 있는 정보에 대해서는 read 속도가 빨라진다.&lt;/li&gt;
&lt;li&gt;단점 : insert/delete/update 속도가 느려진다. 해당 tree를 수정해야 하기 때문.&lt;/li&gt;
&lt;li&gt;자주 select되고, 덜 update되는 column에 대해 쓰는 것이 좋다. + FK나 join도.&lt;/li&gt;
&lt;li&gt;공통적으로 사용하는 것 - index column의 순서는 cardinality가 높은 것부터 낮은 순으로 나열하는 것이 좋다. (data의 unique 개수)&amp;nbsp;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;cardinality가 높은 것, selectivity가 낮은 것(적은 row가 찾아짐), 조회 많은 것, 수정 적은 것&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;b&gt;clustered index&lt;/b&gt;&lt;span style=&quot;text-align: start;&quot;&gt;는 B+ tree의&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;data page가 index 순서대로 정렬된 index&lt;/b&gt;&lt;span style=&quot;text-align: start;&quot;&gt;이고,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;non-clustered index&lt;/b&gt;&lt;span style=&quot;text-align: start;&quot;&gt;는 그렇지 않음.&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Schema&lt;/h3&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;schema의 정의 : DB의 구조와 제약에 대한 명세&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;schema의 3가지 종류&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;independence 2가지 종류&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;schema는 data의 특정 collection의 설명을 말한다. data의 관계, 구조, 표현 방법을 표시하는 구조이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;external schema (view) : 각 user가 접근하는 table&lt;/li&gt;
&lt;li&gt;conceptual schema (logical) : 어떤 종류의 data가 저장되는지&lt;/li&gt;
&lt;li&gt;physical schema (physical) : data가 어떻게 저장되는지&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;independence&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;physical data independence : physical structure를 바꾸어도 logical schema는 불변하는 특징&lt;/li&gt;
&lt;li&gt;logical data independence : logcial structure를 바꾸어도 external schema는 불변하는 특징&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Join&lt;/h3&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;join의 종류 3가지&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;theta join&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(conditional join) R⋈$_c$S :&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;조건 c를 만족하는 tuple&lt;/b&gt;만 가져온다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;equi join&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: theta join에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;조건 c가 equal로만&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;이뤄져 있는 theta join&lt;/li&gt;
&lt;li&gt;&lt;b&gt;natrual join&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;R⋈S :&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;모든 공통 field에 대한 equi join&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;각 연산의 I/O cost&lt;/h3&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;buffer pool?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;seq scan, index scan&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;external sort&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;nested loop join, blocked nested join, sort merge join, grace hash join&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&amp;nbsp;DBMS는 disk에 있는 page를 memory에 frame으로 저장하는데, 이 공간을&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;buffer pool&lt;/b&gt;이라 한다. OS의 paging과 매우 유사하지만 조금은 다르다. (DB는 prefetch 예측하기 때문)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;seq scan : &lt;b&gt;$|R|_p$&lt;/b&gt;이다.&lt;/li&gt;
&lt;li&gt;index scan
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;clustered : &lt;b&gt;$\left \lceil \frac{card(P)}{B} \right \rceil + 1$&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;non clustered : &lt;b&gt;card(P)&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;external sort : &lt;b&gt;$2|R|_p \times (\left \lceil log_{B-1}\left \lceil \frac{|R|_p}{B} \right \rceil \right \rceil +&amp;nbsp; 1)$&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;nested loop join : &lt;b&gt;$|R|_p + |R|_p \times |S|_p$&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;blocked nested join : &lt;b&gt;$|R|_p + \left \lceil \frac{|R|_p}{B-2} \right \rceil \times |S|_p$&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;sort merge join : &lt;b&gt;$|R|_p + |S|_p$&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;grace hash join : &lt;b&gt;$3|R|_p + 3|S|_p$&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Query Optimizer&lt;/h3&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작동 방식&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;query를&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;logical operator들의 tree&lt;/b&gt;로 표현하고,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;logical equivalent rule을 적용해 tree를 바꾼다&lt;/b&gt;.&lt;/li&gt;
&lt;li&gt;앞서 살펴본&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;physical operator를 적용해 physical plan&lt;/b&gt;으로 바꾼다.&lt;/li&gt;
&lt;li&gt;query optimizer는 plan의 실행 시간을 유추해서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;best plan을 도출&lt;/b&gt;한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Key&lt;/h3&gt;
&lt;div data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;key의 종류 4가지&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Candidate Key&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;:&amp;nbsp;tuple을 unique하게 식별할 수 있는 attribute set.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;+ minimality : attribute 1개를 지우면 식별할 수 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Super Key&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;:&amp;nbsp;key의 super set&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Primary Key&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;:&amp;nbsp;candidate key 중 지정된 것.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Foreign Key&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;:&amp;nbsp;다른 relation을 참조하기 위해 다른 relation의 key를 가져온 것. dangling을 알아서 처리해 준다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Normalization&lt;/h3&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;normalization이란&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;anomaly&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 단계가 만족하는 조건&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장단점&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;functional dependency가 뭔지&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용하는 목적은 redundancy를 줄여 anomaly를 막기 위함이다. 단점으로는 relation이 쪼개져 join 연산이 많아진다는 것이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;anomaly는 redundancy 때문에 발생하며, 다음 3가지가 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;insertion anomaly&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: insert 시 없는 정보가 있어 삽입하지 못하는 경우. 또는 null 정보를 넣어야 한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;deletion anomaly&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 삭제 시 원하지 않는 정보가 삭제되는 경우&lt;/li&gt;
&lt;li&gt;&lt;b&gt;update anomaly&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 중복된 여러 값들 중 하나만 수정되는 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;1NF :&amp;nbsp;모든&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;도메인이 atomic value&lt;/b&gt;일 때&lt;/li&gt;
&lt;li&gt;2NF :&amp;nbsp;모든 non-key attribute가 candidate key에 fully dependent한 경우.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;(&lt;b&gt;부분적 함수 종속 제거&lt;/b&gt;, partial functional dependency 삭제)&lt;/li&gt;
&lt;li&gt;candidate key의 일부분으로 식별할 수 없는 것.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;3NF : 모든 functional dependency X =&amp;gt; Y에 대해 X가 superkey이거나 Y가 prime attribute인 경우.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;(&lt;b&gt;이행적 함수 종속 제거&lt;/b&gt;, transivity functional dependency 삭제)&lt;/li&gt;
&lt;li&gt;X =&amp;gt; Y, Y =&amp;gt; Z로 식별할 수 있는 정보가 없는 것.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;BCNF :&amp;nbsp;&lt;b&gt;모든 functional dependency X =&amp;gt; Y에 대해 X가 candidate key&lt;/b&gt;인 경우.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;(&lt;b&gt;모든 결정자 X가 후보키인 것&lt;/b&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;4NF :&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;다치&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;종속 제거&lt;/li&gt;
&lt;li&gt;5NF :&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;조인&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;종속 제거&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&amp;nbsp;어떤 relation의 모든 tuple t$_1$, t$_2$과 어떤 field X, Y에 대해 if t$_1$[X] = t$_2$[X] then t$_1$[Y] = t$_2$[Y]인 X, Y의 관계를 functional dependency라고 한다. 말로 풀어쓰면&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;relation의 field X로 Y를 식별할 수 있는 관계&lt;/b&gt;. 더 풀어 쓰면 X를 알면 Y를 알 수 있는 관계를 말한다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Integrity Constraint&lt;/h3&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정의 : 정확성, 일관성, 유효성이 유지되는 것&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Anomaly&lt;/h3&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정의&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3가지 anomaly&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;anomaly는 redundancy 때문에 발생하며, 다음 3가지가 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;insertion anomaly&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: insert 시 없는 정보가 있어 삽입하지 못하는 경우. 또는 null 정보를 넣어야 한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;deletion anomaly&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 삭제 시 원하지 않는 정보가 삭제되는 경우&lt;/li&gt;
&lt;li&gt;&lt;b&gt;update anomaly&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 중복된 여러 값들 중 하나만 수정되는 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;CAP Theroem / PACELC Theorem&lt;/h3&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정의&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Consistency, Availability, Partition Tolerance 3가지를 만족할 수 없다는 정리. 증명되었다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;consistency : 모든 request가 최신 데이터를 받는다.&lt;/li&gt;
&lt;li&gt;availability : 모든 request는 정상적인 response를 받는다.&lt;/li&gt;
&lt;li&gt;partition tolerance : 네트워크 오류 상황이어도 정상 작동한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;PACELC theorem은 normal state, abnormal state를 구분해 설명하는 방식이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;partition이 존재하는 경우 (abnormal)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;availability과 consistency는 tradeoff이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;partition이 존재하지 않는 경우 (normal)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;latency와 consistency는 tradeoff이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;RDB vs NoSQL&lt;/h3&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각각의 장단점과 차이&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Redis&lt;/h3&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특징 몇가지만 준비&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;append only file : query를 저장하고, crash 시점부터 다시 만들어 두는 방식&lt;/li&gt;
&lt;li&gt;snapshot : 특정 지점을 디스크에 백업&lt;/li&gt;
&lt;li&gt;key-value로 이루어졌다.&lt;/li&gt;
&lt;li&gt;single thread&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빠르지만 날아갈 수 있어서 사용 시 백업에 대한 준비를 해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빠르기 때문에 자주 조회/수정되는 leaderboard, session 등을 저장하기 좋다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2 Phase Locking&lt;/h3&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용 목적&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;lock의 종류 2가지&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;phase 종류 2가지&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;transaction에 lock을 걸어서 serialization을 보장하는 concurreny 처리 방법. lcok은 resource에 대한 mutual exclusion을 위해 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Shared Lock : 해당 resource에 대해 read 연산만 가능한 lock. 따라서 한 resource에 여러 개의 shared lock이 걸릴 수 있다.&lt;/li&gt;
&lt;li&gt;Exclusive Lock :&amp;nbsp;해당 resource에 대해 read 연산과 write 연산 둘 다 가능한 lock. 따라서 한 resource에 하나의 exclusive lock만 걸릴 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;lock만으로는 deadlock이 발생할 수도 있기 때문에&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;strict 2 phase lock&lt;/b&gt;을 사용한다. locking phase와 unlocking phase 2단계로 나뉜다.&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;locking phase : lock 연산만 수행할 수 있는 단계&lt;/li&gt;
&lt;li&gt;unlocking phase : unlock 연산만 수행할 수 있는 단계. unlock은 transaction이 완전히 끝난 후에 실행한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;SQL 종류&lt;/h3&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3가지 - DML, DDL, DCL&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Delete vs Truncate vs Drop&lt;/h3&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;delete는 rollback 가능, data 삭제하는 명령어&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;truncate는 table을 제외한 전체 data를 삭제하는 명령어, rollback 불가능&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;drop은 table을 삭제하는 명령어, rollback 불가능&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Prepared Statement&lt;/h3&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원리&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;RDB vs NoSQL&lt;/h3&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RDB는 &lt;span style=&quot;text-align: start;&quot;&gt;정해진 schema에 따라 data를 저장하고, &lt;/span&gt;relation을 사용해 data를 분산시킨다. 때문에 redundancy를 줄일 수 있고, anomaly를 막을 수 있다. - 즉, consistent하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NoSQL은 정해진 구조가 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RDB의 경우 consistent하지만 수정하기 어렵고, join이 많아질 경우 cost가 매우 커진다. 또한 확장도 어렵다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NoSQL의 경우 유연하고, 확장이 쉽다. 반면 redundancy를 계속 확인해야 한다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;ORM&lt;/h3&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;object와 relation을 매핑하는 것. 코드 레벨에서 DB에 접근하기 때문에 유지보수하기 좋다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;SQL 실행 순서&lt;/h3&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FROM, ON, JOIN&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WHERE, GROUP BY, HAVING&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SELECT&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DISTINT&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ORDER BY&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LIMIT&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Recovery&lt;/h3&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ARIES recovery의 가정 2가지&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;force ? no force ? steal ? no steal ?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;write ahead logging의 방식 2가지&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;aries recovery algorithm의 동작 3가지&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;transaction의 state 5가지&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;ARIES recovery의 가정 : strict 2 phase locking, WAL (write ahead logging)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Force: transaction commit 후 바로 disk에 쓰는 방식&lt;/li&gt;
&lt;li&gt;No Force : transaction commit 후 바로 disk에 쓰지 않는 방식&lt;/li&gt;
&lt;li&gt;Steal: transaction 완료 여부에 상관없이 data를 disk에 기록하는 방식&lt;/li&gt;
&lt;li&gt;No Steal: transaction uncommit 상태에서 data를 disk에 기록하지 않는 방식&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;ARIES recovery의 경우&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;b&gt;Force + No Steal&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;방식으로, 어렵지만 빠르다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;WAL&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;disk에 update하기 전에 log를 작성한다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;undo&lt;/b&gt;를 위해 사용하며, disk에 update한 후 crash가 나면 undo할 수 없기 때문이다.&lt;/li&gt;
&lt;li&gt;transaciton이 commit되기 전에 모든 작업 내용을 log에 기록해야 한다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;redo&lt;/b&gt;를 위해 사용하며, 해당 transaction이&amp;nbsp; commit되었음을 보장한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;recovery algorithm&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;analysis : REDO 시작위치 결정 (checkpoint : transaction이 모두 disk에 쓰인 시점), crash 위치 결정&lt;/li&gt;
&lt;li&gt;redo : analysis에서 결정한 위치부터 crash 직전까지 redo 수행.&lt;/li&gt;
&lt;li&gt;undo : log를 역순으로 읽으면서 uncommit transaction까지 undo. (최근 commit까지)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;transaction state&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;active : transaction이 실행 중인 상태&lt;/li&gt;
&lt;li&gt;failed : transaction에 오류가 발생해 중단된 상태&lt;/li&gt;
&lt;li&gt;partially commited : 사용자의 commit 요청이 왔을 때 도착하는 상태. 아직 반영된 상태가 아니다. commit 되면 commited로 가고, 그렇지 않으면 failed로 간다.&lt;/li&gt;
&lt;li&gt;commited : transaction이 성공적으로 종료된 상태&lt;/li&gt;
&lt;li&gt;aborted : rollback을 수행하고 있는 상태&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Transaction Isloation Level&lt;/h3&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4가지 level&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각각이 뭔지, 어떤 문제가 발생할 수 있는지&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;non consistent data를 허용하는 수준.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Level 1 (&lt;b&gt;Read&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Uncommited&lt;/b&gt;) : 아직 commit되지 않은 record나, transaction이 처리중인 record을 다른 transaction에서 접근할 수 있음. 이 경우 consistent하지 않다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;commit되지 않은 data를 보는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;dirty read&lt;/b&gt;가 발생할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Level 2 (&lt;b&gt;Read&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Commited&lt;/b&gt;) : commit된 transaction의 결과만 조회할 수 있다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;하나의 transaction에 같은 query가 2개 이상 있을 때, 다른 결과가 나오는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;non repeatable read&lt;/b&gt;가 발생할 수 있다. query 실행 시점 사이에 다른 transaction이 2개 실행되면 이런 현상이 생긴다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Level 3 (&lt;b&gt;Repeatable&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Read&lt;/b&gt;) : 하나의 transaction에서 조회한 record가 같음을 보장한다. 하나의 transaction이 읽은 record를 다른 transaction이 변경하지 못하게 한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;같은 transaction을 2번 실행했을 때 결과가 다른&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;phantom&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;read&lt;/b&gt;가 발생할 수 있다. (select - insert - select의 경우)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Level 4 (&lt;b&gt;Serializable&lt;/b&gt;) : 한 transaction이 사용하는 record를 다른 transaction에서 접근할 수 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;Clustering vs Replication&lt;/h3&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333;&quot; data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정의, 장단점&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333;&quot; data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;clustering : 같은 저장소를 사용하되 여러 개의 DBMS를 사용하는 것. 동기 방식을 사용하기 때문에 일관성 있고, DB서버에 대한 부하 분산 가능. 그러나 저장소가 같기 때문에 lock으로 인한 병목 발생 가능.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;replication : 저장소를 비동기 방식으로 사용하는 것. 일반적으로 DB는 select가 많기 때문에 insert할 수 있는 master와, master의 정보를 비동기로 저장하는 read replica를 두는 방식.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>내가 하고싶은 것!/취준</category>
      <author>hyelie</author>
      <guid isPermaLink="true">https://hyelie.tistory.com/646</guid>
      <comments>https://hyelie.tistory.com/entry/DB-%EB%A9%B4%EC%A0%91%EB%8C%80%EB%B9%84-%EC%A7%88%EB%AC%B8#entry646comment</comments>
      <pubDate>Sun, 1 Oct 2023 04:13:29 +0900</pubDate>
    </item>
    <item>
      <title>DB 기초 - 2</title>
      <link>https://hyelie.tistory.com/entry/DB-%EA%B8%B0%EC%B4%88-2</link>
      <description>&lt;blockquote style=&quot;text-align: left;&quot; data-ke-style=&quot;style2&quot;&gt;이 글은 포스텍 한욱신 교수님의 데이터베이스시스템(CSED421) 강의를 기반으로 재구성한 것입니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Key&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;다음 4종류가 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Candidate Key&lt;/b&gt; :&amp;nbsp;tuple을 unique하게 식별할 수 있는 attribute set.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;+ minimality : attribute 1개를 지우면 식별할 수 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Super Key&lt;/b&gt; :&amp;nbsp;key의 super set&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Primary Key&lt;/b&gt; :&amp;nbsp;candidate key 중 지정된 것.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Foreign Key&lt;/b&gt; :&amp;nbsp;다른 relation을 참조하기 위해 다른 relation의 key를 가져온 것. dangling을 알아서 처리해 준다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Normal Form / Normalization&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;[1NF - 2NF - 3NF - BCNF - 4NF - 5NF] 순서로 더 higher하다. 사용하는 목적은 redundancy를 줄여 anomaly를 막기 위함이다. 단점으로는 relation이 쪼개져 join 연산이 많아진다는 것이 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;1NF :&amp;nbsp;모든 &lt;b&gt;도메인이 atomic value&lt;/b&gt;일 때&lt;/li&gt;
&lt;li&gt;2NF :&amp;nbsp;모든 non-key attribute가 candidate key에 fully dependent한 경우.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;(&lt;b&gt;부분적 함수 종속 제거&lt;/b&gt;, partial functional dependency 삭제)&lt;/li&gt;
&lt;li&gt;candidate key의 일부분으로 식별할 수 없는 것.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;3NF : 모든 functional dependency X =&amp;gt; Y에 대해 X가 superkey이거나 Y가 prime attribute인 경우.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;(&lt;b&gt;이행적 함수 종속 제거&lt;/b&gt;, transivity functional dependency 삭제)&lt;/li&gt;
&lt;li&gt;X =&amp;gt; Y, Y =&amp;gt; Z로 식별할 수 있는 정보가 없는 것.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;BCNF :&amp;nbsp;&lt;b&gt;모든 functional dependency X =&amp;gt; Y에 대해 X가 candidate key&lt;/b&gt;인 경우.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;(&lt;b&gt;모든 결정자 X가 후보키인 것&lt;/b&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;4NF : &lt;b&gt;다치&lt;/b&gt; 종속 제거&lt;/li&gt;
&lt;li&gt;5NF : &lt;b&gt;조인&lt;/b&gt; 종속 제거&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Functional Dependency&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;어떤 relation의 모든 tuple t$_1$, t$_2$과 어떤 field X, Y에 대해 if t$_1$[X] = t$_2$[X] then t$_1$[Y] = t$_2$[Y]인 X, Y의 관계를 functional dependency라고 한다. 말로 풀어쓰면 &lt;b&gt;relation의 field X로 Y를 식별할 수 있는 관계&lt;/b&gt;. 더 풀어 쓰면 X를 알면 Y를 알 수 있는 관계를 말한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 사용해 redundancy를 formal한 방식으로 알아낼 수 있는데, functional dependency X =&amp;gt; Y가 있는 경우, X가 중복되면 Y도 중복됨을 알 수 있기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;한편, &lt;b&gt;key는 모든 attribute를 functionally determine&lt;/b&gt;한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Anomaly&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;anomaly는 redundancy 때문에 발생하며, 다음 3가지가 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;insertion anomaly&lt;/b&gt; : insert 시 없는 정보가 있어 삽입하지 못하는 경우. 또는 null 정보를 넣어야 한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;deletion anomaly&lt;/b&gt; : 삭제 시 원하지 않는 정보가 삭제되는 경우&lt;/li&gt;
&lt;li&gt;&lt;b&gt;update anomaly&lt;/b&gt; : 중복된 여러 값들 중 하나만 수정되는 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Prepared Statement&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;일반적인 SQL의 경우 parse 과정을 거치는데, prepared statement는 해당 SQL의 문법을 검사한 후 DB에 caching한다. 때문에 성능이 향상되고, SQL이 이미 caching된 상태이므로 parameter에 들어가는 값을 SQL로 인식하지 않고 parameter로 인식하므로&amp;nbsp; injection을 막을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;CAP Theorem / PACELC Theorem&lt;/h2&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Consistency, Availability, Partition Tolerance 3가지를 만족할 수 없다는 정리. 증명되었다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;consistency : 모든 request가 최신 데이터를 받는다.&lt;/li&gt;
&lt;li&gt;availability : 모든 request는 정상적인 response를 받는다.&lt;/li&gt;
&lt;li&gt;partition tolerance : 네트워크 오류 상황이어도 정상 작동한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;PACELC theorem은 normal state, abnormal state를 구분해 설명하는 방식이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;partition이 존재하는 경우 (abnormal)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;availability과 consistency는 tradeoff이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;partition이 존재하지 않는 경우 (normal)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;latency와 consistency는 tradeoff이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;Redis&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;append only file : query를 저장하고, crash 시점부터 다시 만들어 두는 방식&lt;/li&gt;
&lt;li&gt;snapshot : 특정 지점을 디스크에 백업&lt;/li&gt;
&lt;li&gt;key-value로 이루어졌다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2 Phase Locking&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;transaction에 lock을 걸어서 serialization을 보장하는 concurreny 처리 방법. lcok은 resource에 대한 mutual exclusion을 위해 사용한다. lock 종류는 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Shared Lock : 해당 resource에 대해 read 연산만 가능한 lock. 따라서 한 resource에 여러 개의 shared lock이 걸릴 수 있다.&lt;/li&gt;
&lt;li&gt;Exclusive Lock :&amp;nbsp;해당 resource에 대해 read 연산과 write 연산 둘 다 가능한 lock. 따라서 한 resource에 하나의 exclusive lock만 걸릴 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Group 3400.png&quot; data-origin-width=&quot;346&quot; data-origin-height=&quot;164&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8bp1m/btsv8Qe5zOk/qBYQBzPN8sd3H7OpXr7ki0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8bp1m/btsv8Qe5zOk/qBYQBzPN8sd3H7OpXr7ki0/img.png&quot; data-alt=&quot;2 phase locking&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8bp1m/btsv8Qe5zOk/qBYQBzPN8sd3H7OpXr7ki0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8bp1m%2Fbtsv8Qe5zOk%2FqBYQBzPN8sd3H7OpXr7ki0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;367&quot; height=&quot;174&quot; data-filename=&quot;Group 3400.png&quot; data-origin-width=&quot;346&quot; data-origin-height=&quot;164&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;2 phase locking&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;lock만으로는 deadlock이 발생할 수도 있기 때문에 &lt;b&gt;strict 2 phase lock&lt;/b&gt;을 사용한다. locking phase와 unlocking phase 2단계로 나뉜다.&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;locking phase : lock 연산만 수행할 수 있는 단계&lt;/li&gt;
&lt;li&gt;unlocking phase : unlock 연산만 수행할 수 있는 단계. unlock은 transaction이 완전히 끝난 후에 실행한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;Isolation Level&lt;/h2&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;non consistent data를 허용하는 수준.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Level 1 (&lt;b&gt;Read&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Uncommited&lt;/b&gt;) : 아직 commit되지 않은 record나, transaction이 처리중인 record을 다른 transaction에서 접근할 수 있음. 이 경우 consistent하지 않다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;commit되지 않은 data를 보는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;dirty read&lt;/b&gt;가 발생할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Level 2 (&lt;b&gt;Read&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Commited&lt;/b&gt;) : commit된 transaction의 결과만 조회할 수 있다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;하나의 transaction에 같은 query가 2개 이상 있을 때, 다른 결과가 나오는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;non repeatable read&lt;/b&gt;가 발생할 수 있다. query 실행 시점 사이에 다른 transaction이 2개 실행되면 이런 현상이 생긴다. (insert - update - insert의 경우)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Level 3 (&lt;b&gt;Repeatable&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Read&lt;/b&gt;) : 하나의 transaction에서 조회한 record가 같음을 보장한다. 하나의 transaction이 읽은 record를 다른 transaction이 변경하지 못하게 한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;MVCC를 사용해 transaction에서 사용하는 record들의 version을 관리한다. 때문에 select transaction 실행 중 다른 transaction이 update를 하더라도 MVCC가 저장하고 있는 record를 넘겨주며, 이 때문에 읽는 값은 같다.&lt;/li&gt;
&lt;li&gt;그러나 MVCC는 update는 versioning을 하지 않기 때문에, 같은 transaction을 2번 실행했을 때 결과가 다른&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;phantom&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;read&lt;/b&gt;가 발생할 수 있다. (select - insert - select의 경우)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Level 4 (&lt;b&gt;Serializable&lt;/b&gt;) : 한 transaction이 사용하는 record를 다른 transaction에서 접근할 수 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;ARIES Recovery&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;ARIES recovery의 가정 : strict 2 phase locking, WAL (write ahead logging)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DB에 접근하기 전 lock, transaction 끝난 후 unlock하는 것을 말한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Force / Steal&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Force: transaction commit 후 바로 disk에 쓰는 방식&lt;/li&gt;
&lt;li&gt;No Force : transaction commit 후 바로 disk에 쓰지 않는 방식&lt;/li&gt;
&lt;li&gt;Steal: transaction 완료 여부에 상관없이 data를 disk에 기록하는 방식&lt;/li&gt;
&lt;li&gt;No Steal: transaction uncommit 상태에서 data를 disk에 기록하지 않는 방식&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ARIES recovery의 경우 &lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;b&gt;Force + No Steal&lt;/b&gt; 방식으로, 어렵지만 빠르다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Commit, Rollback&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;commit은 모든 작업이 정상적으로 수행되었다는 명령이며, 실 DB에 반영하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;rollback은 crash가 난 경우, 해당 transaction의 변경을 취소하는 과정이다. 직전 commit까지만 복구한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이를 통해 consistency를 보장할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;WAL&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;disk에 update하기 전에 log를 작성한다. &lt;b&gt;undo&lt;/b&gt;를 위해 사용하며, disk에 update한 후 crash가 나면 undo할 수 없기 때문이다.&lt;/li&gt;
&lt;li&gt;transaciton이 commit되기 전에 모든 작업 내용을 log에 기록해야 한다. &lt;b&gt;redo&lt;/b&gt;를 위해 사용하며, 해당 transaction이&amp;nbsp; commit되었음을 보장한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;ARIES recovery algorithm&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;strict 2 phase lock을 사용하고 있기 때문에 data에 대한 접근은 걱정하지 않아도 된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;analysis : REDO 시작위치 결정 (checkpoint : transaction이 모두 disk에 쓰인 시점), crash 위치 결정&lt;/li&gt;
&lt;li&gt;redo : analysis에서 결정한 위치부터 crash 직전까지 redo 수행.&lt;/li&gt;
&lt;li&gt;undo : log를 역순으로 읽으면서 uncommit transaction까지 undo. (최근 commit까지)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Transaction State&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;크게 5가지 종류가 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;active : transaction이 실행 중인 상태&lt;/li&gt;
&lt;li&gt;failed : transaction에 오류가 발생해 중단된 상태&lt;/li&gt;
&lt;li&gt;partially commited : 사용자의 commit 요청이 왔을 때 도착하는 상태. 아직 반영된 상태가 아니다. commit 되면 commited로 가고, 그렇지 않으면 failed로 간다.&lt;/li&gt;
&lt;li&gt;commited : transaction이 성공적으로 종료된 상태&lt;/li&gt;
&lt;li&gt;aborted : rollback을 수행하고 있는 상태&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;잘못된 내용이나 오탈자에 대한 지적, 질문&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;b&gt;등은 언제나 환영합니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>CS/DB</category>
      <author>hyelie</author>
      <guid isPermaLink="true">https://hyelie.tistory.com/645</guid>
      <comments>https://hyelie.tistory.com/entry/DB-%EA%B8%B0%EC%B4%88-2#entry645comment</comments>
      <pubDate>Sun, 1 Oct 2023 04:11:49 +0900</pubDate>
    </item>
    <item>
      <title>DB 기초 - 1</title>
      <link>https://hyelie.tistory.com/entry/DB-%EA%B8%B0%EC%B4%88-1</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;이 글은 포스텍 한욱신 교수님의 데이터베이스시스템(CSED421) 강의를 기반으로 재구성한 것입니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;DBMS의 정의&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;DBMS란 &lt;b&gt;database를 관리/유지&lt;/b&gt;시켜주는 소프트웨어이다. 사용 이유는 data independency와 효율적 접근, 보안, 동시 접근을 위해서이다. file system에 비해서 cost는 크지만 redundancy가 없고, constraint를 유지할 수 있다는 장점이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Transction &amp;amp; ACID&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;transaction : &lt;b&gt;DBMS의 상호작용 단위&lt;/b&gt;. transaction은 다음 4가지 성질을 가지고 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Atomicity &lt;/b&gt;: transction은 실행되거나, 실행되지 않거나 둘 중 하나의 상태만 가진다. 중간에 끊기지 않는다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Consistency &lt;/b&gt;: transaction의 실행 결과는 항상 일관성이 있다. (정해둔 규칙을 위배하지 않는다.)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Isolation &lt;/b&gt;: transaction 사이에 다른 trasnaction이 낄 수 없다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Durability &lt;/b&gt;: DBMS가 꺼져도 수행된 transaction은 반영되어 있어야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Data Model &amp;amp; Schema&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;data model은 data를 묘사하기 위한 개념의 collection.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;schema는 data의 특정 collection의 설명을 말한다. data의 관계, 구조, 표현 방법을 표시하는 구조이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;schema는 크게 3가지 구조가 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;external schema (view) : 각 user가 접근하는 table&lt;/li&gt;
&lt;li&gt;conceptual schema (logical) : 어떤 종류의 data가 저장되는지&lt;/li&gt;
&lt;li&gt;physical schema (physical) : data가 어떻게 저장되는지&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Data Independence&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;physical data independence : physical structure를 바꾸어도 logical schema는 불변하는 특징&lt;/li&gt;
&lt;li&gt;logical data independence : logcial structure를 바꾸어도 external schema는 불변하는 특징&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Buffer Pool&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;OS에서 file을 byte sequence로 봤던 것처럼, DB는 file - record(tuple) - page 3개의 structure로 분리해 본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;DBMS는 disk에 있는 page를 memory에 frame으로 저장하는데, 이 공간을 &lt;b&gt;buffer pool&lt;/b&gt;이라 한다. OS의 paging과 매우 유사하지만 조금은 다르다. (DB는 prefetch 예측하기 때문)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Index - B+ tree&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;m-way balanced tree&lt;/b&gt; (각 node에는 &lt;b&gt;최대 m-1개의 element&lt;/b&gt;가 들어있고 각 node는 &lt;b&gt;최대 m개의 child&lt;/b&gt;를 가질 수 있다)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;balanced tree인 만큼, child의 height 차이가 1 이상 나지 않는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;모든 &lt;b&gt;leaf node는 doubly linked list&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;space utilization이 50% 이상&lt;/b&gt;이어야 한다.&amp;nbsp;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;leaf node는 $\left \lfloor \frac{n+1}{2} \right \rfloor$개의 pointer가 있어야 하고,&lt;/li&gt;
&lt;li&gt;non-leaf node는 $\left \lceil \frac{n+1}{2} \right \rceil$개의 pointer가 있어야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Group 3399.png&quot; data-origin-width=&quot;776&quot; data-origin-height=&quot;638&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cA8EE5/btswkl5Wd97/vPIVlDfnywLsaSGAsJtuT1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cA8EE5/btswkl5Wd97/vPIVlDfnywLsaSGAsJtuT1/img.png&quot; data-alt=&quot;B+ tree 노드 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cA8EE5/btswkl5Wd97/vPIVlDfnywLsaSGAsJtuT1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcA8EE5%2Fbtswkl5Wd97%2FvPIVlDfnywLsaSGAsJtuT1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;530&quot; height=&quot;436&quot; data-filename=&quot;Group 3399.png&quot; data-origin-width=&quot;776&quot; data-origin-height=&quot;638&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;B+ tree 노드 구조&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;삽입&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;simple case : 해당하는 key의 leaft node에 그냥 넣음&lt;/li&gt;
&lt;li&gt;leaf overflow가 발생하는 경우 : 반 쪼개어 parent에 push한다.&lt;/li&gt;
&lt;li&gt;non-leaf overflow가 발생하는 경우 : 쪼갬&lt;/li&gt;
&lt;li&gt;root overflow가 발생하는 경우 : 쪼갬&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;삭제&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;simple case : 해당하는 key를 삭제해도 utilization이 50% 이상인 경우, 그냥 삭제&lt;/li&gt;
&lt;li&gt;coalesce with sibiling : sibiling과 합치는 방법. 이전 leat node의 largest key를 가져오거나 이후 leaf node의 smallest key를 가져온다.&lt;/li&gt;
&lt;li&gt;redistribute key : key를 재조정해 utilization을 맞추는 방법. sibling과 parent 사이에서 key를 재조정한다.&lt;/li&gt;
&lt;li&gt;non-leaf에서 coalesce나 redistribute : 같은 방법을 취한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Index 쓸 때 장/단점&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;장점 : index에 있는 정보에 대해서는 read 속도가 빨라진다.&lt;/li&gt;
&lt;li&gt;단점 : insert/delete/update 속도가 느려진다. 해당 tree를 수정해야 하기 때문.&lt;/li&gt;
&lt;li&gt;자주 select되고, 덜 update되는 column에 대해 쓰는 것이 좋다. + FK나 join도.&lt;/li&gt;
&lt;li&gt;공통적으로 사용하는 것 - index column의 순서는 cardinality가 높은 것부터 낮은 순으로 나열하는 것이 좋다. (data의 unique 개수)&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Hash Table과의 차이점&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;hash table은 하나의 entry에 대해서는 O(1)이지만, range search를 하면 전체를 탐색해야 하기 때문에 O(n)이라는 단점이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Relational Algebra - JOIN&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;projection, selection 등 여러 가지 표기법이 있지만, 여기서는 join만 살펴보고자 한다. 제일 헷갈리니까.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;theta join&lt;/b&gt; (conditional join) R⋈$_c$S : &lt;b&gt;조건 c를 만족하는 tuple&lt;/b&gt;만 가져온다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;equi join&lt;/b&gt; : theta join에서 &lt;b&gt;조건 c가 equal로만&lt;/b&gt; 이뤄져 있는 theta join&lt;/li&gt;
&lt;li&gt;&lt;b&gt;natrual join&lt;/b&gt; R⋈S : &lt;b&gt;모든 공통 field에 대한 equi join&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Relational Operator Implement&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;SQL은 logical plan인데, 이것은 physical operator로 구현된다. 크게 &lt;b&gt;selection&lt;/b&gt;, &lt;b&gt;join&lt;/b&gt;만 신경쓰면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;cost는 CPU cost, I/O cost 2종류로 나뉘는데 CPU cost는 제하고, I/O cost만 본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;|R|$_p$를 &lt;b&gt;relation R에 해당하는 page 개수&lt;/b&gt;라고 정의하자. 그러면 I/O cost는 |R|$_p$로 나타낼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Selection - Sequential Scan&lt;/h3&gt;
&lt;pre id=&quot;code_1695991702090&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;for each page P in R:
    for each tuple t in P:
        if theta(t) return&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;pseudo code는 위와 같다. 어떤 relation R의 모든 page에 대해 조건을 만족하는 tuple만 return한다. 따라서 &lt;b&gt;cost는 $|R|_p$&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Selection - Index Scan&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;B+ tree로 구성된 index가 있고, 해당 index의 key로 검색할 수 있다면 index scan을 할 수 있다. key로 index를 탐색한 후, 그 결과에서 조건을 만족하는 tuple만 return하므로, &lt;b&gt;cost는 log|R|$_p$ + fetch cost&lt;/b&gt;이다. log항은 B+ tree로 구성된 index 탐색에 걸리는 시간, fetch cost는 index 탐색 결과 table의 크기이다. 이 때 fetch cost는 clustered 여부에 따라 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Clustered vs Non-Clustered Index&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;b&gt;clustered index&lt;/b&gt;는 B+ tree의 leaf node가 가리키는 &lt;b&gt;data page가 index 순서대로 정렬된 index&lt;/b&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;이고, &lt;/span&gt;&lt;b&gt;non-clustered index&lt;/b&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;는 그렇지 않은 index. (data page가 index 순서대로 정렬되어 있지 않음)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Index Scan I/O Cost&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;한 page에 b개 tuple이 있고, predicate의 실행 개수 cardinality를 card(P)로 두자.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;clustered&lt;/b&gt; : fetch time은 leaf node access time + card(P)개에 접근하는 시간인데, 이 때 card(P)개의 tuple은 index의 leaf node이기 때문에 연속적이다. 따라서 &lt;b&gt;fetch cost는 $\left \lceil \frac{card(P)}{B} \right \rceil + 1$&lt;/b&gt;이다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;non-clustered&lt;/b&gt; : not clustered인 경우, 각 leaf node는 key에 해당하는 data가 있는 page pointer가 있다. pointer는 worst case random이므로 &lt;b&gt;fetch cost는 card(P)&lt;/b&gt;라 상한을 둘 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;External Sort&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;page가 너무 커서 in-memory sort가 불가능한 경우 I/O cost가 어떻게 되는지 보자. 그냥 merge sort할 때 I/O cost가 얼마나 나오는지 생각하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;어떤 relation R의 sequence S를 &lt;b&gt;size B의 buffer pool을 사용해 정렬&lt;/b&gt;한다고 하자. 메모리가 부족하지 떄문에 아래와 같은 단계를 거쳐야 한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;buffer pool에 올릴 수 있는 만큼 S를 올린다.&lt;/li&gt;
&lt;li&gt;S를 정렬한다.&lt;/li&gt;
&lt;li&gt;S를 disk에 쓴다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;merge sort를 생각하며 이해해 보자. 한 번의 pass는 아래와 같은 알고리즘으로 수행된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;R을 page에 담고, 정렬한 후 다시 disk에 넣는다. 이 I/O cost는 &lt;span style=&quot;text-align: left;&quot;&gt;read/write를 수행하므로 cost는 $2|R|_p$이다.&lt;/span&gt; 한편 초기 run의 개수는 $\left \lceil \frac{|R|_p}{B} \right \rceil$개이다.&lt;/li&gt;
&lt;li&gt;page를 merge한다. 이 때, buffer pool에 B-1개는 정렬되지 않은 page를, 나머지 1개는 정렬한 후 disk write할 용도로 사용한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;여기서 &lt;b&gt;run은 합쳐야 하는 정렬된 group의 개수&lt;/b&gt;라고 생각하면 되고, &lt;b&gt;pass는 merge 회수&lt;/b&gt;라고 생각하면 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그러면 한 번의 pass에서 B-1개의 run이 줄어드므로 k번째 pass에서 merge할 때는 $\left \lceil \frac{|R|_p}{B(B-1)^k} \right \rceil$개의 run이 있을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&amp;nbsp;즉, 전체 pass의 개수는 $\left \lceil log_{B-1}\left \lceil \frac{|R|_p}{B} \right \rceil \right \rceil + 1$이고, 각 pass에서 run들이 정렬될 때마다 [R을 page에 담고, 정렬한 후 다시 disk에 넣는] 연산이 수행된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt; 따라서 &lt;b&gt;전체 I/O cost는 $2|R|_p \times (\left \lceil log_{B-1}\left \lceil \frac{|R|_p}{B} \right \rceil \right \rceil +&amp;nbsp; 1)$&lt;/b&gt;이다.&lt;/span&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;Join - Nested Loop Join&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;R join S라고 가정하자.&lt;/p&gt;
&lt;pre id=&quot;code_1696001543373&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;for tuple r in R:
    for tuple s in S:
        if r == s then add (r, s) to result&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&amp;nbsp;nested loop인 만큼 for문의 중첩으로 join하는 방식이다. 일단 R의 모든 page를 읽어야 하고, 이후 R의 모든 page에 대해 S의 page를 가져온다. I/O cost는 worst case로, &lt;/span&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&lt;b&gt;I/O cost는 $|R|_p + |R|_p \times |S|_p$&lt;/b&gt;이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&amp;nbsp;참고로 outer relation size에 의존하기 때문에 nested loop join의 경우 &lt;b&gt;outer loop에 더 작은 relation&lt;/b&gt;을 두면 좋다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;Join - Blocked Nested Join&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;b&gt;size B의 buffer pool을 사용해 join&lt;/b&gt;한다고 하자.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;일단 R의 모든 page를 읽어야 한다.&lt;/li&gt;
&lt;li&gt;이후 R의 page 중 B-2개를 먼저 buffer pool에 가져온다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;남는 2개의 공간 중 하나는 S의 page를, 나머지 하나는 join result를 위해 output page로 사용할 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;가져온 B-2개의 page에 대해 S의 모든 page에 대해 검사를 수행한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;따라서 &lt;b&gt;I/O cost는 $|R|_p + \left \lceil \frac{|R|_p}{B-2} \right \rceil \times |S|_p$&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;Join - Sort Merge Join&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&amp;nbsp;sort는 앞에서 다뤘으니 넘어가고, merge만 보자. (보통 정렬되어 있는 것을 sort merge join으로 많이 쓴다.)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&amp;nbsp;merge sort의 merge에 해당하는 시간복잡도가 O(n)인 것처럼, 여기서 R과 S는 정렬되어 있으므로 같은 방식으로 R의 모든 page와 S의 모든 page를 1번씩만 읽으면 된다. 따라서 &lt;/span&gt;&lt;b&gt;&lt;span style=&quot;text-align: start;&quot;&gt;I/O cost는&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&lt;b&gt;$|R|_p + |S|_p$&lt;/b&gt;이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;Join - Grace Hash Join&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&amp;nbsp;grace hash join 이외에도 simple hash join, hybrid hash join 등이 있지만 여기서는 grace hash join만 보겠다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;partition&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&amp;nbsp;R과 S의 모든 page를 B-2개의 page로 hashing하고, 그 결과를 buffer pool에 쓴다. 읽고 쓰기 때문에&lt;/span&gt; &lt;b&gt;partition cost는 $2|R|_p + 2|S|_p$&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;hash join&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;R의 page 하나, S의 page 하나를 buffer pool에 올리고 hashing된 page B-2개와 비교한다. 이 때 $R_i ⋈ S_j$에서 i != j면 empty이다. hash(k1) != hash(k2)면 k1 != k2이기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이렇게 R의 모든 page, S의 모든 page를 buffer pool에 쓰므로 이 과정의 cost는 &lt;b&gt;$|R|_p + |S|_p$&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;b&gt;총계&lt;/b&gt; &lt;b&gt;$3|R|_p + 3|S|_p$&lt;/b&gt;이다.&lt;span style=&quot;text-align: start;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;Query Optimization&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&amp;nbsp;query optimizer는 SQL을 효율적으로 실행할 수 있는 &lt;b&gt;plan&lt;/b&gt;을 세운다. 여기서 plan은 다음과 같은 과정을 거쳐 실행될 plan으로 바뀐다.&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;query를 &lt;b&gt;logical operator들의 tree&lt;/b&gt;로 표현하고, &lt;b&gt;logical equivalent rule을 적용해 tree를 바꾼다&lt;/b&gt;.&lt;/li&gt;
&lt;li&gt;앞서 살펴본 &lt;b&gt;physical operator를 적용해 physical plan&lt;/b&gt;으로 바꾼다.&lt;/li&gt;
&lt;li&gt;query optimizer는 plan의 실행 시간을 유추해서 &lt;b&gt;best plan을 도출&lt;/b&gt;한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;logical join에 대해 physical로 바꾸면 nested loop join, sort merge join, hash join로 표기될 수 있다.&lt;br /&gt;logical selection에 대해 physical로 바꾸면 seq scan이나 index scan으로 표기될 수 있다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&lt;span style=&quot;text-align: left;&quot;&gt;&amp;nbsp;실행 순서에 따라 실행 시간이 꽤 많이 바뀌는데,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt; 실행 시간을 유추할 때는 CPU cost와 I/O cost 2가지를 보며, cardinality 예측과 통계 활용 등의 방식을 사용한다. 물론 cardinality 예측이 틀렸거나 &lt;/span&gt;&lt;span style=&quot;text-align: start;&quot;&gt;통계가 잘못된 경우 등 여러 경우의 수에 의해 좋지 않은 plan이 결정될 수도 있다. query optimizer는 이를 개선하기 위해 query optimizer는 query executor로부터 실행 결과를 feedback받아 cost 추정과 통계를 갱신한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;잘못된 내용이나 오탈자에 대한 지적, 질문&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;b&gt;등은 언제나 환영합니다.&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>CS/DB</category>
      <author>hyelie</author>
      <guid isPermaLink="true">https://hyelie.tistory.com/642</guid>
      <comments>https://hyelie.tistory.com/entry/DB-%EA%B8%B0%EC%B4%88-1#entry642comment</comments>
      <pubDate>Sat, 30 Sep 2023 01:40:45 +0900</pubDate>
    </item>
    <item>
      <title>23.09.25. 풀었던 문제들</title>
      <link>https://hyelie.tistory.com/entry/230925-%ED%92%80%EC%97%88%EB%8D%98-%EB%AC%B8%EC%A0%9C%EB%93%A4</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Programmers Lv. 3 입국심사, 8분&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;parametric search.&lt;/p&gt;
&lt;pre id=&quot;code_1660456126668&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;typedef long long ll;

vector&amp;lt;int&amp;gt; times;

// t시간동안 심사할 수 있는 인원 수
ll calculateNumPass(ll t){
    ll num_pass = 0;
    for(int time : times){
        num_pass += ((ll) t / time);
    }
    return num_pass;
}

long long solution(int n, vector&amp;lt;int&amp;gt; t) {
    ll start = 0, end = 1e18;
    times = t;

    while(start &amp;lt; end){
        ll mid = (start + end) / 2;
        ll num_pass = calculateNumPass(mid);
        if(num_pass &amp;gt;= n){ // 시간을 더 줄일 수 있을 때
            end = mid;
        }
        else{ // 시간을 늘려야 할 때
            start = mid + 1;
        }
    }

    return start;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;시간복잡도&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;O(nlogn)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>PS/PS Log</category>
      <author>hyelie</author>
      <guid isPermaLink="true">https://hyelie.tistory.com/640</guid>
      <comments>https://hyelie.tistory.com/entry/230925-%ED%92%80%EC%97%88%EB%8D%98-%EB%AC%B8%EC%A0%9C%EB%93%A4#entry640comment</comments>
      <pubDate>Mon, 25 Sep 2023 16:01:12 +0900</pubDate>
    </item>
    <item>
      <title>23.09.24. 풀었던 문제들</title>
      <link>https://hyelie.tistory.com/entry/230924-%ED%92%80%EC%97%88%EB%8D%98-%EB%AC%B8%EC%A0%9C%EB%93%A4</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Programmers Lv. 3 보석 쇼핑, 19분&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;two-pointer를 활용하면 되는 문제. 유의할 점은 start와 end 사이에 있는 보석 개수를 세야 하는데, map으로 숫자를 세면 count에 O(보석 개수)만큼의 시간이 걸리고, multiset을 쓰면 identical한 보석 개수를 셀 수 없기 때문에 map과 set을 같이 썼다.&lt;/p&gt;
&lt;pre id=&quot;code_1660456126668&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;set&amp;lt;string&amp;gt; cur_gems;
int num;
map&amp;lt;string, int&amp;gt; cur_gem_count;

bool isContainAll(){
    return cur_gems.size() == num;
}

void insert(string gem){
    cur_gem_count[gem]++;
    cur_gems.insert(gem);
}

void erase(string gem){
    cur_gem_count[gem]--;
    if(cur_gem_count[gem] == 0) cur_gems.erase(gem);
}

vector&amp;lt;int&amp;gt; solution(vector&amp;lt;string&amp;gt; gems) {
    set&amp;lt;string&amp;gt; s;
    for(string gem : gems){
        s.insert(gem);
        cur_gem_count[gem] = 0;
    }
    num = s.size();

    int start = 0, end = 0;
    insert(gems[start]);
    vector&amp;lt;int&amp;gt; answer = {-1, 100001};
    while(start &amp;lt; gems.size() &amp;amp;&amp;amp; end &amp;lt; gems.size()){
        if(start &amp;gt; end){
            end = start;
            continue;
        }

        if(isContainAll()){
            if(answer[1] - answer[0] + 1 &amp;gt; end - start + 1){
                answer[1] = end + 1;
                answer[0] = start + 1;
            }
            erase(gems[start]);
            start++;
        }
        else{
            end++;
            if(end &amp;lt; gems.size()) insert(gems[end]);
        }


    }


    return answer;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;시간복잡도&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;set/map에 insert/pop 시 O(logn)이고, two-pointer가 순회하는 데 걸리는 시간은 O(n)이므로, O(nlogn)이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Programmers Lv. 3 가장 먼 노드, 8분&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;weight가 1보다 크다면 dijkstra를 써야 하지만, 모든 edge weight를 1로 두기 때문에 BFS를 쓰면 되는 문제. 뭐.. 별 것 없다.&lt;/p&gt;
&lt;pre id=&quot;code_1660456126668&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;typedef pair&amp;lt;int, int&amp;gt; pii; // .first : to, .second : dist

queue&amp;lt;pii&amp;gt; q;
vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; edges;

int solution(int n, vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; edge) {
    vector&amp;lt;int&amp;gt; visited(n+1, -1);
    edges.resize(n+1);
    for(vector&amp;lt;int&amp;gt; e : edge){
        int from = e[0], to = e[1];
        edges[from].push_back(to);
        edges[to].push_back(from);
    }

    q.push({1, 0});
    visited[1] = 0;

    int max_value = -1;
    while(!q.empty()){
        pii front = q.front(); q.pop();

        for(int next : edges[front.first]){
            if(visited[next] != -1) continue;

            visited[next] = front.second + 1;
            q.push({next, front.second + 1});
            max_value = max(max_value, visited[next]);
        }
    }

    int answer = 0;
    for(int v : visited) if(v == max_value) answer++;
    return answer;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;시간복잡도&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;BFS는 O(V+E)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Programmers Lv. 3 섬 연결하기, 7분 30초&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;MST를 구성하는 문제. 예전에 &lt;a href=&quot;https://hyelie.tistory.com/entry/%EA%B7%B8%EB%9E%98%ED%94%84-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-5-Minimum-Spanning-TreeKruskal-Prim&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;포스팅&lt;/a&gt;도 했었다. union-find를 쓰는 kruskal과 pq를 쓰는 prim 2가지 방법이 있는데,&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1660456126668&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;typedef pair&amp;lt;int, int&amp;gt; pii;

struct cmp{
    bool operator()(pii &amp;amp;a, pii &amp;amp;b){
        if(a.second == b.second) return a.first &amp;gt; b.first;
        return a.second &amp;gt; b.second;
    }
};

priority_queue&amp;lt;pii, vector&amp;lt;pii&amp;gt;, cmp&amp;gt; pq;
vector&amp;lt;vector&amp;lt;pii&amp;gt;&amp;gt; edges;
vector&amp;lt;int&amp;gt; visited;

int solution(int n, vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; costs) {
    edges.resize(n);
    visited.resize(n);
    fill(visited.begin(), visited.end(), false);
    for(vector&amp;lt;int&amp;gt; cost : costs){
        int from = cost[0], to = cost[1], weight = cost[2];
        edges[from].push_back({to, weight});
        edges[to].push_back({from, weight});
    }

    // prim
    visited[0] = true;
    for(pii e : edges[0]) pq.push(e);

    int answer = 0;
    while(!pq.empty()){
        pii front = pq.top(); pq.pop();
        if(visited[front.first]) continue;

        visited[front.first] = true;
        answer += front.second;
        for(pii e : edges[front.first]) pq.push(e);
    }

    return answer;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;시간복잡도&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;prim의 경우 O(ElogV)이지만 이 코드는 O(ElogE)이다. 기존 prim은 set을 사용해서 visited를 판별하기 때문에 O(ElogV)이다. 반면 여기서는 모든 edge에 대해 edge를 뽑는 for문이 1번씩 수행되므로 O(E), 그리고 pq에 모든 edge가 들어가므로 O(ElogE)이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>PS/PS Log</category>
      <author>hyelie</author>
      <guid isPermaLink="true">https://hyelie.tistory.com/639</guid>
      <comments>https://hyelie.tistory.com/entry/230924-%ED%92%80%EC%97%88%EB%8D%98-%EB%AC%B8%EC%A0%9C%EB%93%A4#entry639comment</comments>
      <pubDate>Mon, 25 Sep 2023 13:23:18 +0900</pubDate>
    </item>
    <item>
      <title>23.09.23. 풀었던 문제들</title>
      <link>https://hyelie.tistory.com/entry/230923-%ED%92%80%EC%97%88%EB%8D%98-%EB%AC%B8%EC%A0%9C%EB%93%A4</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Programmers Lv. 3 불량 사용자, 28분&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;문제 자체는 주어진 것만 풀면 되는 문제. input size가 8이므로 최대 8!의 시간 복잡도이며, 따라서 backtrack(순열)로 풀면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;단, 유의할 점은 `제재 아이디 목록을 구했을 때 아이디들이 나열된 순서와 상관없이 아이디 목록의 내용이 동일하면 같은 것으로 처리한다`이기 때문에, 이를 잘 처리해야 한다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&amp;nbsp;모든&amp;nbsp;banned_id에&amp;nbsp;대해&amp;nbsp;가능한&amp;nbsp;user_id의&amp;nbsp;후보군들을&amp;nbsp;나열하고,&lt;/li&gt;
&lt;li&gt;permutation/backtrack으로 가능한 모든 조합을 나열하고,&lt;/li&gt;
&lt;li&gt;해당 조합에서 겹치는 것을 빼면 되겠네.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위 3가지 flow로 쉽게 처리할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1660456126668&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// banned_id와 user_id가 일치하는지
bool isBanned(string banned_id, string user_id){
    if(banned_id.length() != user_id.length()) return false;
    
    int len = banned_id.length();
    for(int i = 0; i&amp;lt;len; i++){
        if(banned_id[i] == user_id[i] || banned_id[i] == '*') continue;
        // 실수 1. banned_id[i] == '*'로 했어야 했는데 user_id[i] == '*'로 했다.
        else return false;
    }
    return true;
}

set&amp;lt;string&amp;gt; answer;
unordered_map&amp;lt;string, bool&amp;gt; visited; // visited[i] : user_id[i]를 사용했는지 여부. true then used
vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; candidates; // candidites[i] : banned_id[i]가 적용될 수 있는 user_id index vector
vector&amp;lt;string&amp;gt; user_ids, banned_ids;

void backtrack(int cur_depth, int max_depth, string result){
    if(cur_depth == max_depth){
        sort(result.begin(), result.end());
        answer.insert(result);
        return;
    }
    
    for(int i = 0; i&amp;lt;candidates[cur_depth].size(); i++){
        int user_idx = candidates[cur_depth][i]; // 실수 4. user_idx를 i로 넣었다.
        string s = user_ids[user_idx];
        if(!visited[s]){ // 실수 2. visited 로직을 뺐다.
            visited[s] = true;
            backtrack(cur_depth + 1, max_depth, result + to_string(user_idx)); // 실수 5. user_idx가 아니라 i로 넣었다.
            visited[s] = false;
        }
        
    }
    return;
}

int solution(vector&amp;lt;string&amp;gt; uids, vector&amp;lt;string&amp;gt; bids) {
    // init
    user_ids = uids;
    int user_size = user_ids.size();
    for(string user_id : user_ids){
        visited[user_id] = false;
    }
    
    banned_ids = bids; // 실수 3. uid로 넣었다.
    int banned_size = banned_ids.size();
    candidates.resize(banned_size);
    
    // init candidates
    for(int i = 0; i&amp;lt;banned_size; i++){
        for(int j = 0; j&amp;lt;user_size; j++){
            if(isBanned(banned_ids[i], user_ids[j])){
                candidates[i].push_back(j);
            }
        }
    }
    
    backtrack(0, banned_size, &quot;&quot;);
    
    return answer.size();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;시간복잡도&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;O(8!)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;후기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;오랜만에 풀어서? 그런가, 너무 급하게 풀려 해서 그런가, 실수가 너무 많았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>PS/PS Log</category>
      <author>hyelie</author>
      <guid isPermaLink="true">https://hyelie.tistory.com/638</guid>
      <comments>https://hyelie.tistory.com/entry/230923-%ED%92%80%EC%97%88%EB%8D%98-%EB%AC%B8%EC%A0%9C%EB%93%A4#entry638comment</comments>
      <pubDate>Mon, 25 Sep 2023 03:02:36 +0900</pubDate>
    </item>
    <item>
      <title>23.09.20. 취준계획 - 끝!</title>
      <link>https://hyelie.tistory.com/entry/230920-%EC%B7%A8%EC%A4%80%EA%B3%84%ED%9A%8D</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;진짜 너무너무 바쁘다. 수업 과제는 뭐... 받자마자 하루컷 낸다 쳐도, 나머지가 끊임없이 이어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;일단 쓰고 싶은 데는 다 쓰는 게 맞는 것 같다.어차피 막학기 아니니까 여유 가져도 되긴 하는데, 미리 한 번 겪어보고 싶다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;근데 말도 안되게 바쁜게, 졸업과제 &amp;larr; 얘가 진짜 너무 트롤이다. 1학점짜리가 아니라 한 5-6 되는 듯. 지금,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;자소서&lt;/b&gt;도 써야하고&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;코테&lt;/b&gt;공부도 해야하고&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;면접&lt;/b&gt;공부도 해야 하는데... 근데&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;과제연구를 주에 적어도 6시간은 써야 한다&lt;/b&gt;. SD 과제도 있고... 병렬컴퓨팅 과제도 있고.. 시험기간이 되면 교양 레포트도 나오고... 크아아아악. 끔찍하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;취준&amp;nbsp;계획&amp;nbsp;-&amp;nbsp;코테&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;일단 내가 쓴 곳들 중 코테를 보는 곳이 [&lt;s&gt;LG CNS&lt;/s&gt;完,&amp;nbsp;&lt;s&gt;넥토리얼&lt;/s&gt;, &lt;s&gt;베이글코드&lt;/s&gt;完] 이렇게 3군데이다. - 다 끝났다. 이제 코테 공부는 다음 이직 때....&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;코테 공부랑 면접 공부랑 병행하는 건 가능하지만, [수업을 들으면서 + 과제를 하면서 + 코테 공부를 하면서 + 면접 공부를 하는] 건 물리적으로 &lt;b&gt;불가능&lt;/b&gt;하기 때문에, 일단은 [수업을 들으면서 + 과제를 하면서 + 코테 공부를 하면서] 일단은 코테를 보는 곳들에 당장 지원을 해서 코테를 보는 기업들이 끝날 때까지 &lt;b&gt;주구장창 코테 공부만 일단&lt;/b&gt; 해 보자. 프로그래머스 Lv. 3가 한 60문제 남았으니, 하루에 4문제씩 푼다 치면 얼추 코테 치는 기간이랑 겹칠 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;취준&amp;nbsp;계획&amp;nbsp;-&amp;nbsp;면접공부&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그렇게, 어떻게 어떻게 코테를 패스하면 면접이 다가온다. 면접... 시험... 일단 [코테를 패스한 곳들 + 면접을 봐야하는 곳들]을 위주로 면접 준비를 해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;CS&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;OOP, 알고리즘, OS, 네트워크는 블로그에 있는 것들만 복습하면 되고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;DB는 추가로 정리해야 한다. 어.. 이게 거의 7일쯤 걸리지 않을까? 중요한 개념인 B+ tree, relational operation, ERD, recovery 정도만 해도 한 5일이니까... 흠. - 이거는 끝났다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;자료구조 hash랑 tree쪽 단골이니까 한 번 봐 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;java를 사용하는 곳이라면 java, spring 정도 더 채워야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;프로젝트&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;내가 했던 경험 + 프로젝트 깔끔하게 설명할 수 있을 정도로 준비해 가야 할 것이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SW개발병 + 미어캣&lt;/li&gt;
&lt;li&gt;소마&amp;nbsp;+&amp;nbsp;비즈킥스&lt;/li&gt;
&lt;li&gt;Naver2Tistory&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그래.. 이렇게 면접 준비를 한다 치자. 중요한 건, &lt;b&gt;시험 기간이랑 면접 기간이랑 겹친다&lt;/b&gt;. 크아아악 다행히 시험 보는 과목은 SD랑 병렬컴퓨팅만 보면 되니까 그건 다행이다. ㅠ-ㅠ 진짜 너무너무 할게 많다. 파이팅하자,,,,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>내가 하고싶은 것!/Plan</category>
      <author>hyelie</author>
      <guid isPermaLink="true">https://hyelie.tistory.com/637</guid>
      <comments>https://hyelie.tistory.com/entry/230920-%EC%B7%A8%EC%A4%80%EA%B3%84%ED%9A%8D#entry637comment</comments>
      <pubDate>Wed, 20 Sep 2023 00:49:30 +0900</pubDate>
    </item>
    <item>
      <title>23.09.19. 풀었던 문제들</title>
      <link>https://hyelie.tistory.com/entry/230919-%ED%92%80%EC%97%88%EB%8D%98-%EB%AC%B8%EC%A0%9C%EB%93%A4</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Programmers Lv. 3&lt;span&gt;&amp;nbsp;&lt;/span&gt;여행경로, 17분&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그냥 DFS 돌리면 되는 문제. 단 각각의 vertex가 string으로 표현되는 점과 [사용한 표]를 어떻게 표기할지에 대해 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;유의해야 한다. 뭐.. 정석 DFS처럼 각 ticket의 index를 visit했는지 안했는지를 표기할 수 있긴 한데. 결국 alphabet 순서로 DFS + edges를 만들어야 하기 때문에 map을 사용해야 할 것 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;nbsp;처음에는 multiset을 썼었는데, DFS를 위해 multiset에서 element를 빼고 넣는 과정에서 계속 같은 위치를 참조하는 것 같았다. (무한루프) 그래서 map of map을 썼다. edges[i]는 i에 인접한 공항들의 map 목록이고, 이것은 [도착지 이름, 같은 표가 몇 개인지]를 나타낸다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;nbsp;나머지는 뭐.. 코드 보면 이해하기 쉬울 것 같다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1660456126668&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int max_depth;
map&amp;lt;string, map&amp;lt;string, int&amp;gt;&amp;gt; edges; // edges[i] : i에 인접한 공항들
vector&amp;lt;string&amp;gt; result;

bool DFS(int cur_depth, string cur_airport){
    if(cur_depth == max_depth){
        return true;
    }
    
    for(auto &amp;amp;[name, count] : edges[cur_airport]){
        if(count == 0) continue;
        edges[cur_airport][name]--;
        result[cur_depth + 1] = name;
        
        bool hasAnswer = DFS(cur_depth+1, name);
        if(hasAnswer) return true;
        
        edges[cur_airport][name]++;
    }
    return false;
}

vector&amp;lt;string&amp;gt; solution(vector&amp;lt;vector&amp;lt;string&amp;gt;&amp;gt; tickets) {
    max_depth = (int) tickets.size();
    for(vector&amp;lt;string&amp;gt; ticket : tickets){
        string src = ticket[0], dest = ticket[1];
        edges[src][dest]++; 
    }
    
    
    
    result.resize(max_depth + 1);
    result[0] = &quot;ICN&quot;;
    
    DFS(0, &quot;ICN&quot;);
    
    return result;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;시간복잡도&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;ticket size를 n이라 하면, edge에 넣는 데 O(loglogn), 꺼내는 데도 O(loglogn)이다. 어떤 출발지로부터 도착지 목록을 가져오는 데는 O(logn)이 걸리고, 이를 순회하는 데 O(nlogn)이 걸린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;DFS 자체는, vertex가 O(n)이므로 O(n)이다. 따라서 O(nlogn).&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;후기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;참고로 예전에 풀었던 코드는 다음과 같다. 각 DFS에서 모든 ticket을 보기 때문에 시간이 너무 오래 걸릴 것이다! input이 작아 해결되지만 input이 큰 경우는 해결되지 않을 것. 그리고 코드 자체도 훨씬 간단하다.&lt;/p&gt;
&lt;pre id=&quot;code_1695130297795&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;vector&amp;lt;string&amp;gt; answer;

bool cmp(vector&amp;lt;string&amp;gt; &amp;amp;a, vector&amp;lt;string&amp;gt;&amp;amp;b){
    for(int i = 0; i&amp;lt;a.size(); i++){
        if(a[i] &amp;lt; b[i]) return true;
        else if(a[i] &amp;gt; b[i]) return false;
        else continue;
    }
}

void DFS(int depth, int max_depth, string dep, vector&amp;lt;bool&amp;gt;&amp;amp; visited, vector&amp;lt;vector&amp;lt;string&amp;gt;&amp;gt;&amp;amp; tickets, vector&amp;lt;string&amp;gt;&amp;amp; result){
    if(depth == max_depth){
        if(answer.empty()) answer = result;
        answer = cmp(answer, result)? answer : result;
        return;
    }
    
    for(int i = 0; i&amp;lt;max_depth; i++){
        if(tickets[i][0] == dep &amp;amp;&amp;amp; !visited[i]){
            visited[i] = true;
            result[depth + 1] = tickets[i][1];  
            DFS(depth+1, max_depth, tickets[i][1], visited, tickets, result);
            visited[i] = false;
        }
    }
}

vector&amp;lt;string&amp;gt; solution(vector&amp;lt;vector&amp;lt;string&amp;gt;&amp;gt; tickets) {
    int size = tickets.size();
    vector&amp;lt;string&amp;gt; result(size + 1); result[0] = &quot;ICN&quot;;
    vector&amp;lt;bool&amp;gt; visited(size, false);
    DFS(0, tickets.size(), &quot;ICN&quot;, visited, tickets, result);
    return answer;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>PS/PS Log</category>
      <author>hyelie</author>
      <guid isPermaLink="true">https://hyelie.tistory.com/636</guid>
      <comments>https://hyelie.tistory.com/entry/230919-%ED%92%80%EC%97%88%EB%8D%98-%EB%AC%B8%EC%A0%9C%EB%93%A4#entry636comment</comments>
      <pubDate>Tue, 19 Sep 2023 22:35:38 +0900</pubDate>
    </item>
    <item>
      <title>[Model Checking] 과제연구 주제 정리</title>
      <link>https://hyelie.tistory.com/entry/Model-Checking-%EA%B3%BC%EC%A0%9C%EC%97%B0%EA%B5%AC-%EC%A3%BC%EC%A0%9C-%EC%A0%95%EB%A6%AC</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;지도교수님과 여러 가지 이야기를 나눴고, [&lt;b&gt;특정 모델 체킹 언어를 사용해, 특정 시스템의 모델 체킹을 해 보기&lt;/b&gt;]를 주제로 과제연구를 진행하기로 했다. 대충 flow는 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;특정한 model checking 언어를 공부한다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;어떤 system을 체킹할 지 확인하기
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;distributed key-value store (cloud DB)&lt;/li&gt;
&lt;li&gt;RAFT consensus algorithm&lt;/li&gt;
&lt;li&gt;위 2개를 추천받았는데, 모델링하기 위해서는 해당 system의 동작 방식을 알아야 한다. 일단 백엔드로 갈 거니까 분산 DB를 공부하는 게 좋아보인다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;safety, fairness 등 어떤 property를 모델링할지 결정하기&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Model Checking 언어 공부&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;model checking tool은 몇 가지가 있는데, SV 랩에서 현재 사용하고 있는 maude를 공부하기로 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Maude의 사용 방법은 &lt;a href=&quot;https://link.springer.com/book/10.1007/978-1-4471-6687-0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Designing Reliable Distributed Systems&lt;/a&gt;를 추천받았고, 해당 글을 읽고 정리할 예정이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;2장, 3장, 8장, 9장을 일단 보면 된다.&lt;/li&gt;
&lt;li&gt;4장, 5장, 6장, 7장은 skip할 예정이다.&lt;/li&gt;
&lt;li&gt;이후 concurrent를 다루는 10장, communication을 다루는 11장, transport protocol(통신)을 다루는 12장, distributed algorithm을 다루는 13장 중 원하는 system에 해당하는 것을 보면 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;어떤 system을 체킹할 지 결정하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;구글에 `model checking distributed key value store`나 `model checking consensus algorithm`&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;어떤 property를 모델링할지 결정하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이건 system을 결정하면 따라오는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;참고&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;https://www.youtube.com/watch?v=nH4qjmP2KEE&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Project/Model Checking</category>
      <author>hyelie</author>
      <guid isPermaLink="true">https://hyelie.tistory.com/635</guid>
      <comments>https://hyelie.tistory.com/entry/Model-Checking-%EA%B3%BC%EC%A0%9C%EC%97%B0%EA%B5%AC-%EC%A3%BC%EC%A0%9C-%EC%A0%95%EB%A6%AC#entry635comment</comments>
      <pubDate>Tue, 19 Sep 2023 21:51:47 +0900</pubDate>
    </item>
    <item>
      <title>[Model Checking] Linear Time Properties</title>
      <link>https://hyelie.tistory.com/entry/Model-Checking-Linear-Time-Properties</link>
      <description>&lt;blockquote style=&quot;text-align: left;&quot; data-ke-style=&quot;style2&quot;&gt;이 글은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://www.youtube.com/playlist?list=PLwabKnOFhE38C0o6z_bhlF_uOUlblDTjh&quot;&gt;RWTH AACHEN 대학교 Joost-Pieter Katoen 교수님의 2018년 1학기 Introduction to Model Checking 강의&lt;/a&gt;와&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Principles_of_Model_Checking&quot;&gt;Principles of Model Checking&lt;/a&gt;을 기반으로 재구성한 것입니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;model checking의 주된 알고리즘은 transition system과 requirement를 model checker에 넣어, 해당 transition system이 requirement를 만족하는지 여부를 확인하는 방법이다.&lt;span style=&quot;letter-spacing: 0px;&quot;&gt; 그래서 지금까지 transition system을 모델링하는 방법을 살펴봤었고, 지금부터는 requirement를 모델링하는 방법을 살펴볼 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&amp;nbsp;이 포스트에서는 크게 4가지를 살핀다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;state-based and linear time view&lt;/li&gt;
&lt;li&gt;definition of linear time properties&lt;/li&gt;
&lt;li&gt;invariants and safety&lt;/li&gt;
&lt;li&gt;liveness and fairness&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;State Graph&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;transition system TS는 (S, Act, &amp;rarr; I, AP, L)의 tuple로 표기한다. 여기서 Act는 interaction, communication을 modeling하고 fairness를 설명할 때 쓴다. 반면 AP와 L은 properties를 정의할 때 쓴다. 따라서 일단 properties를 다룰 때는 &lt;b&gt;labeling과 state&lt;/b&gt;만 일단 신경쓴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Definition. State Graph&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;state graph G$_{T}$를 정의하자. transition system TS가 (S, Act, &amp;rarr; I, AP, L)일 때, G$_{T}$의 node는 S와 동일하고, edge는 action label이 없는 transition이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Definition. Path Fragment&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;a href=&quot;https://hyelie.tistory.com/entry/Model-Checking-Transition-System%EA%B3%BC-Program-Graph&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;이전 포스팅&lt;/a&gt;에서 execution fragment, initial execution fragment, maximal execution fragment에 대해 살펴봤었다. path fragment는 이와 유사한 개념으로 &lt;span style=&quot;letter-spacing: 0px;&quot;&gt;graph에서 정의되는 execution fragment라 생각하면 된다. TS를 G$_{T}$로 바꿨던 것처럼 execution fragment는 $\pi$=$s_0s_1s_2$...such that s$_{i+1}$ &amp;isin; Post(s$_i$) for all i in |$\pi$|로 정의한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;initial path fragment는 s$_0$ &amp;isin;S$_0$인 경우를 말한다. (initial state에서 시작한다는 뜻이다.)&lt;/li&gt;
&lt;li&gt;maximal path fragment는 infinite거나 terminal state에서 끝나는 path fragment를 말한다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;transition system execution이 initial &amp;amp; maximal execution fragment였던 것처럼, 동일하게 &lt;b&gt;transition system path는 initial &amp;amp; maximal path fragment&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;추가로, 편의를 위해 어떤 state s의 path를 s에서 시작하는 maximal path fragment라 정의하자. 또한 &lt;b&gt;Paths(T)는 transition system의 가능한 모든 path의 집합&lt;/b&gt;을, &lt;b&gt;Paths(s)는 state s에서 시작하는 모든 maximal path fragment들의 집합&lt;/b&gt;이라 정의하자. &lt;b&gt;Paths$_{\text{fin}}$(s)는 s에서 시작하는 모든 finite path fragment $\hat{\pi}$들의 집합&lt;/b&gt;이라 정의하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Linear Time View vs Branching Time View&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;바로 위에서 transition system T를 state graph와 state에 대한 labeling으로 나누었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;linear time view는 path-based이다. state sequence만 보면 모든 것을 알 수 있다는 뜻이다. branching의 경우, 각 branching의 결과를 state sequence로 볼 수 있기 때문에 branching과 관련이 없다. action을 abstract하고 AP에 투영한다. 이를 trace라 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;branching time view는 nondeterministic branch에 대해 설명하므로 state와 branch를 abstraction한다. 이를 computation tree라 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Trace&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;path는&amp;nbsp;$\pi$=$s_0s_1s_2$였는데, 이 &lt;b&gt;path의 각 state에 대한 atomic proposition을 적용한 L(s$_0$)L(s$_1$)L(s$_2$)를 trace&lt;/b&gt;라 한다. trace는&amp;nbsp;labeling의 sequence이므로 ($2^{\text{AP}}$)$^{\omega}$ &amp;cup; ($2^{\text{AP}}$)$^{+}$에 속한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;개선: Terminal State 삭제&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이러한 표현은 식이 너무 더럽기 때문에 TS에 terminal state가 없다고 가정하기도 한다. terminal state가 없다면 모든 경우에서 finite에 대한 계산을 없앨 수 있다. (모든 경우가 infinite)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;기존 terminal state에서 자기 자신으로 가는 edge 하나를 추가하는 방식으로 terminal state를 없앤다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예를 들어, 프로그램이 의도했던 종료 상태 s로 가는 transition system을 생각해 보자. 모든 reachable terminal state s에 대해 특별한 stop state로 edge를 추가해 기존 s를 terminal state가 아니게 하고, stop state는 자기 자신으로 edge를 추가하면 terminal state가 없어졌으므로 path가 infinite하게 된다.&lt;/li&gt;
&lt;li&gt;다른 경우는 system fault로 인한 부분이다. 이 경우는 terminal이 되기를 원하지 않지만 system fault나 deadlock에 의해 멈추는 경우는 프로그램의 설계가 잘못되었으므로 이를 수정해야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;b&gt;Traces(T)&lt;/b&gt;를 &lt;b&gt;transition system T의 모든 path $\pi$들의 trace의 합집합&lt;/b&gt;이라고 하자. 수식으로 정의하면 Traces(T) := {traces($\pi$) : $\pi$ &amp;isin; Paths(T)}이고, 이 때 &lt;b&gt;$\phi$는 initial &amp;amp; maximal path fragment&lt;/b&gt;이다. Traces(T) &amp;sube; ($2^{\text{AP}}$)$^{\omega}$이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;같은 방법으로 &lt;b&gt;&lt;span style=&quot;text-align: start;&quot;&gt;Traces$_\text{fin}$(T) := {traces($\hat{\pi}$) : $\hat{\pi}$&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&amp;isin; Paths$_\text{fin}$(T)}&lt;/b&gt;라고 정의하자. 이 때 &lt;b&gt;$\hat{\pi}$는 initial &amp;amp; finite path fragment&lt;/b&gt;이다. Traces$_\text{fin}$(T) &lt;span style=&quot;text-align: start;&quot;&gt;&amp;sube;&lt;span&gt; (&lt;/span&gt;&lt;/span&gt;$2^{\text{AP}}$)$^{*}$이다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&amp;nbsp;만약 terminal state를 삭제한 경우에는 무조건 infinite가 된다는 점을 유의하자.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Linear Time Property&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;앞서 model checker는 transition system T와 requirement를 구체화한 spec으로 T가 spec을 만족하는지 여부를 확인하는 기계라고 했다. 여기서 spec중 하나가 linear time property이다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;atomic propisition의 집합 AP에 대한 linear time property는 &lt;b&gt;($2^{\text{AP}}$)$^{\omega}$의 부분집합&lt;/b&gt;이다. 즉, state의 labeling의 infinite word의 부분집합이라는 뜻이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Definition. Satisfaction Relation for Linear Time Property&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;transition system이 언제 linear time property를 만족하는지 알 수 있어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;어떤 transition system TS와, TS의 atomic proposition AP, AP의 linear proposition E에 대해 &lt;b&gt;Traces(T) &amp;sube; E iff TS $\models$ E&lt;/b&gt;이다. transition system의 모든 path의 trace가 E에 속할 때, transition system TS가 E를 만족하고 그 역도 성립한다는 것을 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;state에 대해서도 이를 정의하는데, s가 TS의 state일 때 &lt;b&gt;Traces(s) &amp;sube; E iff s $\models$ E&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Definition. Trace Inclusion and Linear Time Properties&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;T$_1$과 T$_2$가 AP에 대한 transition system이고, AP에 대한 linear time property가 E에 대해 &lt;b&gt;Traces(T$_1$) &amp;sube; Traces(T$_2$) iff T$_2$ $\models$ E then T$_1$ $\models$ E&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;trace inclusion은 아래 3가지 영역에서 나타난다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;implementation / refinement relation : 예를 들어 어떤 transition system T$_i$가 LT property E를 만족한다고 했을 때, T$_i$를 조금 수정한 T$_{i+1}$이 있다고 하자. 이 때 T$_{i+1}$&amp;nbsp;&amp;sube; T$_i$라면 T$_{i+1}$도 E를 만족한다는 것을 보일 수 있다.&lt;/li&gt;
&lt;li&gt;resolving nodeterminisim : 예를 들어 어떤 scheduling policy에서 nondeterministic한 선택을 하는 T에서 deterministic 한 선택을 하는 T'을 만들었다고 하자. 이 때 Traces(T') &amp;sube; Traces(T)이므로 T가 E를 만족하면 T'도 E를 만족하는 것을 보일 수 있다.&lt;/li&gt;
&lt;li&gt;data abstraction : 원래 transition system T의 data를 추상화해 transition system T'를 만들었다고 하자. T'가 더 상위의 개념이므로 T의 모든 data들은 T'를 만족한다. &lt;span style=&quot;text-align: left;&quot;&gt;Traces(T)&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&amp;sube; Traces(T')이므로, T'가 E를 만족하면 T도 E를 만족한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Definition. Trace Equivalance&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;같은 atomic proposition AP에 대한 transition system T$_1$, T$_2$가 있을 때 &lt;b&gt;Traces(T$_1$) = Traces(T$_2$) iff trace equivalance&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;앞선 Trace Inclusion and LT Properties에 의해 transition system이 trace equivalent하면 동일한 linear time property를 만족한다. T$_1$과 T$_2$가 AP에 대한 transition system이고, AP에 대한 linear time property가 E에 대해 &lt;b&gt;Traces(T$_1$) = Traces(T$_2$) iff 모든 LT property E에 대해 T$_2$ $\models$ E then T$_1$ $\models$ E&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Safety Properties and Invariants&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그럼 이제 linear time property를 분류하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;b&gt;safety properties&lt;/b&gt;는 `&lt;b&gt;나쁜 상태가 일어나지 않는 것&lt;/b&gt;`을, &lt;b&gt;liveness properties&lt;/b&gt;는 `&lt;b&gt;좋은 상태가 일어나는 것&lt;/b&gt;`을 의미한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;safety properties 예시 : deadlock이 일어나지 않는 것. critical section에 한 번에 2개 이상의 process가 접근하지 않는 것. 신호등의 red는 yellow가 선행한다. 등등.&lt;/li&gt;
&lt;li&gt;liveness properties 예시 : wait 상태에 들어간 process는 언젠가 critical section에 진입하는 것.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 때 safety properties 중 special case가 &lt;b&gt;invariants&lt;/b&gt;이며, &lt;b&gt;어떤 나쁜 state에 도달하지 않는 것&lt;/b&gt;을 의미한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Invariant&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;AP에 대한 LT property E가 다음 조건을 만족하는 propositional fomula $\phi$가 존재할 때 E를 invariant라 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;E = {A$_0$A$_1$A$_2$... &amp;isin; ($2^{\text{AP}}$)$^{\omega}$ : $\forall$ i &amp;ge; 0. A$_i$ $\models$ $\phi$}&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 때 $\phi$를 E의 invariant condition이라 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 수식이 의미하는 것은 모든 trace의 모든 node가 $\phi$를 만족할 때, 이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 때, invariant E에 대해 T $\models$ E와 다음 3가지는 동치이다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;iff&lt;/b&gt; trace($\phi$) &amp;isin; E for all path $\phi$ &amp;isin; Paths(T)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;iff&lt;/b&gt; s $\models$ $\phi$ for all states s on a path of T&lt;/li&gt;
&lt;li&gt;&lt;b&gt;iff&lt;/b&gt; s $\models$ $\phi$ for all states s &amp;isin; Reach(T)&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;의미하는 것은 다음과 같다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;transition system T가 invariant LT property E를 만족한다면 transition system의 어떤 path를 고르더라도 trace($\phi$)가 E를 만족하는 것을 의미한다.&lt;/li&gt;
&lt;li&gt;T의 path의 모든 state s가 $\phi$를 만족하는 것을 의미한다.&lt;/li&gt;
&lt;li&gt;T의 모든 reachable state s가 $\phi$를 만족하는 것을 의미한다.&amp;nbsp;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Invariant Model Checking&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자. 그럼 invariant를 쓸 때 model checker의 알고리즘을 얘기해 보자. model checker는 transition system T와 invariant LT property E를 받는다. model checker는 T가 E를 만족하는지 살핀다. 만약 만족한다면 true를, 그렇지 않다면 error를 나타낸다. 이 방법은 graph에서 DFS/BFS를 돌려 Reach(T)의 모든 state에 대해 s가 $\phi$를 만족하는지 살핀다. 만약 만족하면 true, 아닌 경우 error이다. pseudo code로 표현하면 다음과 같다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;FOR ALL s$_0$ &amp;isin; S$_0$ DO&lt;br /&gt;&amp;nbsp; &amp;nbsp; IF DFS(s$_0$, $\phi$) THEN&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return &quot;no&quot;&lt;br /&gt;&amp;nbsp; &amp;nbsp; FI&lt;br /&gt;OD&lt;br /&gt;return &quot;yes&quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DFS(s$_0$, $\phi$는 s$_0$에서 DFS를 했을 때 $\phi$를 만족하면 true를, 아닌 경우 false를 리턴하는 함수이다. 이 pseudo code를 수정해 어떤 path의 어떤 state에서 error가 났는지도 쉽게 찾을 수 있을 것이다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;IF s $\notin$ U THEN&lt;br /&gt;&amp;nbsp; &amp;nbsp; IF s $\not\models$ $\phi$ THEN return &quot;true&quot; FI&lt;br /&gt;&amp;nbsp; &amp;nbsp; IF s $\models$ $phi$ THEN&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; insert s in U&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; FOR ALL s' &amp;isin; Post(s) DO&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; IF DFS(s', $\phi$) THEN&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return &quot;true&quot;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; FI&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; OD&lt;br /&gt;&amp;nbsp; &amp;nbsp; FI&lt;br /&gt;FI&lt;br /&gt;return &quot;false&quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Safety Property&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;safety properties는 nothing bad will happen - 원치 않는 상태로 가지 않는 것을 보장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;invariant는 bad state에 도달하지 않는 것을 의미하고 invariant가 아닌 safety properties는 bad prefix가 없는 것을 의미한다. 사실상 invariant는 safety properties의 special case라는 것을 생각하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;수식으로 정의해 보자. E가 AP에 대한 LT property라고 하자. 즉, E &amp;sube; ($2^{\text{AP}}$)$^{\omega}$일 때, &lt;span style=&quot;text-align: start;&quot;&gt;$\sigma$ = A$_0$A$_1$A$_2$... &amp;isin;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;($2^{\text{AP}}$)$^{\omega}$ \ E에 대해 A$_0$A$_1$...A$_n$B$_{n+1}$B$_{n+2}$... 중 어떤 것도 E에 속하지 않도록 하는 $\sigma$의 finite prefix A$_0$A$_1$...A$_{n}$이 존재할 때, 이 조건을 만족하는 E를 safety property라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;해석하자면 &lt;b&gt;E에 없는 prefix를 어떻게 확장하더라도 E에 속하지 않는 property가 safety property&lt;/b&gt;이다. 즉... A$_0$A$_1$...A$_n$로 시작하는 word가 safety property에 속해야 한다는 것이라 보면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;수식으로 표현하면 E( = P$_\text{safe}$) &amp;cap; {$\sigma$\ &amp;isin; ($2^{\text{AP}}$)$^{\omega}$&lt;span style=&quot;text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;| $\hat{\sigma}$는 $\sigma$'의 finite prefix&lt;/span&gt;&lt;/span&gt;} = $\phi$, 이 때 $\hat{\sigma}$는 bad prefix이며 bad prefix로 시작하는 word와 E의 합집합이 공집합이어야 한다는 뜻으로 의미는 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;BadPref&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;bad prefix의 집합을 BadPref라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;minimal bad prefix는 word의 bad prefix 중 제일 짧은 bad prefix를 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;Definition. Satisfaction Relation for Linear Time Property&lt;/h4&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;앞서 Satisfaction Relation for Linear Time Property에서 어떤 transition system TS와, TS의 atomic proposition AP, AP의 linear proposition E에 대해&amp;nbsp;Traces(T) &amp;sube; E &lt;b&gt;iff&lt;/b&gt; TS $\models$ E&lt;span style=&quot;text-align: start;&quot;&gt;라는 것을 살폈다. 여기에 추가로 붙는 iff 명제들이 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;TS $\models$ E&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;iff&lt;/b&gt; TS $\models$ E&lt;/li&gt;
&lt;li&gt;&lt;b&gt;iff&lt;/b&gt; Traces$_\text{fin}$(T) &amp;cap; BadPref = $\phi$&lt;/li&gt;
&lt;li&gt;&lt;b&gt;iff&lt;/b&gt; Traces$_\text{fin}$(T) &amp;cap; MinBadPref = $\phi$
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;T의 finite trace 중 BadPref가 없다는 뜻이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Corollary&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모든 invariant는 safety property이다. invariant는 safety property의 special case이다. 앞서 invariant는 bad state가 없는 것을, safety property는 bad prefix가 없는 것을 의미한다고 했다. bad state 또한 bad prefix로 표현할 수 있으므로, 부분집합이다.&lt;/li&gt;
&lt;li&gt;$\phi$는 safety property이다.&lt;/li&gt;
&lt;li&gt;($2^{\text{AP}}$)$^{\omega}$는 safety property이다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Prefix Closure&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;infinite word $\sigma$를 A$_0$A$_1$...라고 뒀을 때, pref($\sigma$)를 $\sigma$의 finite prefix의 집합으로, LT property pref(E)를 $\bigcup_{\sigma in E}^{}$pref($\sigma$)라고 정의하자. 이 때 closure(E)를 {&lt;span style=&quot;text-align: start;&quot;&gt;$\sigma$&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&amp;isin;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;($2^{\text{AP}}$)$^{\omega}$ pref($\sigma$) &amp;sube; pref(E)}라고 정의한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;해석하자면 &lt;b&gt;모든 prefix pref($\sigma$)가 pref(E)에 속하는 모든 $\sigma$&lt;/b&gt;를 말하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Corollary&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp;E가 safety property iff closure(E) = E&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;E가 closed이므로 closure를 적용해도 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;증명은 생략.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Finite Trace Equivalence&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;앞서 Trace Inclusion and Linear Time Properties에서&amp;nbsp;&lt;span style=&quot;text-align: left;&quot;&gt;T$_1$&lt;/span&gt;&lt;span style=&quot;text-align: left;&quot;&gt;과 T&lt;/span&gt;&lt;span style=&quot;text-align: left;&quot; data-mathml=&quot;&amp;lt;math xmlns=&amp;quot;http://www.w3.org/1998/Math/MathML&amp;quot;&amp;gt;&amp;lt;msub&amp;gt;&amp;lt;mi&amp;gt;&amp;lt;/mi&amp;gt;&amp;lt;mn&amp;gt;2&amp;lt;/mn&amp;gt;&amp;lt;/msub&amp;gt;&amp;lt;/math&amp;gt;&quot;&gt;&lt;span aria-hidden=&quot;true&quot;&gt;&lt;span style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;text-align: left;&quot;&gt;$_2$&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;text-align: left;&quot;&gt;가 AP에 대한 transition system이고, AP에 대한 linear time property가 E에 대해&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;Traces(T&lt;span style=&quot;text-align: left;&quot; data-mathml=&quot;&amp;lt;math xmlns=&amp;quot;http://www.w3.org/1998/Math/MathML&amp;quot;&amp;gt;&amp;lt;msub&amp;gt;&amp;lt;mi&amp;gt;&amp;lt;/mi&amp;gt;&amp;lt;mn&amp;gt;1&amp;lt;/mn&amp;gt;&amp;lt;/msub&amp;gt;&amp;lt;/math&amp;gt;&quot;&gt;&lt;span&gt;&lt;span style=&quot;text-align: left;&quot;&gt;$_1$&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;) &amp;sube; Traces(T$_2$) &lt;b&gt;iff&lt;/b&gt; T$_2$ $\models$&lt;span&gt;&amp;nbsp;&lt;/span&gt;E then T$_1$&lt;span&gt; $\models$&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;E&lt;span style=&quot;text-align: left;&quot;&gt;이다.라고 했었다. 이는 finite trace에도 똑같이 적용된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: left;&quot;&gt; Traces$_{\text{fin}}$(T&lt;span style=&quot;text-align: left;&quot; data-mathml=&quot;&amp;lt;math xmlns=&amp;quot;http://www.w3.org/1998/Math/MathML&amp;quot;&amp;gt;&amp;lt;msub&amp;gt;&amp;lt;mi&amp;gt;&amp;lt;/mi&amp;gt;&amp;lt;mn&amp;gt;1&amp;lt;/mn&amp;gt;&amp;lt;/msub&amp;gt;&amp;lt;/math&amp;gt;&quot;&gt;&lt;span&gt;&lt;span style=&quot;text-align: left;&quot;&gt;$_1$&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;) &amp;sube; Traces&lt;span style=&quot;text-align: left;&quot;&gt;$_{\text{fin}}$&lt;/span&gt;(T$_2$) &lt;b&gt;iff&lt;/b&gt; T$_2$ $\models$&amp;nbsp;E then T$_1$&lt;span style=&quot;text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;$\models$&lt;/span&gt;&amp;nbsp;E&lt;span style=&quot;text-align: left;&quot;&gt;이다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: left;&quot;&gt;&amp;nbsp;한편, Traces$_{\text{fin}}$(T)는 Traces(T)의 nonempty prefix의 집합, 즉&lt;b&gt;&amp;nbsp;pref(Traces(T))&lt;/b&gt;이다!&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;Corollary&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Traces(T) &amp;sube; Traces(T') then &lt;span style=&quot;text-align: left;&quot;&gt;Traces$_{\text{fin}}$(T) &amp;sube; &lt;span style=&quot;text-align: left;&quot;&gt;Traces$_{\text{fin}}$(T')이다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Q.&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;text-align: left;&quot;&gt;trace equivalence iff finite trace equivalence인가? no.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;Traces(T) &amp;sube; Traces(T') then&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;text-align: left;&quot;&gt;Traces$_{\text{fin}}(T)&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;text-align: left;&quot;&gt;&amp;sube;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;text-align: left;&quot;&gt;Traces$_{\text{fin}}(T')이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: left;&quot;&gt;반면 그 역은 성립하지 않는데,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;text-align: left;&quot;&gt;단, T가 terminal state가 없고(모든 path가 infinite), T'가 finite한 경우에는 성립한다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;text-align: left;&quot;&gt;즉, T가 terminal state가 없고 T'가 finite하고 &lt;span style=&quot;text-align: left;&quot;&gt;Traces$_{\text{fin}}(T)&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;text-align: left;&quot;&gt;&amp;sube;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;text-align: left;&quot;&gt;Traces$_{\text{fin}}(T')인 경우 Traces(T) &amp;sube; Traces(T')이다. 이러한 &lt;b&gt;조건이 걸렸을 때만 finite trace equivalence가 trace equivalence를 의미&lt;/b&gt;한다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;text-align: left;&quot;&gt;Liveness Property&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;safety property는 &lt;b&gt;원치 않는 상태로 전이하지 않는 것을 보장&lt;/b&gt;하고, liveness property는 &lt;b&gt;원하는 상태에 도달하는 것을 보장&lt;/b&gt;한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;구분하는 방법은.. safety property의 경우 finite bad prefix가 존재하지만 liveness property는 그런 것이 없기 때문에, 이것으로 safety와 liveness를 구분할 수 있다. 즉, finite bad prefix로 해당 property를 반증할 수 있다면 safety, 그렇지 않다면 liveness이다. 정의에 따라 liveness는 모든 finite word를 infinite로 확장할 수 있는데, 따라서 finite word로 반증할 수 없기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;수식으로 정의하면 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;E가 AP에 대한 LT property라고 하자. 즉, E &amp;sube; ($2^{\text{AP}}$)$^{\omega}$일 때,&lt;span&gt;&lt;b&gt; AP의 finite word가 E에 속하는 infinite word로 확장할 수 있을 때 E를 liveness property&lt;/b&gt;라고 한다. 즉, pref(E) = ($2^{\text{AP}}$)$^{+}$라는 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Decomposition Theorem&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;모든 linear time property E는 safety property와 liveness property 2개로 나뉘고, 이 둘의 intersecion은 다시 E가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;즉, 모든 linear time property를 safety property와 liveness property의 conjunction으로 표현할 수 있다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;둘 다를 갖춘 property가 있을까? 있긴 한데, ($2^{\text{AP}}$)$^{\omega}$만 그렇다. 나머지는 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Fairness&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LT property는 다음 4가지로 나뉜다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;safety property + invariant&lt;/li&gt;
&lt;li&gt;liveness property&lt;/li&gt;
&lt;li&gt;safety property와 liveness property 둘 다를 만족하는 단 하나&lt;/li&gt;
&lt;li&gt;나머지의 모든 property p는 safety property와 liveness property의 교집합이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp;즉, fairness assumption은 LT property가 아니다&lt;/b&gt;!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;예를 들어 [모든 process가 critical section에 들어간야 한다]는 liveness property를 생각해보자. 이를 만족하지 않는 path가 존재한다. 이처럼 &lt;b&gt;liveness property는 종종 violated&lt;/b&gt;된다. 이는 path가 fair하지 않기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이를 해결하기 위해 때문에 not fair한 몇몇 move를 없애 liveness property를 만족하게 만들 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;process fairness는 interleaving과 contention으로 인한 non-determinism의 적당한 해결책이 있다는 것을 가정한다. 이 중 대표적인 것은 아래 3가지가 있다. 유의점은, &lt;b&gt;fairness property는 not fair한 action을 배제하는 것&lt;/b&gt;이기 때문에 어떤 것이 not fair한지 정의하는 것이 중요하다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;unconditional fairness&lt;/b&gt; : 모든 process가 infinitely often하게 자신의 차례를 가져오는 것을 말한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;strong fairness&lt;/b&gt; : enabled infinitely often한 모든 process가 infinitely often하게 자신의 차례를 가져오는 것을 말한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;weak fairness&lt;/b&gt; : 특정 시간부터 continuously enable한 모든 process가 infinitely often하게 자신의 차례를 가져오는 것을 말한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;enabled infinitely often : 해당 action이 수행될 수 있는 finite한 구간이 있다는 것 + process가 실행되지 않는 finite 구간이 있을 수도 있음.&lt;/li&gt;
&lt;li&gt;continuously enable : 특정 구간에 해당 action이가 실행되는 구간이 연속적으로 있다는 것. 해당 process가 수행되지 않는 구간이 finitely often하게 존재한다는 뜻이다.&lt;/li&gt;
&lt;li&gt;대충 이 둘의 차이는, 구간 내에 action을 수행할 수 있는 곳이 continuous하냐, 아니면 sparse하냐로 받아들이면 될 것 같다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;수식으로 정의하면 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;transition system T의 action set Act에 대해 A &amp;sube; Act이고 $\rho$ = s$_0$ $\overrightarrow{\alpha_0}$ s$_1$ &amp;nbsp;$\overrightarrow{\alpha_1}$ s$_2$ $\overrightarrow{\alpha_2}$ ... 인 infinite execution fragment $\rho$를 보자. 이 때&amp;nbsp; fairness는 action에 의해 parameterized된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;859&quot; data-origin-height=&quot;335&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bCU33f/btsuGxUWuUF/kDOJQtCJPULwXC0RXunEHK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bCU33f/btsuGxUWuUF/kDOJQtCJPULwXC0RXunEHK/img.png&quot; data-alt=&quot;fairness의 정의&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bCU33f/btsuGxUWuUF/kDOJQtCJPULwXC0RXunEHK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbCU33f%2FbtsuGxUWuUF%2FkDOJQtCJPULwXC0RXunEHK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;464&quot; height=&quot;181&quot; data-origin-width=&quot;859&quot; data-origin-height=&quot;335&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;fairness의 정의&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;$\rho$는 unconditional A fairness : A에 속하는 action이 infinitely many하게 존재한다는 뜻이다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;text-align: left;&quot;&gt;$\rho$는 &lt;/span&gt;strongly A fairness : s$_j$에서 수행할 수 있는 action이 infinetly many하게 A에 존재할 경우, 그러한 action $\alpha_i$ &amp;isin; A가 infinetly many하게 존재한다는 뜻이다.&lt;/li&gt;
&lt;li&gt;$\rho$는 weak A fairness : 어떤 s$_j$에서 수행할 수 있는 action이 A에 존재할 경우, 그러한 action $\alpha_i$ &amp;isin; A가 infinetly many하게 존재한다는 뜻이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;사용된 notation은 다음과 같다. Act(s$_i$)를 s$_i$에서 택할 수 있는 모든 action set이라고 하자. 각각의 의미는 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;$\overset{&amp;infin;}{\exists}$가 `infinitely many하게 존재함`&lt;/li&gt;
&lt;li&gt;$\overset{&amp;infin;}{\forall}$를 `거의 모든 for all`&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;if unconditionally A-fair then strongly A-fair이고, unconditionally A-fair then weakly A-fair이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Hierarchy&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;fairness assumption의 목적은 unfair execution을 삭제하는 것이 목적이다. 이 때 T의 모든 unconditional A-fair execution action &amp;sube; T의 모든 strong A-fair execution action &amp;sube; T의 모든 weakly A-fair execution action &amp;sube;&amp;nbsp;T의 모든 가능한 execution와 같이 계층구조가 이루어져 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;F-fair&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;fairness assumption A에 여러 개의 action을 넣는다고 해서 그 action들에 대해 fair해지는 것이 아니다. fairness의 정의는 A의 &lt;b&gt;어떤&lt;/b&gt; action을 infinitely often하게 수행하는 것이기 때문이다. 따라서 Fairness Assumption F는 (F$_\text{ucond}$, F$_\text{strong}$, F$_\text{weak}$)로 정의한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;text-align: start;&quot;&gt;F$_\text{ucond}$,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;text-align: start;&quot;&gt;F$_\text{strong}$,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;F$_\text{weak}$&amp;nbsp;&amp;sube; 2$^{\text{Act}}$이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&amp;nbsp;F$_\text{ucond}$ &amp;sube; &lt;/span&gt;&lt;span style=&quot;text-align: start;&quot;&gt;F$_\text{strong}$ &amp;sube; &lt;/span&gt;F$_\text{weak}$이기 때문에 계층적으로 나눠 fairness assumption을 정의할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그러면 F-fair를 정의하자. execution $\rho$는 F-fair &lt;b&gt;iff&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;text-align: left;&quot;&gt;모든&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;A &amp;isin; F$_\text{ucond}$일 때 unconditionally A-fair&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;text-align: left;&quot;&gt;모든&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;A &amp;isin;&lt;span&gt;&amp;nbsp;&lt;/span&gt;F$_\text{strong}$일 때 strongly A-fair&lt;/li&gt;
&lt;li&gt;모든 A &amp;isin;&lt;span&gt;&amp;nbsp;&lt;/span&gt;F$_\text{weak}$일 때 weakly A-fair&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;FairTrace&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;FairTraces$_F$(T)를&amp;nbsp; {$\rho$가 T의 F-fair execution일 때 &lt;span style=&quot;text-align: start;&quot;&gt;trace($\rho$)&lt;/span&gt;}로 정의한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Fairness와 Safety는 irrelevant&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;transition system T와, AP에 대한 LT property E가 있을 때 T $\models_F$ E $\Leftrightarrow$ FairTraces$_F$(T) &amp;sube; E. 즉, fairness는 safety의 역할을 할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위 수식이 의미하는 것은 fairness assumption F와 LT property E를 만족하는 것과, T의 FairTraces가 E에 속한다는 것은 동치라는 뜻이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Fairness 만들기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;fairness assumption은 가능한 한 weak해야 한다. 처음에는 weak로 잡고, 안 되면 strong으로, 안 되면 uncondition으로 fairness assumption을 옮겨야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Fairness와 Safety의 관계&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;fairness assumption은 safety property에 영향을 주지 않는다. 단 이를 위해서는 몇 가지 스킬을 써야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp;realizability&lt;/b&gt;는 각각의 initial finite path fragment가 F-fair path로 확장되어야 한다는 것을 요구한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;realizable의 정의는 fairness assumption는 T의 reachable한 state s에 대해, &lt;b&gt;s에서 시작하는 F-fair path가 존재하는 경우&lt;/b&gt; transition system T에서 realizable하다고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 때 &lt;b&gt;realizable fairness assumption은 safety property와 관계없다&lt;/b&gt;. 만약 transition system T와 safety property E에 대해 fairness assumption &lt;span style=&quot;text-align: start;&quot;&gt;F가 &lt;span&gt;&lt;span style=&quot;text-align: start;&quot;&gt;realizable&lt;span&gt; 하다면 T $\models$ E &lt;b&gt;iff&lt;/b&gt; T $\models_F$ E이다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&lt;span&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;위 명제는 non-realizable fairness assumption에 대해서는 성립하지 않는다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Project/Model Checking</category>
      <author>hyelie</author>
      <guid isPermaLink="true">https://hyelie.tistory.com/634</guid>
      <comments>https://hyelie.tistory.com/entry/Model-Checking-Linear-Time-Properties#entry634comment</comments>
      <pubDate>Tue, 19 Sep 2023 00:57:03 +0900</pubDate>
    </item>
    <item>
      <title>23.09.17. 풀었던 문제들</title>
      <link>https://hyelie.tistory.com/entry/230917-%ED%92%80%EC%97%88%EB%8D%98-%EB%AC%B8%EC%A0%9C%EB%93%A4</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Programmers Lv. 3 베스트앨범, 15분&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그냥 풀면 되는 구현 문제.&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;장르별 재생회수의 순서&lt;/li&gt;
&lt;li&gt;장르 내부에서 재생회수의 순서&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위 2가지에 대해 정렬되어 있어야 하기 때문에 map을 2개 써야 한다. 그냥 뭐.. map에 genre를 key로 넣어 재생회수 합계와 해당 genre에 속한 노래들의 {재생회수, index}를 저장하면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1660456126668&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int len;

struct Info{
    int play, index;
};

struct cmp{
    // 조건 1. play가 큰 것.
    // 조건 2. index가 작은 것.
    bool operator()(Info &amp;amp;a, Info &amp;amp;b){
        if(a.play == b.play) return a.index &amp;gt; b.index;
        return a.play &amp;lt; b.play;
    }
};

typedef pair&amp;lt;string, int&amp;gt; psi;

bool cmpMap(psi &amp;amp;a, psi &amp;amp;b){
    if(a.second == b.second) return a.first &amp;lt; a.first;
    return a.second &amp;gt; b.second;
}

vector&amp;lt;int&amp;gt; solution(vector&amp;lt;string&amp;gt; genres, vector&amp;lt;int&amp;gt; plays) {
    len = plays.size();
    
    // 1. 장르별 재생회수
    map&amp;lt;string, int&amp;gt; genrePlay; // genrePlay[i] : genre i의 재생 회수
    // 2. 장르 내부에서 재생회수 
    map&amp;lt;string, priority_queue&amp;lt;Info, vector&amp;lt;Info&amp;gt;, cmp&amp;gt;&amp;gt; genrePlayMap; // genrePlayMap[i] : genre i의 plays pa
    for(int i = 0; i&amp;lt;len; i++){
        string genre = genres[i];
        int play = plays[i];
        
        genrePlay[genre] += play;
        
        Info info; info.index = i; info.play = play;
        genrePlayMap[genre].push(info);
    }
    
    // map value로 정렬
    vector&amp;lt;psi&amp;gt; genrePlays(genrePlay.begin(), genrePlay.end());
    sort(genrePlays.begin(), genrePlays.end(), cmpMap);
    
    vector&amp;lt;int&amp;gt; answer;
    for(psi data : genrePlays){
        string genre = data.first;
        
        priority_queue&amp;lt;Info, vector&amp;lt;Info&amp;gt;, cmp&amp;gt; &amp;amp;pq = genrePlayMap[genre];
        answer.push_back(pq.top().index); pq.pop();
        if(!pq.empty()) answer.push_back(pq.top().index);
    }
    
    return answer;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;시간복잡도&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;for문이 O(n). map size는 worst case O(n)이므로 insert/pop에 O(nlogn)이 걸린다. 정렬에는 O(nlogn). 총 합계 O(nlogn)이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;후기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;굳이 pq로 안하고 vector로 넣은 다음에 한 번에 정렬했어도 됐을 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Programmers Lv. 3, 스티커 모으기(2), 18분&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;전형적인 DP 문제.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;문제를 딱 보면 dp[i]를 i번째 스티커까지 봤을 때 최댓값으로 둘 지, i번째까지 보고 i번째 스티커를 떼었을 때 최댓값이라고 정의할지 고민이 조금 된다. 그러나 후자로 선택하면 뗀다는 정보가 추가적으로 필요하기에 복잡해진다. 전자로 선택하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그러면 점화식은 dp[i] = max(dp[i-1], dp[i-2] + sticker[i])가 된다. 후항은 dp[i-2]에서 sticker를 떼든 말든 상관없이 i번째 스티커를 뗄 수 있기 때문이고, 전항은 i-1번째 스티커를 떼었는지 여부는 모르기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;또 하나, 유의해야 할 것이 있는데 이 문제는 선형이 아니라 환형이기 때문에 0번째 스티커를 떼는 경우와 0번째 스티커를 떼지 않는 경우를 고려해야 한다. 전자의 경우에는 마지막 스티커를 뗄 수 없고, 후자의 경우는 마지막 스티커를 뗄 수 있으므로 dp 연산을 어디까지 할지 지정만 잘 해주면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1660456126668&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int len;

int solution(vector&amp;lt;int&amp;gt; sticker)
{
    len = sticker.size();
    if(len &amp;lt;= 2) return *max_element(sticker.begin(), sticker.end());
    
    vector&amp;lt;int&amp;gt; dp(len, 0);
    
    // case 1. 0번째 것 뜯는 경우
    dp[0] = sticker[0];
    dp[1] = dp[0];
    for(int i = 2; i&amp;lt;len-1; i++){
        dp[i] = max(dp[i-1], sticker[i] + dp[i-2]);
    }
    int answer = *max_element(dp.begin(), dp.end());
    
    // case 2. 0번째 것 뜯지 않는 경우
    fill(dp.begin(), dp.end(), 0);
    dp[0] = 0;
    dp[1] = sticker[1];
    for(int i = 2; i&amp;lt;len; i++){
        dp[i] = max(dp[i-1], sticker[i] + dp[i-2]);
    }
    answer = max(answer, *max_element(dp.begin(), dp.end()));

    return answer;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;시간복잡도&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;O(n) 순회를 2번 하므로 O(n)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>PS/PS Log</category>
      <author>hyelie</author>
      <guid isPermaLink="true">https://hyelie.tistory.com/633</guid>
      <comments>https://hyelie.tistory.com/entry/230917-%ED%92%80%EC%97%88%EB%8D%98-%EB%AC%B8%EC%A0%9C%EB%93%A4#entry633comment</comments>
      <pubDate>Mon, 18 Sep 2023 00:57:12 +0900</pubDate>
    </item>
    <item>
      <title>23.09.14. 풀었던 문제들</title>
      <link>https://hyelie.tistory.com/entry/230914-%ED%92%80%EC%97%88%EB%8D%98-%EB%AC%B8%EC%A0%9C%EB%93%A4</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;프로그래머스 Lv. 2 최댓값과 최솟값, 4분 30초&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;c++에서 delimiter를 사용해 string을 파싱할 줄 알면 바로 풀리는 문제. iss 쓰고 while getline으로 풀면 된다!&lt;/p&gt;
&lt;pre id=&quot;code_1660456126668&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string solution(string s) {
    int min_v = INT_MAX;
    int max_v = INT_MIN;
    
    istringstream iss(s);
    string buffer;
    while(getline(iss, buffer, ' ')){
        int v;
        if(buffer[0] == '-'){
            v = stoi(buffer.substr(1));
            v *= -1;
        }
        else{
            v = stoi(buffer);
        }
        
        min_v = min(min_v, v);
        max_v = max(max_v, v);
    }
    
    return to_string(min_v) + &quot; &quot; + to_string(max_v);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;시간복잡도&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;s를 파싱하고 최대/최소값을 갱신하는 데 O(n)이 걸린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;프로그래머스 Lv. 2 최솟값 만들기, 1분 40초&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;수학 규칙을 알면 되는 문제. A는 오름차순, B는 내림차순으로 정렬한 후 곱한 것을 더한 것이 답이다.&lt;/p&gt;
&lt;pre id=&quot;code_1660456126668&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int solution(vector&amp;lt;int&amp;gt; A, vector&amp;lt;int&amp;gt; B)
{
    sort(A.begin(), A.end(), less&amp;lt;int&amp;gt;());
    sort(B.begin(), B.end(), greater&amp;lt;int&amp;gt;());
    
    int answer = 0, len = A.size();
    for(int i = 0; i&amp;lt;len; i++){
        answer += A[i] * B[i];
    }

    return answer;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;시간복잡도&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;정렬에 O(nlogn), 순회에 O(n)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;프로그래머스 Lv. 2 올바른 괄호, 2분&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;stack 쓰면 매우 쉽게 풀리는 문제. 사실상 예제문제다. 이런 건 1레벨로 낮춰줬으면.&lt;/p&gt;
&lt;pre id=&quot;code_1660456126668&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;bool solution(string s)
{
    stack&amp;lt;char&amp;gt; stk;
    for(char c : s){
        if(c == ')'){
            if(stk.empty() || stk.top() != '(') return false;
            stk.pop();
        }
        else if(c == '('){
            stk.push('(');
        }
    }
    
    return stk.empty();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;시간복잡도&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;순회에 O(n)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;프로그래머스 Lv. 2 이진 변환 반복하기, 6분 40초&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;주어진 대로 변환하면 되는 문제. 뭐.. 10진수를 n진수로 변환할 때 % 이후 /를 한다는 것만 기억하면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1660456126668&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;string&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;

using namespace std;

int num_removed_zeros = 0;
int num_binary_translation = 0;

string removeZero(string x){
    int total = (int) x.length();
    string result = &quot;&quot;;
    for(char c : x){
        if(c == '1') result += &quot;1&quot;;
    }
    int num_ones = result.length();
    num_removed_zeros += total - num_ones;
    return result;
}

string decimalToBinary(int x){
    num_binary_translation++;
    
    string result = &quot;&quot;;
    while(x){
        result += to_string(x % 2);
        x /= 2;
    }
    reverse(result.begin(), result.end());
    return result;
}

vector&amp;lt;int&amp;gt; solution(string s) {
    while(s != &quot;1&quot;){
        int len = (int) removeZero(s).length();
        s = decimalToBinary(len);
    }
    return {num_binary_translation, num_removed_zeros};
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;프로그래머스 Lv. 3 숫자의 표현, 13분&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;첫 접근&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;아래 느낌으로 nested loop로 돌렸다. n이 1만이니.. 그러나 당연히 TLE. 다른 방법이 필요했다.&lt;/p&gt;
&lt;pre id=&quot;code_1694698987697&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;vector&amp;lt;int&amp;gt; dp(10001, 0); // dp[i] : 연속된 자연수로 i를 표현하는 방법의 개수
int INF = 10001;

int solution(int n) {
    for(int i = 1; i&amp;lt;=n; i++){
        for(int len = 1;)
        for(int j = i; j&amp;lt;=5000; j++){
            int sum = (j - i + 1) * (i + j) / 2;
            if(sum &amp;gt;= INF) continue;
            dp[sum]++;
        }
    }
    
    return dp[n];
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;두 번째 접근&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;sliding window가 딱 생각이 났다. sum을 계산하고, 작으면 e를 늘이고, 크면 s를 늘이는 방식으로.&lt;/p&gt;
&lt;pre id=&quot;code_1660456126668&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int getSum(int s, int e){
    return (e - s + 1) * (s + e) / 2;
}

int solution(int n) {
    // dp 실패했으니 sliding window로 해 볼까? two pointer로.
    int s = 1, e = 1, answer = 0;
    while(s &amp;lt;= n){
        if(s &amp;gt; e){
            e = s;
            continue;
        }
        
        int sum = getSum(s, e);
        if(sum == n){
            answer++;
            s++;
            continue;
        }
        if(sum &amp;lt; n) e++;
        else if(sum &amp;gt; n) s++;
    }
    return answer;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;시간복잡도&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;s의 sliding에 O(n)이, e의 sliding에 O(n)이 걸린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;프로그래머스 Lv. 3 숫자 게임, 17분 40초&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;greedy 문제이다. 일단, 기본 전제: 이길 수 있으면 이기되 최대한 작은 수로 이기고, 지는 경우에는 최대한 작은 수로 져야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;첫 접근&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;최대한 작은 수로 이긴다 -&amp;gt; binary search, 특히 upper bound로 풀었다. 처음에 놓친 점은, multiset을 하지 않은 것.&lt;/p&gt;
&lt;pre id=&quot;code_1694699128473&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int solution(vector&amp;lt;int&amp;gt; A, vector&amp;lt;int&amp;gt; B) {
    multiset&amp;lt;int&amp;gt; s(B.begin(), B.end());
    int answer = 0;
    set&amp;lt;int&amp;gt;::iterator iter;
    for(int point : A){
        
        iter = upper_bound(s.begin(), s.end(), point);
        if(iter == s.end()){ // point보다 큰 것이 없는 경우
            s.erase(s.begin());
        }
        else{ // 존재 : point보다 큰 것중 제일 작은 것
            s.erase(iter);
            answer++;
        }
    }
    
    return answer;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그러나 TLE가 난다! input size가 10만이고, set의 연산은 O(logn)이므로 O(nlogn)으로 풀릴 줄 알았는데, set 연산이 생각보다 느린갑다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;두 번째 접근&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그래서 A는 내림차순으로, B는 오름차순으로 정렬한 후 B가 이기지 못하는 경우라면 B의 제일 작은 점수를 희생시키고, 이길 수 있으면 이기는 방식으로 선택했다.&lt;/p&gt;
&lt;pre id=&quot;code_1660456126668&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int solution(vector&amp;lt;int&amp;gt; A, vector&amp;lt;int&amp;gt; B) {
    sort(A.begin(), A.end(), greater&amp;lt;int&amp;gt;());
    sort(B.begin(), B.end(), less&amp;lt;int&amp;gt;()); // 오름차
    int start = 0, end = B.size()-1;
    int answer = 0;
    for(int point : A){
        if(point &amp;gt;= B[end]){ // B가 못이기는 경우
            start++;
        }
        else{
            end--;
            answer++;
        }
    }
    
    return answer;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;시간복잡도&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;정렬에 O(nlogg), 순회에 O(n)이므로 O(nlogn)이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;후기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;왜 set으로는 안풀릴까.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;프로그래머스 Lv. 3 기지국 설치, 27분&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;전파가 닿지 않는 부분을 알면, w의 /와 % 연산을 이용해서 쉽게 답을 낼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그러면 전파가 닿지 않는 부분을 알아야 하는데,&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;전파가 닿는 구간을 찾음&lt;/li&gt;
&lt;li&gt;전체에서 위 구간을 뺌&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위 방식으로 구현했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;N이 작으면 bitmap 형식으로 풀어도 되는데, 2억이므로 시간 내에 풀 수 없다. 따라서 [시작 좌표, 끝 좌표]로 어떻게든 풀어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;전파가 닿는 구간 찾기
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;겹치는 부분만 잘 처리해 주면 된다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;전파가 닿지 않는 구간 찾기
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;전체에서 전파가 닿는 구간을 빼면 된다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre id=&quot;code_1660456126668&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;typedef pair&amp;lt;int, int&amp;gt; pii; // .first : 시작 지점, .second : 끝 지점. (included)

int solution(int n, vector&amp;lt;int&amp;gt; stations, int w)
{
    int s = stations[0] - w, e = stations[0] + w;
    s = max(s, 1); e = min(e, n);
    pii p = {s, e};
    vector&amp;lt;pii&amp;gt; overlaps(1, p);
    
    // 겹쳐 있는 구간을 찾을 것임.
    for(int station : stations){
        s = station - w, e = station + w;
        s = max(s, 1); e = min(e, n);
        int prev_e = overlaps.back().second;
        
        if(prev_e + 1 &amp;lt; s){ // 아예 새로운 구간이 시작되는 경우
            overlaps.push_back({s, e});
        }
        else{ // 이어지거나, 겹치는 경우
            overlaps.back().second = e;
        }
    }
    overlaps.push_back({n+1, n+1});
    
    
    // overlapping 결과 테스트
    // for(pii p : overlaps){
    //     cout&amp;lt;&amp;lt;p.first&amp;lt;&amp;lt;&quot;, &quot;&amp;lt;&amp;lt;p.second&amp;lt;&amp;lt;endl;
    // }
    
    // sum of ceil(겹쳐 있지 않은 구간 / w)가 답.
    int answer = 0;
    s = 1;
    w = 2 * w + 1;
    for(pii overlap : overlaps){
        e = overlap.first - 1;
        
        answer += (e - s + 1) / w;
        answer += ((e - s + 1) % w != 0);
        
        s = overlap.second + 1;
    }
    
    return answer;
}

/*
비어있는 공간만 찾으면 됨. 구현 문제 같음.
stations들이 겹쳐 있는 구간을 찾는 문제로 바꾸면 쉬울 것 같은데?

*/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;시간복잡도&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;overlaps를 만드는 데 O(n), 이후 overlaps를 순회할 때 O(n)이므로 O(n)이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;후기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 문제는 구현에서 조금 절었다. 실수한 점은, 첫 값을 넣을 때도 s와 e가 [1, n] 사이에 있게 filtering 했어야 했는데 그러지 않아서 문제가 생겼다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>PS/PS Log</category>
      <author>hyelie</author>
      <guid isPermaLink="true">https://hyelie.tistory.com/632</guid>
      <comments>https://hyelie.tistory.com/entry/230914-%ED%92%80%EC%97%88%EB%8D%98-%EB%AC%B8%EC%A0%9C%EB%93%A4#entry632comment</comments>
      <pubDate>Thu, 14 Sep 2023 22:54:42 +0900</pubDate>
    </item>
    <item>
      <title>[Model Checking] Modeling Concurrent System</title>
      <link>https://hyelie.tistory.com/entry/Model-Checking-Modeling-Concurrent-System</link>
      <description>&lt;blockquote style=&quot;color: #666666; text-align: left;&quot; data-ke-style=&quot;style2&quot;&gt;이 글은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://www.youtube.com/playlist?list=PLwabKnOFhE38C0o6z_bhlF_uOUlblDTjh&quot;&gt;RWTH AACHEN 대학교 Joost-Pieter Katoen 교수님의 2018년 1학기 Introduction to Model Checking 강의&lt;/a&gt;와&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://en.wikipedia.org/wiki/Principles_of_Model_Checking&quot;&gt;Principles of Model Checking&lt;/a&gt;을 기반으로 재구성한 것입니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;지난 글에서는 Transition System과 Program Graph, 그리고 Program Graph를 Transition System으로 변환하는 방법을 살펴 봤다. 여기서 살폈었던 것들은 닫힌 계로써, 단 하나의 프로그램만 모델링하는 방법이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이제 총 n개의 parallel system P$_1$, P$_2$, ... P$_n$ 이 있을 때를 모델링하고자 한다. 이 때 각 thread의 행동은 아래 3가지 중 하나이다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;no communication (interleaving) : system이 `independent`할 때 (서로 communication하지 않을 때)&lt;/li&gt;
&lt;li&gt;synchronous communication (handshaking) : system들이 `shared variable`를 사용해 communication을 할 때&lt;/li&gt;
&lt;li&gt;asynchronous communication (channel) : `queue`를 사용해 communication할 때&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 글에서는 위 3가지를 어떻게 transition system으로 표현하는지 살펴볼 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Interleaving&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;b&gt;interleaving communication&lt;/b&gt;은 &lt;b&gt;parallel process들의 concurrent, independent action&lt;/b&gt;을 의미한다. (서로 communication하지 않는다.) 때문에 interleaving(끼워넣기)라고 표현한다. 앞서 그랬듯 이 process들은 nondeterministic하게 다음 action을 결정한다. 기호로는 `|||`로 표기한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;interleaving에서 중요한 점은 action $\alpha$와 $\beta$가 &lt;b&gt;어떤 순서로 실행되든 결과가 같다&lt;/b&gt;는 것이다. 이를Effect($\alpha$ ||| $\beta$) = Effect($(\alpha; \beta)$ + $(\beta; \alpha)$)로 표현한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Definition. Transition System의 Interleaving&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;transition system TS$_1$이 (S$_1$, Act$_1$, &amp;rarr;$_1$, I$_1$, AP$_1$, L$_1$), TS$_2$ (S$_2$, Act$_2$, &amp;rarr;$_2$, I$_2$, AP$_2$, L$_2$)일 때 이 둘의 interleaving 결과 TS$_1$ ||| TS$_2$는 다음과 같이 정의한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;727&quot; data-origin-height=&quot;273&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VV522/btstG7YHEdh/XGMgfm7J6xbZVTbRUwsW2k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VV522/btstG7YHEdh/XGMgfm7J6xbZVTbRUwsW2k/img.png&quot; data-alt=&quot;interleaving communication의 정의&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VV522/btstG7YHEdh/XGMgfm7J6xbZVTbRUwsW2k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVV522%2FbtstG7YHEdh%2FXGMgfm7J6xbZVTbRUwsW2k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;711&quot; height=&quot;267&quot; data-origin-width=&quot;727&quot; data-origin-height=&quot;273&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;interleaving communication의 정의&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;interleaving system의 state들은 &lt;b&gt;각&lt;/b&gt; transition system의 &lt;b&gt;state들의 cartesian product&lt;/b&gt;가 될 것이다.&lt;/li&gt;
&lt;li&gt;&amp;rarr;는&amp;nbsp;$\frac{s_1 \overset{\alpha}{\rightarrow} s'_1}{&amp;lt;s_1, s_2&amp;gt; \overset{\alpha}{\rightarrow} &amp;lt;s'_1, s_2&amp;gt;}$ AND $\frac{s_2 \overset{\alpha}{\rightarrow} s'_2}{&amp;lt;s_1, s_2&amp;gt; \overset{\alpha}{\rightarrow} &amp;lt;s_1, s'_2&amp;gt;}$로 정의한다.&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;좌항의 분자가 의미하는 것은 $s_1$에서 $\alpha$를 통해 $s'_1$으로 이동하는 TS$_1$의 transition relation을 의미한다. 분모가 의미하는 것은 state &amp;lt;$s_1$, $s_2$&amp;gt;에 대해 $\alpha$를 통해 &amp;lt;$s'_1$, $s_2$&amp;gt;로 이동하는 transition relaction을 의미한다. 이 때 &lt;b&gt;TS$_2$의 transition은 일어나지 않는다!&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;우항도 똑같은 방식으로 이해할 수 있다.&lt;/li&gt;
&lt;li&gt;이전에 살펴봤듯 이 분수식이 의미하는 것은 [분자 부분이 참이면 분모 부분을 만족하는데, 이 중 제일 작은 relation]이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;L(s$_1$, s$_2$) = L(s$_1$)&amp;nbsp;&amp;cup; L(s$_2$)로 정의한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;interleaving의 경우 서로 영향을 주지 않기 때문에 cartesian produce와 union으로 표현된다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Communication via Shared Variables&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;interleaving의 경우에는 공유하는 변수가 없어 두 subsystem의 cartesian product를 하면 되었다.&amp;nbsp;그러나 만약 어떤 variable이 shared variable인 경우에는 어떻게 될까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;예를 들어 어떤 shared variable x를 사용하는 transition system 1, transition system 2가 있다고 하자. 둘의 interleaving 결과로 &amp;lt;x=4, x=6&amp;gt;와 같은 존재할 수 없는, &lt;b&gt;inconsistent state&lt;/b&gt;가 생성된다. 이를 해결하기 위해 shared variable을 사용하는 경우는 따로 정의해 주어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;shared variable을 사용하는 parallel program의 transition system을 바로 합치는 것은 어렵기 때문에 &lt;b&gt;program graph 단계에서 interleaving을 통해 shared variable을 먼저 정의&lt;/b&gt;하고, 이후 &lt;b&gt;합친 program graph를 transition system으로 전환해 shared variable을 사용하는 transition system을 정의&lt;/b&gt;한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Definition. Program Graph의 Interleaving&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;program graph PG1와 program graph PG2의 interleaving은 다음과 같다. (단순한 cartesian product이다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;726&quot; data-origin-height=&quot;256&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uDb41/btstMZMfoqf/lHX5yQVA7inOk3I33gEkJ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uDb41/btstMZMfoqf/lHX5yQVA7inOk3I33gEkJ1/img.png&quot; data-alt=&quot;program graph의 interleaving 정의&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uDb41/btstMZMfoqf/lHX5yQVA7inOk3I33gEkJ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuDb41%2FbtstMZMfoqf%2FlHX5yQVA7inOk3I33gEkJ1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;661&quot; height=&quot;233&quot; data-origin-width=&quot;726&quot; data-origin-height=&quot;256&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;program graph의 interleaving 정의&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Loc의 경우 Loc$_1$과 Loc$_2$의 cartesian product이다.&lt;/li&gt;
&lt;li&gt;&amp;rarr;는 위 그림과 같이 정의한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;program grpah의 edge에는 guard와 action이 있었다.&lt;/li&gt;
&lt;li&gt;좌항의 분자 $\text{l} \overset{g:\alpha}{\rightarrow} \text{l'}$는 location l에서 g를 만족할 때 $\alpha$를 수행해 l'로 가는 transition을 의미한다.&lt;/li&gt;
&lt;li&gt;좌항의 분모는 location &amp;lt;l$_1$, l$_2$&amp;gt;에서 g를 만족할 때 $\alpha$를 수행해 &amp;lt;l'$_1$, l$_2$&amp;gt;로 transition 할 수 있다는 것이다.&lt;/li&gt;
&lt;li&gt;이 분수식이 의미하는 것은 [분자 부분이 참이면 분모 부분도 참인데, 이를 만족하는 relation 중 제일 작은 relation]이다.&lt;/li&gt;
&lt;li&gt;우항도 같은 방식으로 이해할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;L(s$_1$, s$_2$) = L(s$_1$)&amp;nbsp;&amp;cup; L(s$_2$)로 정의한다.&lt;/li&gt;
&lt;li&gt;guard는 각각의 guard의 or 연산이다.&lt;/li&gt;
&lt;li&gt;shared varibale는 Var1&amp;nbsp;&amp;cap; Var2이고, Var1&amp;nbsp;&amp;cup; Var2 - (Var1 &amp;cap; Var2)가 local variable이 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;program graph는 shared variable을 허용하는데, program graph에서는 &lt;b&gt;state에 변수의 값이 들어가지 않고 location이 들어가기 때문&lt;/b&gt;이다.(변수의 값을 얻기 위해서는 evaluation function을 사용해야 한다.) 이후 이를 transition system으로 변환할 때, location과 evaluation을 state로 만드는 특성 때문에 program graph의 interleaving을 transition system으로 전환하면 shared variable을 사용할 수 있게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 때문에 다음 특성이 성립한다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;PG = PG$_1$ ||| PG$_2$, TS$_1$ = TS(PG$_1$), TS$_2$ = TS(PG$_2$)라고 정의했을 때&lt;br /&gt;shared variable이 없는 경우에는 TS$_1$ ||| TS$_2$&amp;nbsp; = TS(PG)이지만&lt;br /&gt;shared variable이 있는 경우에는 TS$_1$ ||| TS$_2$&amp;nbsp; &amp;ne;&amp;nbsp;TS(PG)이다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Mutual Exclution with Semaphore&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;semaphore와 같이 shared variable을 사용해서 mutual exclusion을 구현하는 경우를 생각해 보자. (42p 참고) 글로 다 나와있는 내용이라 세부 내용은 생략하지만 중요한 부분만 기술하면 아래와 같다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;PG 2개를 interleaving한 후, 만들어진 location들 중에서 reachability를 따진다.&lt;/li&gt;
&lt;li&gt;reachable한 state들만 따져서 transition system으로 바꾼다.&lt;/li&gt;
&lt;li&gt;critical section으로 둘 다 들어갈 수 있는지 / 둘 다 wait하는 상태는 없는지 확인한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Perterson's Mutual Exclution Algorithm&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;45p 참고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Synchronous Message Passing (Handshaking)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;synchronous message passing (handshaking)이란 interacting하는 transition system들이 synchronous하게 특정 action을 처리해야 한다는 것을 의미한다. 즉, 2개 이상의 transition system이 &lt;b&gt;동시에 특정 action을 수행해야 한다&lt;/b&gt;는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이를 위해 새로운 operator ||$_{H}$를 정의한다. 이 연산자는 &lt;b&gt;independent action에 대해서는 interleaving&lt;/b&gt;하고, H&lt;b&gt;에 있는 action들에 대해서는 synchronization action&lt;/b&gt;을 취하는 연산을 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;681&quot; data-origin-height=&quot;191&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dvGD9C/btstOidK7KY/v1FsI5oOk8Xs3Qmjp1JwZ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dvGD9C/btstOidK7KY/v1FsI5oOk8Xs3Qmjp1JwZ0/img.png&quot; data-alt=&quot;Synchronous Message Passing의 정의&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dvGD9C/btstOidK7KY/v1FsI5oOk8Xs3Qmjp1JwZ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdvGD9C%2FbtstOidK7KY%2Fv1FsI5oOk8Xs3Qmjp1JwZ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;663&quot; height=&quot;186&quot; data-origin-width=&quot;681&quot; data-origin-height=&quot;191&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Synchronous Message Passing의 정의&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;S, Act,&amp;nbsp;&amp;rarr;, I, AP, L에 대해서 모두 cartesian product를 적용한다.&lt;/li&gt;
&lt;li&gt;&amp;rarr;는 아래와 같이 이다. 요약하자면 즉 H에 있지 않은 $\alpha$에 대해서만 기존 relation을 가져오고, H에 있는 것들은 새로 정의한다.&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;$\alpha$ &amp;isin; Act$_i$&amp;nbsp; \ H인 $\alpha$에 대해서 : $\frac{s_1 \overset{\alpha}{\rightarrow} s'_1}{&amp;lt;s_1, s_2&amp;gt; \overset{\alpha}{\rightarrow} &amp;lt;s'_1, s_2&amp;gt;}$ AND $\frac{s_2 \overset{\alpha}{\rightarrow} s'_2}{&amp;lt;s_1, s_2&amp;gt; \overset{\alpha}{\rightarrow} &amp;lt;s_1, s'_2&amp;gt;}$로 정의한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;위의 interleaving과 같다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;$\alpha$ &amp;isin;H인 $\alpha$에 대해서 : $\frac{s_1 \overset{\alpha}{\rightarrow} s'_1 \cap s_2 \overset{\alpha}{\rightarrow} s'_2}{&amp;lt;s_1, s_2&amp;gt; \overset{\alpha}{\rightarrow} &amp;lt;s'_1, s'_2&amp;gt;}$로 정의한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이는 s$_1$에서 $\alpha$를 수행해 s'$_1$으로 가고, s$_2$에서 $\alpha$를 수행해 s'$_2$으로 가고, 이 때 state는 &amp;lt;s$_1$, s$_2$&amp;gt;에서 &amp;lt;s'$_1$, s'$_2$&amp;gt;로 가는 relation 중 제일 작은 것을 의미한다.&lt;/li&gt;
&lt;li&gt;&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Properties of Handshaking&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;transition system들의 handshaking은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;commutative&lt;/b&gt;(교환 가능)하지만&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;not associative&lt;/b&gt;(결합 불가능)이다. 단, 모든 transition system이 같은 H에 대해 synchronize하는 경우에는 associative이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 때 H = Act$_1$ &amp;cap; Act$_2$일 때는 H를 생략하고 ||로 표기한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;또한 H = $\phi$인 경우에는 interleaving operator로 표기할 수 있다. (TS$_1$ ||$_{\phi}$ TS$_2$) = TS$_1$ ||| TS$_2$)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;예시&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Group 3395.png&quot; data-origin-width=&quot;765&quot; data-origin-height=&quot;207&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cKCnbq/btstHcZ2aVA/kGfSQma9R9mxIMfRcpY201/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cKCnbq/btstHcZ2aVA/kGfSQma9R9mxIMfRcpY201/img.png&quot; data-alt=&quot;handshaking 예시&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cKCnbq/btstHcZ2aVA/kGfSQma9R9mxIMfRcpY201/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcKCnbq%2FbtstHcZ2aVA%2FkGfSQma9R9mxIMfRcpY201%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;662&quot; height=&quot;179&quot; data-filename=&quot;Group 3395.png&quot; data-origin-width=&quot;765&quot; data-origin-height=&quot;207&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;handshaking 예시&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;왼쪽과 같은 transition system TS$_1$, TS$_2$가 있을 때 TS$_1$ ||$_{\beta}$ TS$_2$는 오른쪽 그림과 같다. $\beta$를 수행하기 전 state까지 기다리고, 이후에 $\beta$를 수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Synchronous Message Passing using Arbiter&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;arbiter는 2개의 state가 있는 machine이다. 두 transition system과 arbiter가 synchronize되므로, arbiter의 action을 통해서만 lock을 얻을 수 있다는 것이 핵심이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;만약 두 transition system 모두 wait 상태라면, arbiter는 둘 중 하나만 non-deterministic하게 request 상태로 바꿔줄 수 있다. (arbiter의 resource가 한정되어 있기 때문이다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이를 식으로 표현하면 TS$_{\text{Arb}}$ = (TS$_1$ ||| TS$_2$) || Arbiter이다. TS$_1$과 TS$_2$는 서로 communication하지 않기 때문에 interleaving으로, 이 둘의 interleaving 결과와 arbiter가 synchronize한다는 것을 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Channel System&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;data-dependent parallel system은 아래의 3가지로 표현한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;shared variable들로 communication&lt;/li&gt;
&lt;li&gt;synchronous message passing (handshaking)&lt;/li&gt;
&lt;li&gt;asynchronous message passing&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;한편, channel system은 FIFO 형태의 buffer인 channel로 통신하는 parallel system이다. 이 때 &lt;b&gt;buffer size가 0이면 synchronous message passing&lt;/b&gt;, 즉 &lt;b&gt;handshaking을 의미&lt;/b&gt;하고 &lt;b&gt;buffer size가 0보다 크면&lt;/b&gt; delay가 있는 &lt;b&gt;asynchronous pessage passing을 의미&lt;/b&gt;한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;앞서 handshaking은 살펴봤으니 여기서는 asynchronous message passing을 살펴볼 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Asynchronous Message Passing&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;synchronous handshaking에서는 &lt;b&gt;동기화된 action들은 동시에 실행&lt;/b&gt;되어야 했다. 반면 &lt;b&gt;asynchronous&lt;/b&gt;의 경우 sender가 channel에 message를 보내면, receiver는 그 message를 (여유가 있을 때) 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이를 위해 sender, receiver 2개의 program graph, 그리고 buffer를 정의해야 한다. 참고로, &lt;b&gt;channel system은 program graph를 사용해 정의&lt;/b&gt;한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Definition. Communication Action&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;buffer에 대한 action인 communication action 2가지를 새로 정의하자. 참고로, 이 둘의 합집합을 Comm이라고 부르기도 한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;c!v&lt;/b&gt; : value v를 channel c에 넣는다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;c?x&lt;/b&gt; : channel c에서 message를 받아 variable x에 넣는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;cap(c) &amp;gt; 0일 때 c!v와 c?x의 동작은 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;c!v : &lt;b&gt;c의 capacity가 가득 차지 않았을 때만 가능&lt;/b&gt;하다. 하는 동작은 enqueue(c, v)이다.&lt;/li&gt;
&lt;li&gt;c?x : &lt;b&gt;c가 비지 않았을 때만 가능&lt;/b&gt;하다. 하는 동작은 &amp;lt;x := front(c); dequeue(c)&amp;gt;이다.&lt;/li&gt;
&lt;li&gt;조건을 만족하지 않는 동작은 정의하기에 따라 다르다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그럼 기존에는 어떤 action $\alpha$에 대해 $\text{l} \overset{g:\alpha}{\rightarrow} \text{l'}$로 정의했던 것에 communication action도 추가할 수 있다. 추가하는 action들은 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;$\text{l} \overset{g:c!v}{\rightarrow} \text{l'}$와 $\text{l} \overset{g:c?x}{\rightarrow} \text{l'}$&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;$\text{l} \overset{g:c!v}{\rightarrow} \text{l'}$는 location l에서 g가 true일 때 value v를 channel에 넣는 것을 통해 l'로 가는 것을 의미한다.&lt;/li&gt;
&lt;li&gt;$\text{l} \overset{g:c?x}{\rightarrow} \text{l'}$는 locaiton l에서 g가 true일 때 channel에서 message를 받아 variable x에 넣는 것을 의미한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 때 channel에서 message를 받을 때는 front()에 있는 것을 가져온다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Definition. Channel&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이전 포스트에서 typed variable x의 domain을 Dom(x)라고 했고, 모든 x에 대해 모든 Dom(x)들의 합집합을 Var로 정의했고, $\eta$를 Var과 Values를 매핑하는 evaluation function으로 정의했었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;channel 이와 유사하게 정의할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;b&gt;typed channel c&lt;/b&gt;를 다음과 같이 정의한다: typed channel c에 대해 capacity cap(c) &amp;isin; {0, 자연수} &amp;cup; {&amp;infin;}이고 domain Dom(c)를 가진다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;뜻은 capacity가 0과 자연수 또는 unbounded이고, 들어갈 수 있는 값이 Dom(c)라는 뜻이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Definition. Channel Evaluation Function&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;typed channel에 대한 &lt;b&gt;evaluation function $\xi$&lt;/b&gt;는 Chan과 Values*를 매핑하는 함수이며, $\xi$(c)는 Dom(c)에 속하고, 길이가 cap(c)보다 작거나 같아야 한다고 정의한다. 이 때 Chan은 typed channel의 합집합을, Values*는 channel 안에 담긴 finite한 값을 의미한다. 따라서&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Definition. Channel System&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;communication action, channel, channel evaluation function을 정의했으므로 이제 channel system를 정의해 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;b&gt;channel system은 P$_i$를 (Var, Chan)에 대한 program graph라고 두었을 때 [ P$_1$ | P$_2$ | ... | P$_n$ ]&lt;/b&gt;라고 정의한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Var는 typed variable의 set을, Chan은 typed channel의 set을 의미한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 때 (Var, Chan)에 대한 program graph &lt;span style=&quot;text-align: start;&quot;&gt;P$_i$는 (Loc$_i$, Act$_i$, Effect$_i$,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&amp;rarr;$_i$, Loc$_{0,i}$, g$_i$)로 표기하며, channel에 대한 정보만 추가하기 위해 &lt;b&gt;&amp;rarr;만 새로 정의&lt;/b&gt;한다. 나머지는 앞서 살펴봤던 program graph P$_i$의 정의 (Loc$_i$, Act$_i$, Effect$_i$, &amp;rarr;$_i$, Loc$_{0,i}$, g$_i$)와 동일하다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;rarr;&amp;nbsp;&amp;sube; Loc&amp;nbsp;&amp;times; Cond(Var) &amp;times; (Act &amp;cup; Comm) &amp;times; Loc&lt;/li&gt;
&lt;li&gt;기존 &amp;rarr;는 $\text{l} \overset{g:\alpha}{\rightarrow} \text{l'}$만 있었다. 여기에 아래 2가지가 추가된다.&lt;/li&gt;
&lt;li&gt;$\text{l} \overset{g:c!v}{\rightarrow} \text{l'}$ : location l에서 channel c에 value v를 보내 location l'로 변함&lt;/li&gt;
&lt;li&gt;$\text{l} \overset{g:c?x}{\rightarrow} \text{l'}$ : location l에서 channel c에 있는 값을 x에 할당해 location l'로 변함
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 때 c는 P$_i$가 보낼 수 있는 channel이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Definition. Transition System으로 표현한 Channel System&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;바로 앞에서 program graph로 표현한 channel system을 살펴봤다. 이를 transition system으로 변환한 결과는 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;715&quot; data-origin-height=&quot;156&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/baaftm/btstOgmE6vK/aesZDQaLkuxTtHtgQTykx1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/baaftm/btstOgmE6vK/aesZDQaLkuxTtHtgQTykx1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/baaftm/btstOgmE6vK/aesZDQaLkuxTtHtgQTykx1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbaaftm%2FbtstOgmE6vK%2FaesZDQaLkuxTtHtgQTykx1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;715&quot; height=&quot;156&quot; data-origin-width=&quot;715&quot; data-origin-height=&quot;156&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;719&quot; data-origin-height=&quot;257&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2TeqC/btstRRGEeNK/7TgCKJzpwWKBiWdZbW8OZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2TeqC/btstRRGEeNK/7TgCKJzpwWKBiWdZbW8OZK/img.png&quot; data-alt=&quot;transition system으로 표기한 channel system의 정의&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2TeqC/btstRRGEeNK/7TgCKJzpwWKBiWdZbW8OZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2TeqC%2FbtstRRGEeNK%2F7TgCKJzpwWKBiWdZbW8OZK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;719&quot; height=&quot;257&quot; data-origin-width=&quot;719&quot; data-origin-height=&quot;257&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;transition system으로 표기한 channel system의 정의&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;state는 &amp;lt;l$_1$, l$_2$, ... , l$_n$, $\eta$, $xi$&amp;gt;이다. l$_i$는 P$_i$의 location이고 $\eta$는 variable evaluation function, $\xi$는 channel evaluation function이다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;따라서 S = (Loc$_1$ &amp;times; Loc$_n$) &amp;times; Eval(Var) &amp;times; Eval(Chan)이다.&lt;/li&gt;
&lt;li&gt;각 program graph의 위치, 각 program graph의 변수 값, 각 channel의 값이 state가 된다는 뜻이다.&lt;/li&gt;
&lt;li&gt;$\eta$는 Var과 $\bigcup_{x \in Var}^{} Dom(x)$ with $\eta(x)$ in Dom(x)를 매핑한다.&lt;/li&gt;
&lt;li&gt;$\xi$는 Var과 $\bigcup_{x \in Chan}^{} Dom(c)*$ with $\xi(x)$ in Dom(c)* and &lt;span style=&quot;text-align: left;&quot;&gt;$|\xi(x)| \le cap(c)*$를 매핑한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;615&quot; data-origin-height=&quot;642&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5MRo0/btstG6yJM1U/ijNmKABKcYsgJyGfBgmLUK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5MRo0/btstG6yJM1U/ijNmKABKcYsgJyGfBgmLUK/img.png&quot; data-alt=&quot;transition system으로 표기한 channel system의 transition relation&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5MRo0/btstG6yJM1U/ijNmKABKcYsgJyGfBgmLUK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5MRo0%2FbtstG6yJM1U%2FijNmKABKcYsgJyGfBgmLUK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;615&quot; height=&quot;642&quot; data-origin-width=&quot;615&quot; data-origin-height=&quot;642&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;transition system으로 표기한 channel system의 transition relation&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;text-align: left;&quot;&gt;&lt;span&gt;transition relation는 위 그림과 같다.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;text-align: left;&quot;&gt;&lt;span&gt;independent한 경우에는 기존 interleaving과 같이 쓰면 된다. 분자는 기존 state의 전환 중 어떤 l$_i$의 전환이 $g:\alpha$를 쓴다는 것을, 분모는 오직 l$_i$의 location만 l'$_i$로 바뀐 것을 의미한다. 이를 만족하는 제일 작은 relation이라는 뜻이다.&lt;/span&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;text-align: left;&quot;&gt;&lt;span&gt;이 때 $\xi$는 변하지 않으므로 그대로 유지된다.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;text-align: left;&quot;&gt;&lt;span&gt;asynchronous message passing은 아래와 같다. asynchronous이므로 cap(c) &amp;gt; 0이 전제된다.&lt;/span&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;text-align: left;&quot;&gt;&lt;span&gt;receiving의 경우&amp;nbsp;&lt;/span&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;text-align: left;&quot;&gt;&lt;span&gt;$\xi$(c)의 length &amp;gt; 0이고, $\xi$(c)가 &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;text-align: left;&quot;&gt;&lt;span&gt;v$_1$...v$_k$일 때 &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;text-align: left;&quot;&gt;&lt;span&gt;분모는&lt;span style=&quot;text-align: left;&quot;&gt; l$_i$의 location만 l'$_i$로, channel에서 값을 가져왔으니 $\eta$와 $\xi$가 바뀐 것을 의미한다. $\eta$'는 기존 $\eta$에서 &lt;b&gt;x값만&lt;/b&gt; v$_1$으로 바뀐 것을, $\xi$'(c)는 &lt;b&gt;channel c의 값만&lt;/b&gt; v$_2$...v$_k$로 바뀌었다는 것이다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;text-align: left;&quot;&gt;&lt;span&gt;&lt;span style=&quot;text-align: left;&quot;&gt;분자는 g를 만족하는 transition에 대해 l&lt;span style=&quot;text-align: left;&quot;&gt;$_i$의 location만 l'$_i$로 바뀐 것을 의미한다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;text-align: left;&quot;&gt;&lt;span&gt;&lt;span style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;text-align: left;&quot;&gt;&lt;span&gt;channel에서 값을 꺼내오고, variable evaluation function과 channel evaluation function 2가지를 갱신하고, location을 바꾼 것이라고 이해하면 된다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;sending의 경우 - receiving의 경우와 같다.&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;text-align: left;&quot;&gt;&lt;span&gt;$\xi$(c)의 length &amp;le; cap(c)이고(빈 공간이 있고), $\xi$(c)가 v$_1$...v$_k$일 때 &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;text-align: left;&quot;&gt;&lt;span&gt;분모는&lt;span style=&quot;text-align: left;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;l$_i$의 location만 l'$_i$로, channel에 값을 넣었으니 $\xi$만 바뀐 것을 의미한다. $\xi$'(c)는&lt;span&gt;&lt;b&gt; channel c의 값만&lt;/b&gt; 기존 $\xi$(c)에서&lt;span style=&quot;text-align: left;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;v가 추가되어 v$_1&lt;/span&gt;$...v$_k$v로 바뀐 것으로 바뀐 것을 의미한다. 변수 값이 바뀌지 않았으므로 $\eta$는 바뀌지 않는다!&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;text-align: left;&quot;&gt;&lt;span&gt;&lt;span style=&quot;text-align: left;&quot;&gt;분자는 g를 만족하는 transition에 대해 l&lt;span style=&quot;text-align: left;&quot;&gt;$_i$의 location만 l'$_i$로 바뀐 것을 의미한다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;text-align: left;&quot;&gt;channel에 값을 넣고, channel evaulation function과 locaiton을 갱신했다고 이해하면 된다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;text-align: left;&quot;&gt;synchronous message passing은 다음과 같다.&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;text-align: left;&quot;&gt;synchronous message passing은 2개의 location이 동시에 바뀌어야 한다는 뜻이다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;text-align: left;&quot;&gt;program graph i는 message를 receieve하고, program graph j는 message를 send한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;text-align: left;&quot;&gt;분자가 의미하는 것은 g$_1$과 g$_2$를 만족할 때 &lt;b&gt;서로 다른&lt;/b&gt; program graph가 message를 주고받는 것을 의미한다.&lt;/span&gt;&lt;span style=&quot;text-align: left;&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;text-align: left;&quot;&gt;분모는 이 transition으로 i, j의 location이 바뀌는 것, 그리고 변수 값도 달라졌으므로 $\eta$도 바뀐 것을 의미한다. $\eta$'는 $\eta$에서 &lt;b&gt;x값만&lt;/b&gt; v로 바뀌었다. $\xi$는 변하지 않는다!&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Act, I(초기 state), AP, L는 program graph를 transition system으로 변환하는 것과 동일하며 따로 추가되는 것이 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Variation&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc; text-align: left;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;c!v의 경우, c!expr처럼 변수 하나의 값이 아니라 expression의 결과값도 보낼 수 있다.&lt;/li&gt;
&lt;li&gt;$\text{l} \overset{c?x:\alpha}{\rightarrow} \text{l'}$처럼 communication을 condition으로 쓸 수 있다. 이 경우는 channel에서 값을 꺼내왔을 때만 $\alpha$ action을 수행하는 것을 의미한다. 값을 꺼내 오는 것은 channel에 값이 있을 때만 가능하므로 이를 조건으로 쓴 것이다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;text-align: start;&quot;&gt;기존에는 closed channel system을 [ P$_1$ | P$_2$ | ... | P$_n$ ]처럼 작성했는데, 외부와 통신하는 open channel system을&lt;span&gt;&amp;nbsp;&lt;/span&gt;P$_1$ | P$_2$ | ... | P$_n$로 표기할 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;State Explosion&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;2개의 program graph가 있고, 각각 2개의 location이 있고,&amp;nbsp;2개의 shared variable이 있다고 하자. 이 때 두 program graph는 capacity 10짜리 channel이 2로 communication한다. (편의상 모든 변수는 boolean이라 두자.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 경우 state의 개수는 어떻게 될까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Loc에 해당하는 state는 2 * 2이고, $\eta$에 해당하는 state도 2 * 2, $\xi$에 해당하는 state는 (2$^{11}$ - 1) * (2$^{11}$ - 1)개이다. 이 3가지의 곱이 전체 state의 개수이다. 너무 크다! 만약 unbounded channel인 경우에는 당연히 INF이다. 이 문제를 해결하는 방법은 추후에 알아본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;(nano)promola&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&amp;nbsp;위에서 배운 수식들은 수학적 도구를 사용하기 때문에 너무 복잡하다. 때문에 더 간단한 형식으로 써야 하는데, 이 형식은 비전문가도 사용하지 않을 정도로 간단해야 하지만 의미를 정확하게 나타내야 한다. 이를 언어로 표현하는 방식 중 하나가 promela이다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&amp;nbsp;promela는 model checker 중 하나인 SPIN의 input language이다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&amp;nbsp;SPIN의 동작 방식은 다음와 같다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;사용자가 promela program을 작성한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;promela program은 channel system을 매핑한 것으로써, 적당한 syntax를 사용해 이를 쓸 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;SPIN은 사용자가 작성한 promela program을 channel system으로 바꾼다.&lt;/li&gt;
&lt;li&gt;변환된 channel system을 transition system으로 바꾼다.&lt;/li&gt;
&lt;li&gt;transition system에서 model checking을 진행한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;nanopromela program $\overline{P}$ = [ P$_1$ | P$_2$ | ... | P$_n$ ]일 때, 각 P$_i$를 process라고 한다. process의 동작을 의미하는 statement의 syntax는 다음과 같다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;nanopromela에서 변수 선언, dynamic process 생성 등 detail 등을 사용하지 않는다.&lt;/blockquote&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;stmt&amp;nbsp;::= &lt;br /&gt;&lt;br /&gt;skip&amp;nbsp;|&amp;nbsp;x&amp;nbsp;:=&amp;nbsp;expr&amp;nbsp;|&amp;nbsp;c?x&amp;nbsp;|&amp;nbsp;c!expr&amp;nbsp;| &lt;br /&gt;stmt$_1$;&amp;nbsp;stmt$_2$&amp;nbsp;|&amp;nbsp;atomic{assignments}&amp;nbsp;| &lt;br /&gt;if&amp;nbsp;::&amp;nbsp;g$_1$&amp;nbsp;$\Rightarrow$&amp;nbsp;stmt$_1$&amp;nbsp;...&amp;nbsp;::&amp;nbsp;g$_n$&amp;nbsp;$\Rightarrow$&amp;nbsp;stmt$_n$&amp;nbsp;fi&amp;nbsp;| &lt;br /&gt;do&amp;nbsp;::&amp;nbsp;g$_1$&amp;nbsp;$\Rightarrow$&amp;nbsp;stmt$_1$&amp;nbsp;...&amp;nbsp;::&amp;nbsp;g$_n$&amp;nbsp;$\Rightarrow$&amp;nbsp;stmt$_n$&amp;nbsp;od&amp;nbsp;|&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 사용하는 syntax들은 channel system에서 사용하는 것과 동일하므로 쉽게 이해할 수 있을 것이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;skip : atomic command를 의미한다.&lt;/li&gt;
&lt;li&gt;x := expr : assignment&lt;/li&gt;
&lt;li&gt;c?x, c!expr : channel에 push/pop&lt;/li&gt;
&lt;li&gt;atomic{} : atomic operations&lt;/li&gt;
&lt;li&gt;if : guard 조건을 만족할 때 branch
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;만약 else option이 없는 경우는 stmt를 skip으로 두면 if branch를 탈출한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;do : guard 조건을 만족할 때 loop
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;n개의 guard 중 1개 이상이 true가 될 때까지 loop를 돌린다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;유의점&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. test-and-set semantics&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;promela에서 선언한 [guard에 대한 evaluation + enabled guard의 선택 + 선택된 guard에 해당하는 첫 번째 atomic statement의 실행] 이 3가지 동작은 program graph에서 하나의 edge가 된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예를 들어 guard가 x&amp;gt;=0, x&amp;lt;=0이 있는 상황이라면 어떤 statement를 실행해야 할까? 이를 위해 test-and-set semantics를 사용한다. x의 값을 보고, enabled guard를 non-determinsitic하게 고르고, 해당 guard의 첫 번째 atomic statement를 실행한다. 이 모든 것이 한 edge가 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. blocking semantics&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;if문에서 guard가 만족되지 않는 경우, 빠져나가지 않고 만족될 때까지 기다린다! blocking semantics는 if문에서만 적용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;if&amp;nbsp;x&amp;nbsp;&amp;gt;&amp;nbsp;10&amp;nbsp;$\Rightarrow$&amp;nbsp;atomic{...}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;x&amp;nbsp;&amp;lt;&amp;nbsp;4&amp;nbsp;&amp;nbsp;$\Rightarrow$&amp;nbsp;atomic{...}&lt;br /&gt;fi&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;직관적이지 않기 때문에 nanopromela 예시를 들어 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;x가 7이라고 하자. 그러면 if문의 모든 guard가 false이기 때문에 if가 시작되지 직전 위치에서 기다린다. x가 shared variable이라고 하면, 다른 process에서 값을 바꿀 수 있고, 그러면 guard가 true로 되기 때문이다. 원칙적으로는 무한히 기다릴 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;정리&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;interleaving `|||`
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;shared variable과 independent한 transition system의 경우 transition system의 interleaving TS$_1$ ||| TS$_2$로 표현한다.&lt;/li&gt;
&lt;li&gt;shared variable를 사용하는 경우 program graph의 interleaving P$_1$ ||| P$_2$를 transition system으로 변환한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;synchronous message passing (handshaking) ||$_H$
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;H에 속해있는 action만 synchronization이 일어날 때&amp;nbsp;TS$_1$ ||$_H$ TS$_2$로 표기한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;channel system
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;interleaving, shared variable, message passing 3가지가 모두 속해 있다.&lt;/li&gt;
&lt;li&gt;buffer size에 따라 synchronous message passing인지 asynchronous message passing인지가 결정된다.&lt;/li&gt;
&lt;li&gt;[ P$_1$ | P$_2$ | ... | P$_n$ ]로 표기하며 이 결과를 transition system으로 변환한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Project/Model Checking</category>
      <author>hyelie</author>
      <guid isPermaLink="true">https://hyelie.tistory.com/631</guid>
      <comments>https://hyelie.tistory.com/entry/Model-Checking-Modeling-Concurrent-System#entry631comment</comments>
      <pubDate>Wed, 13 Sep 2023 01:51:23 +0900</pubDate>
    </item>
    <item>
      <title>23.09.12. 풀었던 문제들</title>
      <link>https://hyelie.tistory.com/entry/230912-%ED%92%80%EC%97%88%EB%8D%98-%EB%AC%B8%EC%A0%9C%EB%93%A4</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Leetcode 1647. Minimum Deletions to Make Character Frequencies Unique, 30분&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;첫 접근&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그냥 주는 대로 풀었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;일단 각 char이 몇 번 나오는지 알아둬야 하니까 map같은 걸 써야 하는데, 이 문제의 경우 알파벳 소문자만 나오기 때문에 vector(26, 0)을 map처럼 쓰면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이후, 내림차순으로 정렬한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;정렬한 후, 만약 겹치는 값이 있는 경우에는 해당 값을 1 줄이고, 해당 문을 다시 반복한다. 이 접근이 문제될 때는, 2 2 2와 같은 예시를 보자. 2 2 2에서 index 1이 2 1 2로 바뀐다. index 1에서 같은 값의 비교는 앞의 값만 보기 때문에 pass. index 2에서 앞의 값만 보기 때문에 pass한다. 문제가 생긴다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;때문에, 바로 앞의 index와 값이 같은지 + 앞의 값보다 값이 크면 1 줄이는 식으로 구현했다.&lt;/p&gt;
&lt;pre id=&quot;code_1660456126668&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Runtime 42 ms Beats 93.66%
// Memory 17.3 MB Beats 77.52%

class Solution {
public:
    int minDeletions(string s) {
        vector&amp;lt;int&amp;gt; f(26, 0); // f[i] : frequency of ith alphabet
        for(char c : s){
            f[c - 'a']++;
        }
        sort(f.begin(), f.end(), greater&amp;lt;int&amp;gt;());

        // f가 겹치는 것들이 있는 경우, 몇 개의 letter를 삭제해 겹치지 않게 바꿔야 함.
        // 내린 값이 겹치지 않게 설정해야 함. 그 중 제일 큰 값으로.
        int answer = 0;
        for(int i = 1; i&amp;lt;f.size(); i++){
            // 만약 겹치는 경우! i번째 char를 줄여야 함.
            if(f[i] == 0) continue;

            if(f[i-1] &amp;lt;= f[i]){
                f[i]--;
                answer++;
                i--;
            }
        }

        // print(f);

        return answer;
    }
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;시간복잡도&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;frequency를 세는 데 O(n)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;정렬에 O(nlogn)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이후 메인 로직인 for문에서 어떻게 시간이 걸릴 지 모르겠다. 그러나 worst case는 [모든 숫자가 똑같을 때] worst case이며, 이 경우에 25 * 26 / 2만큼 실행된다. (앞의 것과 비교하고, 만약 더 크면 1을 내리기 때문)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그러나 이렇게 해도, 입력 문자가 26이기 때문에 충분히 풀린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;두 번째 접근&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;for loop로 돌리니까 뭔가 직관성이 떨어진다. for loop 대신 pq를 쓰는 방법도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;세 번째 로직만 조금 바뀌는데, pq의 top t를 뽑고, 이후의 pq.top()과 같은 경우는 pq.top()을 줄여야 한다. t를 1 줄이고 pq에 다시 넣는다. 만약 t와 pq.top()이 다른 경우에는 그냥 빼버리면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 경우를 pq.size() == 1이 될 때까지 반복한다. pq.size()가 1인 경우는 무조건 identical한 값이 튀어나오기 때문이다.&lt;/p&gt;
&lt;pre id=&quot;code_1694516757274&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Runtime 49 ms Beats 79.99%
// Memory 17.3 MB Beats 58%

class Solution {
public:
    int minDeletions(string s) {
        vector&amp;lt;int&amp;gt; f(26, 0); // f[i] : frequency of ith alphabet
        for(char c : s){
            f[c - 'a']++;
        }

        priority_queue&amp;lt;int, vector&amp;lt;int&amp;gt;, less&amp;lt;int&amp;gt;&amp;gt; pq;

        for(int e : f){
            pq.push(e);
        }

        int answer = 0;
        while(!pq.empty()){
            int t = pq.top(); pq.pop();
            if(t == 0 || pq.size() == 0) break;
            if(t &amp;gt; 0 &amp;amp;&amp;amp; pq.top() == t){
                answer++;
                pq.push(t-1);
            }
        }

        return answer;
    }
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;시간복잡도&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;pq를 써서 시간복잡도가 개선될 것 같지만, -- 연산의 회수는 동일하다. 그러나 pq의 push, pop 연산에서 logn을 추가로 소모하기 때문에 더 많은 시간이 걸린다! 아마 26log26쯤 더 걸리지 않을까 싶다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Programmers Lv. 3 최고의 집합, 4분&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;중앙값을 택하는 게 곱이 제일 커진다. 뭐... 고등학교 수학을 배운 이과라면 다들 아는 내용.&lt;/p&gt;
&lt;pre id=&quot;code_1660456126668&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;vector&amp;lt;int&amp;gt; solution(int n, int s) {
    // 만들 수 없는 경우 : 
    if(n &amp;gt; s) return {-1};
    
    // 중앙값일 때 곱이 최대
    int q = s/n;
    vector&amp;lt;int&amp;gt; answer(n, q);
    int diff = s % n;
    // answer 배열의 뒤에서부터 diff개에 1씩 더해주면 됨
    for(int i = n-1, cnt = 1; cnt &amp;lt;= diff; i--, cnt++){
        answer[i]++;
    }
    
    return answer;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;시간복잡도&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;n size 배열을 만들고 s%n만큼 순회한다. 배열 만들 때 O(n), 순회할 때 worst O(n)이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;Programmers Lv. 3 야근 지수, 3분 30초&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;pq를 사용할 줄 안다면 바로 풀리는 문제. n이 1,000,000으로 1백만인데, O(nlogn) 알고리즘은 5백만까지 커버 가능하다!&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;2$^{10}$이 1000정도로 잡으면, 2$^{20}$이 1,000,000이다. 1,000,000에 20을 곱해봤자 2천만 정도로, 백만 정도는 O(nlogn)으로 풀린다!&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;앞 문제와 마찬가지로, 제일 큰 값을 1 줄이는 게 야근 지수를 제일 많이 줄일 수 있다. 이를 위해 제일 큰 값을 항상 뽑아와야 하는데, 그러면 pq가 생각난다!&lt;/p&gt;
&lt;pre id=&quot;code_1694519317091&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;typedef long long ll;

long long solution(int n, vector&amp;lt;int&amp;gt; works) {
    priority_queue&amp;lt;int, vector&amp;lt;int&amp;gt;, less&amp;lt;int&amp;gt;&amp;gt; pq;
    for(int work : works) pq.push(work);
    
    while(1){
        if(n == 0 || pq.empty()) break;
        int t = pq.top(); pq.pop();
        if(t != 1) pq.push(t-1);
        n--;
    }
    
    long long answer = 0;
    while(!pq.empty()){
        int t = pq.top(); pq.pop();
        answer += (ll) t * (ll) t;
    }
    return answer;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;시간복잡도&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;pq의 각 연산은 works size 5만을 w로 두면 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;O(logw)이다. n은 operation 개수이므로, O(nlogw)이다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;Programmers Lv. 3 단어 변환, 12분 30초&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;`최단 거리` -&amp;gt; BFS를 쓰면 된다. 단, 이 문제는 일반적으로 BFS하던 board가 아니라 string으로 하기 때문에 string에 대해 visited를 처리해야 하고, string에 대해 neighbor를 얻어와야 한다. 이건 구현 문제고.. 이것만 잘 처리하면 별 것 없다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;int의 경우에는 vector로 선언해서 쉽게 풀 수 있지만 string의 경우에는 어렵다. 그렇다고 string to integer mapper를 넣기에는 모든 string을 쓰는 부분에 mapper를 call해야 하기 때문에 너무 번거롭다고 생각해 그냥 string을 쓰기로 했다. 이를 위해 map을 사용했는데, map을 쓰면 string을 key로 넣어 visited 여부와 neighbor vector를 쉽게 얻어올 수 있기 때문이었다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 부분만 끝내면 뭐.. 나머지는 BFS다.&lt;/p&gt;
&lt;pre id=&quot;code_1694519340398&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int len;
map&amp;lt;string, bool&amp;gt; visited; // visited[i] : string i를 사용했는지 여부
map&amp;lt;string, vector&amp;lt;string&amp;gt;&amp;gt; nextMap; // nextMap[i] : i에서 바꿀 수 있는 string vector

struct info{
    string s;
    int dist;  
};

// string a를 b로 바꿀 수 있는지 여부
bool canTransfer(string &amp;amp;a, string &amp;amp;b){
    int cnt = 0;
    for(int i = 0; i &amp;lt; len; i++){
        if(a[i] != b[i]) cnt++;
        if(cnt &amp;gt;= 2) break;
    }
    return cnt == 1;
}

int solution(string begin, string target, vector&amp;lt;string&amp;gt; words) {
    // init
    len = begin.length();
    words.push_back(begin);
    for(string word : words){
        visited[word] = false;
    }
    int wsize = words.size();
    for(int i = 0; i&amp;lt;wsize; i++){
        for(int j = i+1; j&amp;lt;wsize; j++){
            if(canTransfer(words[i], words[j])){
                nextMap[words[i]].push_back(words[j]);
                nextMap[words[j]].push_back(words[i]);
            }
        }
    }
    
    // solve : BFS
    queue&amp;lt;info&amp;gt; q;
    info i; i.s = begin; i.dist = 0;
    q.push(i);
    visited[begin] = true;
    while(!q.empty()){
        string cur_s = q.front().s;
        int cur_d = q.front().dist;
        q.pop();
        if(cur_s == target) return cur_d;
        
        for(string next_s : nextMap[cur_s]){
            if(!visited[next_s]){
                info i; i.s = next_s; i.dist = cur_d + 1;
                q.push(i);
                visited[next_s] = true;
            }
        }
    }
    
    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;시간복잡도&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;BFS 시간복잡도는 O(V+E). V = 50이고, worst case 각 vertex당 E = 50이 될 수 있다. 그래봤자 2500이므로 시간 내에 충분히 끝낼 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;Programmers Lv. 3 등굣길, 8분 30초&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;길찾기 DP 문제. dp[i][j] = dp[i-1][j] + dp[i][j-1]이라는 bottom-up DP로 쉽게 풀 수 있다. 단, 물웅덩이가 있는 곳은 계산하지 않아야 하므로(0으로 두어야 하므로) 이 부분은 set으로 빨리 처리했다. 물론 초기화도 해 주고.&lt;/p&gt;
&lt;pre id=&quot;code_1694519352790&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;typedef pair&amp;lt;int, int&amp;gt; pii;
int MOD = 1000000007;

int solution(int m, int n, vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; puddles) {
    vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; dp(m, vector&amp;lt;int&amp;gt;(n, 0));
    
    set&amp;lt;pii&amp;gt; puddle_set;
    for(vector&amp;lt;int&amp;gt; puddle : puddles){
        puddle_set.insert({puddle[0]-1, puddle[1]-1});
    }
    
    // init
    for(int r = 0; r&amp;lt;m; r++){
        if(puddle_set.find({r, 0}) != puddle_set.end()) break;
        dp[r][0] = 1;
    }
    for(int c = 0; c&amp;lt;n; c++){
        if(puddle_set.find({0, c}) != puddle_set.end()) break;
        dp[0][c] = 1;
    }
    
    // dp
    for(int r = 1; r&amp;lt;m; r++){
        for(int c = 1; c&amp;lt;n; c++){
            if(puddle_set.find({r, c}) != puddle_set.end()) continue;
            dp[r][c] = (dp[r-1][c] % MOD + dp[r][c-1] % MOD) % MOD;
        }
    }
    
    return dp[m-1][n-1];
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;후기&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;뭔가 생각과 타이핑의 속도가 엄청나게 빨라진 것 같다. 그만큼 &lt;b&gt;안 덤벙거리게 잘&lt;/b&gt; 해야 겠다. 특히 마지막 문제도.. 로직은 다 맞았는데 % 연산을 까먹었다. 구현 자체는 안 덤벙대서 다행이다. 근데 이건 머릿속에 좌르르 흘러가는 느낌이 든다.!!! 물론 복잡한 문제도 아니고... 구현이 복잡한 문제를 30분컷 내야 하는데. 끄악&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>PS/PS Log</category>
      <author>hyelie</author>
      <guid isPermaLink="true">https://hyelie.tistory.com/630</guid>
      <comments>https://hyelie.tistory.com/entry/230912-%ED%92%80%EC%97%88%EB%8D%98-%EB%AC%B8%EC%A0%9C%EB%93%A4#entry630comment</comments>
      <pubDate>Tue, 12 Sep 2023 21:02:14 +0900</pubDate>
    </item>
    <item>
      <title>23.09.11. 풀었던 문제들</title>
      <link>https://hyelie.tistory.com/entry/230911-%ED%92%80%EC%97%88%EB%8D%98-%EB%AC%B8%EC%A0%9C%EB%93%A4</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Leetcode 1282. Group the People Given the Group Size They Belong To, 30분&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;문제 자체는 빨리 풀었는데, 여러 개선을 한다고 시간이 좀 걸렸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;첫 번째 접근&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;최대한 생각나는 대로 구현했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;groupMap은 특정 size를 가지고 있는 모든 group의 정보를 가지고 있다. 때문에 구현은 단순하다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;입력으로 받은 size의 group이 없는 경우 생성한다.&lt;/li&gt;
&lt;li&gt;입력으로 받은 size의 group이 있는 경우,
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;해당 group이 존재하지만 아직 size만큼 차지 못하는 경우 - 해당 group에 현재 인원을 넣어준다.&lt;/li&gt;
&lt;li&gt;해당 group이 있고, size만큼 찬 경우 - 새로운 group을 넣어준다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이렇게 푸니까 runtime 22ms, beats 8.5%가 나왔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;시간복잡도로 따지면, 입력 size를 n이라고 하면 O(nlogn)이다. 시간이 상대적으로 느리게 나온 이유는 vector를 계속 추가적으로 할당해서 그런 것 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1694432566511&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Runtime 22 ms Beats 8.50% 
// Memory 15.3 MB Beats 5.29%

class Solution {
public:
    map&amp;lt;int, vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt;&amp;gt; groupMap; // groupMap[i] : i명짜리 group
    // i번째 사람을 groupSize 크기의 group에 넣는 함수
    void insertIntoGroup(int i, int groupSize){
        // 아직 해당 size의 group이 없는 경우 - 생성
        if(groupMap.find(groupSize) == groupMap.end()){
            vector&amp;lt;int&amp;gt; newGroup(1, i);
            groupMap[groupSize].push_back(newGroup);
            return;
        }

        // 해당 size의 group이 있는 경우
        // 1. group이 있지만 아직 그만큼 차지 못한 경우
        // 2. 기존에 존재하는 group size가 groupSize여서 새로운 group을 만들어야 하는 경우
        vector&amp;lt;int&amp;gt; &amp;amp;lastGroup = groupMap[groupSize].back();
        
        // case 2
        if(lastGroup.size() == groupSize){
            vector&amp;lt;int&amp;gt; newGroup(1, i);
            groupMap[groupSize].push_back(newGroup);
        }
        // case 1
        else{
            lastGroup.push_back(i);
            // groupMap[groupSize].back().push_back(i);
        }
        
    }
    vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; groupThePeople(vector&amp;lt;int&amp;gt;&amp;amp; groupSizes) {
        int len = groupSizes.size();
        for(int i = 0; i&amp;lt;len; i++){
            insertIntoGroup(i, groupSizes[i]);
        }

        vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; answer;
        for(auto &amp;amp;[groupSize, groups] : groupMap){
            for(vector&amp;lt;int&amp;gt; group : groups){
                answer.push_back(group);
            }
        }
        return answer;
    }
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;두 번째 접근&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;첫 번째 버전을 조금 개선했다. answer vector를 미리 선언해 두고, insertIntoGroup() 함수에서 groupMap을 다루는데, 만약 group이 가득차게 되면 answer에 넣는 방식으로 구현했다. 이렇게 하니 runtime 12ms가 나왔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;시간복잡도 자체는 동일하다! 다만 2차원 vector를 1차원으로 줄였고 같은 vector에 계속 접근해 cache쪽에서 성능이 향상된 것이 아닐까 싶다.&lt;/p&gt;
&lt;pre id=&quot;code_1694432712575&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Runtime 12 ms Beats50.27% 
// Memory 13.4 MB Beats 37.1%

class Solution {
public:
    map&amp;lt;int, vector&amp;lt;int&amp;gt;&amp;gt; groupMap; // groupMap[i] : i size짜리 group
    vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; answer;
    // i번째 사람을 groupSize 크기의 group에 넣는 함수
    void insertIntoGroup(int i, int groupSize){
        // 아직 해당 size의 group이 없는 경우 - 생성
        if(groupMap.find(groupSize) == groupMap.end()){
            vector&amp;lt;int&amp;gt; newGroup(0);
            groupMap[groupSize] = newGroup;
        }

        // 해당 size의 group이 있는 경우 - 거기다가 넣음.
        vector&amp;lt;int&amp;gt; &amp;amp;group = groupMap[groupSize]; // groupSize짜리 group
        group.push_back(i);

        if(groupSize == group.size()){
            answer.push_back(group);
            vector&amp;lt;int&amp;gt; newGroup(0);
            group = newGroup;
        }
    }
    vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; groupThePeople(vector&amp;lt;int&amp;gt;&amp;amp; groupSizes) {
        int len = groupSizes.size();
        for(int i = 0; i&amp;lt;len; i++){
            insertIntoGroup(i, groupSizes[i]);
        }
        return answer;
    }
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;세 번째 접근&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;두 번째 접근에서 아이디어를 얻어, 어차피 입력이 정확하다는 것은 보장되므로 groupMap에 모두 다 넣고, 해당 group의 size만큼 파싱하는 방법이다. 이 경우 3ms로 매우 실행시간이 빨라졌는데, 두 번째 접근보다 cache 접근 쪽에서 더 향상된 것이 아닐까 싶다.&lt;/p&gt;
&lt;pre id=&quot;code_1660456126668&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Runtime 3 ms Beats 98.93%
// Memory 13.9 MB Beats 17%

class Solution {
public:
    map&amp;lt;int, vector&amp;lt;int&amp;gt;&amp;gt; groupMap;
    vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; answer;
    vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; groupThePeople(vector&amp;lt;int&amp;gt;&amp;amp; groupSizes) {
        int len = groupSizes.size();
        for(int i = 0; i&amp;lt;len; i++){
            groupMap[groupSizes[i]].push_back(i);
        }

        for(auto &amp;amp;[size, group] : groupMap){
            vector&amp;lt;int&amp;gt; temp;
            int cnt = 0;
            for(int i : group){
                temp.push_back(i);
                cnt++;

                if(cnt == size){
                    answer.push_back(temp);
                    cnt = 0;
                    temp.clear();
                }
            }
        }

        return answer;
    }
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;시간복잡도&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;입력 size가 n일 때, for loop에 O(n)이고, insertIntoGroupMap()에서 O(logn)이 필요하므로 O(nlogn)이다. 세 번째 접근도 동일한 과정을 거치므로 O(nlogn)이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Programmers Lv. 2 Jadencase 문자열 만들기, 12분&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;코테 푸는 느낌 내려고 2시간 내에 lv2 문제 1개(1번 느낌으로) + lv3 문제 3-4개 푸려고 한다. 나중에 가면 lv3가 장난 아니게 어려워지니까 천천히 폼 올려서 감당해 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;첫 접근&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;일단 처음에는 딱 보자마자 생각난, delimiter로 string parsing 후 upper/lower 쓰는 방식을 택했다. string parsing은 &lt;a href=&quot;https://hyelie.tistory.com/entry/PS-%ED%95%98%EB%A9%B4%EC%84%9C-%EC%95%8C%EB%A9%B4-%EC%A2%8B%EC%9D%80-STL%EB%93%A4&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;해당 포스팅&lt;/a&gt; 참조.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;단, 이렇게 풀었더니 하나가 틀렸다. 뭐지 뭐지 고민하다가 다음 문제 풀고 다시 돌아왔는데, 이렇게 풀면 맨 마지막에 있는 공백을 처리하지 못한다. 때문에 예외 케이스로 하나 추가해 줬다.&lt;/p&gt;
&lt;pre id=&quot;code_1660456126668&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;string&amp;gt;
#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;sstream&amp;gt;

using namespace std;

vector&amp;lt;string&amp;gt; split(string s){
    vector&amp;lt;string&amp;gt; result;
    istringstream iss(s);
    string buffer;
    
    while(getline(iss, buffer, ' ')){
        result.push_back(buffer);
    }
    
    return result;
}

string tolower(string s){
    for(char &amp;amp;c : s){
        if(isupper(c)) c = tolower(c);
    }
    return s;
}

string toJadenCase(string s){
    if(islower(s[0])){
        s[0] = toupper(s[0]);
    }
    return s;
}

string solution(string s) {
    vector&amp;lt;string&amp;gt; parsed_string = split(s);
    for(string &amp;amp;s : parsed_string){
        s = tolower(s);
        s = toJadenCase(s);
    }
    
    string answer = &quot;&quot;;
    int len = parsed_string.size();
    for(int i = 0; i&amp;lt;len-1; i++){
        answer += parsed_string[i] + &quot; &quot;;
    }
    answer += parsed_string[len-1];
    if(s[s.length()-1] == ' ') answer += &quot; &quot;;
    
    return answer;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;두 번째 접근&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그러나 이렇게 풀면 맘에 안들긴 하다. 그래서 다른 풀이를 생각해 냈다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;단어의 시작은 공백 뒤에 오기 때문에, 뒤에서부터 검색한다. 만약 s[i]가 문자이고 s[i-1]이 공백이면 upper해주면 된다. 나머지는 모두 tolower() 하면 된다. 끝!&lt;/p&gt;
&lt;pre id=&quot;code_1694437461555&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;string&amp;gt;
#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;sstream&amp;gt;

using namespace std;

string solution(string s) {
    int len = s.length();
    for(int i = len-1; i&amp;gt;=1; i--){
        if(isupper(s[i])) s[i] = tolower(s[i]);
        if(s[i-1] == ' ') s[i] = toupper(s[i]);
    }
    if(islower(s[0])) s[0] = toupper(s[0]);
    return s;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;시간복잡도&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;string s.length()를 n이라고 했을 때 처음부터 끝까지 순회만 하므로 O(n)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Programmers Lv. 3 정수 삼각형, 10분&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;간단한 DP 문제. 8분만에 풀고 넘겼고, 이후 제출 후 틀려서 다시 풀었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;실수했던 점은, edge case는 처리 잘 했는데 일반 case를 처리할 때 for문의 제일 마지막 element가 돌지 않게 설정했음.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;for(int i = 1; i&amp;lt;h-1; i++)로 풀었었다.&lt;br /&gt;for(int i = 1; i&amp;lt;=h-1; i++)로 풀어야 한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1660456126668&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;string&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;iostream&amp;gt;

using namespace std;

int solution(vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; triangle) {
    int n = triangle.size();
    vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; dp(n, vector&amp;lt;int&amp;gt;(n, 0));
    
    dp[0][0] = triangle[0][0];
    for(int h = 1; h&amp;lt;n; h++){ // height
        // 양쪽 끝 edge case 처리
        dp[h][0] = dp[h-1][0] + triangle[h][0];
        dp[h][h] = dp[h-1][h-1] + triangle[h][h];
        for(int i = 1; i&amp;lt;h; i++){ // 각 layer는 위에 2개 보고 큰 것으로 결정 가능
            dp[h][i] = max(dp[h-1][i], dp[h-1][i-1]) + triangle[h][i];
        }
    }
    
    return *max_element(dp[n-1].begin(), dp[n-1].end());
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;시간복잡도&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;n by n size를 모두 탐색하므로 O(n$^2$)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Programmers Lv. 3 이중우선순위큐, 12분&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;몇 가지를 알아둬야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;일단 nlogn 알고리즘의 경우 5백만까지 커버할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;문제 접근은, 최대/최소값 둘 다를 nlogn으로 처리해야 하기 때문에 tree 구조인 map, set을 고려했다. set의 경우 element가 중복되지 않기 때문에 multiset을 써야 한다는 것은 알았지만 STL 이름을 잊어서 map으로 풀었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1660456126668&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;set&amp;gt;
#include &amp;lt;string&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;map&amp;gt;
#include &amp;lt;sstream&amp;gt;
#include &amp;lt;iostream&amp;gt;

using namespace std;

map&amp;lt;int, int&amp;gt; m;

void insertIntoQueue(int n){
    m[n]++;
}

void removeMax(){
    int number = m.rbegin()-&amp;gt;first, count = m.rbegin()-&amp;gt;second;
    if(count == 1) m.erase(number);
    else m[number]--;
}

void removeMin(){
    int number = m.begin()-&amp;gt;first, count = m.begin()-&amp;gt;second;
    if(count == 1 || count == 0) m.erase(number);
    else m[number]--;
}
vector&amp;lt;int&amp;gt; solution(vector&amp;lt;string&amp;gt; arguments) {
    vector&amp;lt;int&amp;gt; answer;
    multiset&amp;lt;int&amp;gt; que;

    string sub;

    for(auto s : arguments) {
        sub =s.substr(0, 2);
        if(sub==&quot;I &quot;) que.insert(stoi(s.substr(2,s.length()-2))); 
        else if(s.substr(2,1)==&quot;1&quot;&amp;amp;&amp;amp;que.size()&amp;gt;0) { que.erase(--que.end()); }
        else if(que.size()&amp;gt;0) { que.erase(que.begin()); }
    }

    if(que.size()==0) { answer.push_back(0); answer.push_back(0); }
    else { 
       answer.push_back(*que.rbegin()); 
        answer.push_back(*que.begin());
    }

    return answer;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;시간복잡도&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;한 operation에 O(logn)이고 operation이 n개이므로 O(nlogn)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;후기&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;map iterator에 접근하는 방법. begin().first로 접근하는 게 아니라 begin()-&amp;gt;first로 접근한다.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;.first는 key, .second는 value&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;rbegin() - --end()라고 생각하면 된다. 제일 뒤에 있는 element.&lt;/li&gt;
&lt;li&gt;istringstream으로 정해져 있는 input 받는 방법 - 기억해 두자.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Programmers Lv. 3 네트워크, 10분&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;BFS를 쓸 줄 알면 풀리는 간단한 문제. 뭐.. 딱히 설명할 것도 없다. indexing만 주의하면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1660456126668&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;string&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;

using namespace std;

int maxn;
vector&amp;lt;int&amp;gt; visited; // visited[i] : i번 computer 썼는지 여부
vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; connected; // connected[i][j] : i번과 j번이 연결되어 있으면 true, else false

// n번째 computer에서 BFS
void BFS(int n){
    queue&amp;lt;int&amp;gt; q;
    q.push(n);
    visited[n] = true;

    while(!q.empty()){
        int cur = q.front(); q.pop();
        visited[cur] = true;

        for(int next = 0; next&amp;lt;maxn; next++){
            if(!visited[next] &amp;amp;&amp;amp; connected[cur][next]){
                q.push(next);
            }
        }
    }
}

int solution(int n, vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; computers) {
    vector&amp;lt;int&amp;gt; visited_init(n+1, false); 
    visited = visited_init;
    connected = computers;
    maxn = n;

    int answer= 0;
    for(int i =0; i&amp;lt;n; i++){
        if(!visited[i]){
            visited[i] = true;
            BFS(i);
            answer++;
        }
    }
    return answer;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;시간복잡도&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;BFS 시간복잡도는 O(V+E)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>PS/PS Log</category>
      <author>hyelie</author>
      <guid isPermaLink="true">https://hyelie.tistory.com/628</guid>
      <comments>https://hyelie.tistory.com/entry/230911-%ED%92%80%EC%97%88%EB%8D%98-%EB%AC%B8%EC%A0%9C%EB%93%A4#entry628comment</comments>
      <pubDate>Mon, 11 Sep 2023 22:17:22 +0900</pubDate>
    </item>
    <item>
      <title>[Model Checking] Transition System과 Program Graph</title>
      <link>https://hyelie.tistory.com/entry/Model-Checking-Transition-System%EA%B3%BC-Program-Graph</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;이 글은 &lt;a href=&quot;https://www.youtube.com/playlist?list=PLwabKnOFhE38C0o6z_bhlF_uOUlblDTjh&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;RWTH AACHEN 대학교 Joost-Pieter Katoen 교수님의 2018년 1학기 Introduction to Model Checking 강의&lt;/a&gt;와 &lt;a href=&quot;https://en.wikipedia.org/wiki/Principles_of_Model_Checking&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Principles of Model Checking&lt;/a&gt;을 기반으로 재구성한 것입니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 글에서는 model checking에서 사용하는 transition system이 대체 뭔지, 그리고 우리가 사용하는 일반적인 프로그램을 transition system으로 바꾸는 방법을 살펴본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Transition System&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;transition system은 &lt;b&gt;directed graph&lt;/b&gt;로 나타낸다. 이 때 graph의 node는 &lt;b&gt;state&lt;/b&gt;를, edge는 &lt;b&gt;transition&lt;/b&gt;을 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Definition. Transition System&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;transition system TS는 (S, Act, &amp;rarr; I, AP, L)의 tuple로 표기한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;`S`는 state의 집합&lt;/li&gt;
&lt;li&gt;`Act`는 action의 집합&lt;/li&gt;
&lt;li&gt;`&amp;rarr;`는 S &amp;times; Act &amp;times; S의 부분집합이며, transition relation을 의미한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;편의상 (s, $\alpha$, s') &amp;isin; &amp;rarr; 대신&lt;/li&gt;
&lt;li&gt;s$\overset{\alpha}{\rightarrow}$s'으로 표기한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 때 s &amp;isin; S, s'&lt;span style=&quot;text-align: left;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;isin; S이고 $\alpha$ &amp;isin; Act이다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;text-align: left;&quot;&gt;state s에서 action $\alpha$를 통해 s'로 transit된다고 이해하면 될 것 같다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;`S$_0$` &amp;sube; S이며, 초기 state를 의미한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;S$_0$의 원소가 하나인 경우는 거기서 시작하지만, 2개 이상인 경우에는 non-deterministic하게 시작 위치가 결정된다. 정말 간단하게 말하자면 random하게 고른다는 뜻이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;`AP`는 Atomic Propisition들의 집합으로, state의 property를 의미한다.&lt;/li&gt;
&lt;li&gt;`L`은 S&lt;span style=&quot;text-align: left;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;rarr; $2^{\text{AP}}$,&lt;/span&gt; labeling function을 의미한다.&lt;span style=&quot;text-align: left;&quot;&gt; 모든 state을 AP의 power set에 매핑하는 것이다.&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;text-align: left;&quot;&gt;state와 property의 매핑이라고 이해하면 된다.&lt;br /&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;text-align: left;&quot;&gt;L(s) = ap라고 하면, state s의 property가 ap라는 뜻이다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 때 TS는 S, Act, AP가 finite면 finite이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Transition System 예시&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;490&quot; data-origin-height=&quot;183&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdPktf/btstwHrZysv/hoRUI4TpZMaoi706Kf030k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdPktf/btstwHrZysv/hoRUI4TpZMaoi706Kf030k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdPktf/btstwHrZysv/hoRUI4TpZMaoi706Kf030k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbdPktf%2FbtstwHrZysv%2FhoRUI4TpZMaoi706Kf030k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;490&quot; height=&quot;183&quot; data-origin-width=&quot;490&quot; data-origin-height=&quot;183&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이런 transition system이 있을 때, 다음과 같이 표기할 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;S = {pay, select, soda, beer}&lt;/li&gt;
&lt;li&gt;Act = {get_soda, get_beer, insert_coin, $\tau$}&lt;/li&gt;
&lt;li&gt;&amp;rarr;는 위 그림의 모든 edge.&lt;/li&gt;
&lt;li&gt;S$_0$&amp;nbsp;= {pay}&lt;/li&gt;
&lt;li&gt;AP와 L은 property를 어떻게 설정하냐에 따라 달라진다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&quot;vending machine이 코인을 넣은 후에만 음료를 준다&quot;라는 property를 택한다고 하자.&lt;/li&gt;
&lt;li&gt;그러면 AP = {pay, drink}로 설정할 수 있고, 이 경우 L(pay) = {pay}, L(soda) = L(beer) = {paid, drink}, L(select) = {paid}로 둘 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Definition. Predecessors and Successors&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;직역하면 전임자와 후임자이다. &lt;span style=&quot;text-align: left;&quot;&gt;s&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&amp;isin; S 그리고&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;text-align: left;&quot;&gt;$\alpha$&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&amp;isin; Act일 때,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&amp;nbsp;s의 $\alpha$-successors는 다음와 같이 정의한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;480&quot; data-origin-height=&quot;54&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Of2Xz/btstCXNwWDJ/YZRLTbLHqc6TKwWuWDONKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Of2Xz/btstCXNwWDJ/YZRLTbLHqc6TKwWuWDONKK/img.png&quot; data-alt=&quot;successors of s&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Of2Xz/btstCXNwWDJ/YZRLTbLHqc6TKwWuWDONKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOf2Xz%2FbtstCXNwWDJ%2FYZRLTbLHqc6TKwWuWDONKK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;480&quot; height=&quot;54&quot; data-origin-width=&quot;480&quot; data-origin-height=&quot;54&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;successors of s&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;어떤 state s에서 action alpha를 통해 s'로 갈 수 있을 때, Post(s, $\alpha$) = s'들의 집합이다. Post(s)는 모든 $\alpha$에 대해 Post(s, $\alpha$)의 합집합이다.&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;graph식 언어로 표기하면, Post(s, $\alpha$)는 state s에서 edge $\alpha$를 통해 갈 수 있는 neighbor들은, Post(s)는 $state s의 모든 neighbor를 말하는 것이다.&amp;nbsp;이렇게 생각하면 predecessors도 바로 이해할 수 있다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;s의 $\alpha$-predecessors는 다음과 같이 정의한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;435&quot; data-origin-height=&quot;63&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bVHvxY/btstHdbv6gJ/WWeMwiHB1D9q4Zi3bie4Bk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bVHvxY/btstHdbv6gJ/WWeMwiHB1D9q4Zi3bie4Bk/img.png&quot; data-alt=&quot;predecessors of s&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bVHvxY/btstHdbv6gJ/WWeMwiHB1D9q4Zi3bie4Bk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbVHvxY%2FbtstHdbv6gJ%2FWWeMwiHB1D9q4Zi3bie4Bk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;435&quot; height=&quot;63&quot; data-origin-width=&quot;435&quot; data-origin-height=&quot;63&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;predecessors of s&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 어떤 state s'에서 alpha를 통해 s로 갈 수 있을 때 &lt;span style=&quot;text-align: start;&quot;&gt;Pre(s', $\alpha$) = s일 때 s'의 집합이다. Pre(s)는 모든 $\alpha$에 대해 Pre(s, $\alpha$)의 합집합이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;Definition. Terminal State&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&amp;nbsp;transition system의 state s에 대해 Post(s) = $\phi$일 때 s를 terminal state이라고 한다.&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;graph식 언어로 표기하자면 outgoing edge가 없는 node를 terminal로 말한다는 것이다.&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;parallel system을 모델링할 때 terminal state는 바람직하지 않은 것으로 간주한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Definition. Execution Fragment&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;finite exeucution fragment는 다음과 같이 정의한다. 이 때 n $\ge$ 0일 때 n을 execution fragment length라고 한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;440&quot; data-origin-height=&quot;28&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MotSx/btstxOKBZlO/HveAOOMKJSdTDZxgFkVPR0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MotSx/btstxOKBZlO/HveAOOMKJSdTDZxgFkVPR0/img.png&quot; data-alt=&quot;finite execution fragment 정의&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MotSx/btstxOKBZlO/HveAOOMKJSdTDZxgFkVPR0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMotSx%2FbtstxOKBZlO%2FHveAOOMKJSdTDZxgFkVPR0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;440&quot; height=&quot;28&quot; data-origin-width=&quot;440&quot; data-origin-height=&quot;28&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;finite execution fragment 정의&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;infinite execution fragment는 다음과 같이 정의한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;414&quot; data-origin-height=&quot;39&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bIjo4F/btstwggPZPX/5RUMDykvixEeAjUQWIhd01/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bIjo4F/btstwggPZPX/5RUMDykvixEeAjUQWIhd01/img.png&quot; data-alt=&quot;infinite execution fragment 정의&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bIjo4F/btstwggPZPX/5RUMDykvixEeAjUQWIhd01/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbIjo4F%2FbtstwggPZPX%2F5RUMDykvixEeAjUQWIhd01%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;414&quot; height=&quot;39&quot; data-origin-width=&quot;414&quot; data-origin-height=&quot;39&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;infinite execution fragment 정의&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp;&lt;/b&gt;infinite execution fragment의 odd-length prefix는 finite execution fragment이다. &lt;b&gt;&amp;nbsp;`??? 여기 잘 모르겠음`&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;graph식 언어로 표기하자면 path라고 생각하면 된다.&amp;nbsp;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Definition. Maximal &amp;amp; Initial Execution Fragment&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;finite execution fragment가 terminal state로 종료되거나, infinite execution fragment인 경우 이들을 &lt;b&gt;maximal execution fragment&lt;/b&gt;라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;execution fragment가 initial state에서 시작하는 경우 &lt;b&gt;initial execution fragment&lt;/b&gt;라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Definition. Transition System Exeuction&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;b&gt;transition system TS의 execution은 initial &amp;amp; maximal execution fragment&lt;/b&gt;다. 즉, initial state에서 시작해야 하며, terminal state로 종료되거나 또는 infinite execution fragment여야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Definition. Reachable States&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;transition system TS의 execution이 있을 때, 다음과 같은 initial, finite execution fragment에 대해 아래 조건을 만족하는 s를 reachable이라고 한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;257&quot; data-origin-height=&quot;42&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MYM9B/btstCGSCfmp/QPKZYwFAEpEsCPsZk22Ick/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MYM9B/btstCGSCfmp/QPKZYwFAEpEsCPsZk22Ick/img.png&quot; data-alt=&quot;transition system reachable 정의&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MYM9B/btstCGSCfmp/QPKZYwFAEpEsCPsZk22Ick/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMYM9B%2FbtstCGSCfmp%2FQPKZYwFAEpEsCPsZk22Ick%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;257&quot; height=&quot;42&quot; data-origin-width=&quot;257&quot; data-origin-height=&quot;42&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;transition system reachable 정의&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;앞서 설명했듯 TS의 execution은 initial &amp;amp; maximal execution fragment이므로 s$_0$ &amp;isin; S$_0$이다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;graph식으로 설명하면, initial node에서 어떤 edge를 거쳐 해당 node까지 도달할 수 있다면 해당 node를 reachable이라고 한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Reach(TS)는 TS의 모든 reachable state를 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;여기까지 Transition System에 대해 살펴봤다. model checking에서 우리가 하고자 하는 것은 어떤 system을 modeling한 transition system TS와, requirement를 model checker에 넣어 OK인지 NO인지 얻어내는 것이다. 지금까지 TS를 살펴봤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;HW/SW modeling&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;46p 참고.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;HW modeling은 transition system을 modeling하는 것과 같다. state를 정의하고, initial state를 정의하고, state에 따라 next input으로 인해 다음 state가 정해지므로, 이를 기반으로 &amp;rarr;를 얻을 수 있다. 이후 AP와 L은 어떻게 설정하느냐에 따라 달라진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Data Dependent System&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Data Dependent System은 conditional branch에 의존하는 system을 의미한다. Data Dependent System을 transition system으로 모델링할 때는 conditional transition을 사용하며, condition으로 label을 지정하는 resulting graph를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;단편적으로만 살펴보면, conditional branch로 나타낸 program graph는 다음과 같은 과정을 통해 transition system으로 변환할 수 있다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;이를 위해 첫째로 program counter에 대해 알아야 하고, 둘째로 관련된 모든 데이터(모든 변수들)를 알아야 한다. 이 2가지를 합쳐 transition system의 state로 표현하고, 조건에 따라 다음 state로 transit하게 만들면 된다.&lt;/li&gt;
&lt;li&gt;program counter의 각 assignment에 해당하는 명령어를 action으로 만든다. 그러면 transition relation이 만들어진다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;일단은 Data Dependent System을 Program Graph로 나타내고, 이를 Transition System으로 변환하는 방법을 살펴보겠다. 그 전에 program graph에서 사용하는 몇 가지 개념부터 살펴보겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Definition. Evaluation function&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;먼저 &lt;b&gt;typed variable&lt;/b&gt;는 variable x와 x의 domain Dom(x)로 이루어진다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;boolean의 경우 Dom(x) = {0, 1}&lt;/li&gt;
&lt;li&gt;integer의 경우 Dom(x) = N(정수)&lt;/li&gt;
&lt;li&gt;와 같이, domain은 해당 변수가 가질 수 있는 값들의 집합이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote style=&quot;text-align: left;&quot; data-ke-style=&quot;style2&quot;&gt;Dom(x)가 0, 1인 boolean variable인 경우 Value는 0, 또는 1을 가지고 있게 된다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 변수를 formalize하기 위해 evaluation function $\eta$를 두어 변수를 evaluate한다.&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;코딩하면서 변수에 값을 넣는다는 개념이 아니라, 변수에 있는 값을 꺼내오는 함수를 만든 것이라고 생각하면 된다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;b&gt;Var를 typed variable들의 집합&lt;/b&gt;, &lt;b&gt;Values&lt;/b&gt;를 $\bigcup_{x \in Var}^{} Dom(x)$(&lt;b&gt;Var에 있는 모든 변수들의 domain의 합집합&lt;/b&gt;)이라고 할 때, evaluation function $\eta$는 Var에서 Values를 매핑하는 함수이다. 이 때 $\eta$는 type consistent하기 때문에 해당 변수의 type을 유지한 채로 값을 꺼내온다. 즉,&lt;span style=&quot;text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;x &amp;isin; Var인 x에 대해&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;$\eta$(x) &amp;isin;&lt;span style=&quot;text-align: left;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;Dom(x)가 된다는 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;b&gt;Eval(Var)는 Var의 evaluation function들의 집합&lt;/b&gt;이라 정의한다.&lt;/span&gt;&lt;span style=&quot;text-align: left;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Definition. Conditions of Typed Variables - Guard&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;앞서 Var를 typed variable들의 집합이라고 했다. &lt;b&gt;Cond(Var)는 Var에 있는 변수들의 boolean condition&lt;/b&gt;을 의미한다. 이를 &lt;b&gt;guard&lt;/b&gt;라고 부르기도 한다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&amp;not;x &amp;and; y &amp;lt; z와 같은 예시들이 있다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Definition. Satisfaction Relation&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;앞에서 Var, Eval(Var), Cond(Var)를 정의했다. 이들을 사용해서&amp;nbsp;조건을 만족하는지 여부를 수식으로 표현할 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Eval(Var)가 Cond(Var)를 만족하는 경우 Eval(Var) $\models$ Cond(Var)로 표기한다.&lt;/li&gt;
&lt;li&gt;Eval(Var)가 Cond(Var)를 만족하지 않는 경우 Eval(Var) $\not\models$ Cond(Var)로 표기한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;아래와 같은 예시들이 있다.&lt;br /&gt;|x = 0, y = 3, z = 6| $\models$ &amp;not;x &amp;and; y &amp;lt; z&lt;br /&gt;|x = 0, y = 3, z = 6| $\not\models$ x &amp;or; y = z&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Definition. Effect function&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;b&gt;effect function은 다음 매핑&lt;/b&gt;을 의미한다: Effect : Act &amp;times; Eval(Var) &amp;rarr; Eval(Var)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;앞서 설명했든 모든 변수들에 있는 값들은 변수와 값을 매핑하는 함수인 Eval()에 의해 결정된다. 즉, 위 수식이 의미하는 것은 &lt;b&gt;기존 변수들의 값&lt;/b&gt;에 &lt;b&gt;Act&lt;/b&gt;(실행해야 하는 코드, assignment에 해당하는 코드)으로 계산한 &lt;b&gt;새로운 변수들의 값&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;$\alpha$가 action(assignment에 해당하는 코드), $\eta$가 evaluation function일 때 표기는 Effect($\alpha$, $\eta$)와 같이 한다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;$\alpha$가 x = 2x + y;인 경우&lt;br /&gt;Effect($alpha$, [x = 1, y = 3]) = [x = 5, y = 3]&lt;br /&gt;&lt;br /&gt;$\alpha$가 x = 2x + y; y = 1-x인 경우&lt;br /&gt;Effect($alpha$, [x = 1, y = 3]) = [x = 5, y = -4]&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Definition. Program Graph&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;program graph PG는 (Loc, Act, Effect, &amp;rarr;, Loc$_0$, g$_0$)으로 표기한다.&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;`Loc`은 location의 (finite) 집합. 각 program graph의 node라고 생각하면 된다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;program의 line은 finite하기 때문이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;`Act`는 action의 집합&lt;/li&gt;
&lt;li&gt;`Effect`는 Act &amp;times; Eval(Var) &amp;rarr; Eval(val)이며, 현재 값으로 다음 값을 계산하는 함수이다.&lt;/li&gt;
&lt;li&gt;`&amp;rarr;`는 Loc &amp;times; Cond(Var) &amp;times; Act &amp;times; Loc이며, conditional transition relation을 의미한다.&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;편의상 &amp;rarr; 대신 $\text{l} \overset{g:\alpha}{\rightarrow} \text{l'}$으로 표기하며, l &amp;isin; Loc, l'&lt;span style=&quot;text-align: left;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;isin; Loc, g &amp;isin; Cond(Var), $\alpha$ &amp;isin; Act이다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;text-align: left;&quot;&gt;(l, g, $\alpha$, l')으로 표기하기도 한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;text-align: left;&quot;&gt;location l에서 g를 만족할 때 $\alpha$를 수행해 l'로 가는 transition을 의미한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;`Loc$_0$` &amp;sube; Loc은 initial location을 의미한다.&lt;/li&gt;
&lt;li&gt;`g$_0$` &amp;isin; Cond(Var)는 initial condition을 의미한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Program Graph &amp;rarr; Transition System으로 변환&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Var을 사용하는 Program Graph의 동작은 아래와 같은 로직을 따라 Transition System Sementics로 바꿀 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;509&quot; data-origin-height=&quot;346&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xFmgp/btstCXUjGvL/9vSxoP6qIKFbiZxlXxm2Fk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xFmgp/btstCXUjGvL/9vSxoP6qIKFbiZxlXxm2Fk/img.png&quot; data-alt=&quot;Program Graph to Transition System&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xFmgp/btstCXUjGvL/9vSxoP6qIKFbiZxlXxm2Fk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxFmgp%2FbtstCXUjGvL%2F9vSxoP6qIKFbiZxlXxm2Fk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;509&quot; height=&quot;346&quot; data-origin-width=&quot;509&quot; data-origin-height=&quot;346&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Program Graph to Transition System&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;state는 2가지를 포함한다: program counter(location)과 variable evaluation이 그것이다. &amp;lt;l, $\eta$&amp;gt;로 표현하며 &lt;b&gt;l은 location&lt;/b&gt;을, &lt;b&gt;$\eta$는&lt;/b&gt; variable을 값으로 매핑하는 함수인 &lt;b&gt;variable evaluation function&lt;/b&gt;을 의미한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;state는 location과 evaluation의 쌍으로 이뤄져 있다!&lt;/li&gt;
&lt;li&gt;때문에 S = Loc &lt;span style=&quot;text-align: left;&quot;&gt;&amp;times;&lt;span&gt; Eval(Var)이다. 이 중 도달 불가능한 state도 있겠지만 가능한 모든 경우를 취한다.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;S$_0$는 {&lt;span style=&quot;text-align: left;&quot;&gt;&amp;lt;l, $\eta$&amp;gt; : l &amp;isin; Loc$_0$, $\eta \models g_0$}로 포현한다.&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;text-align: left;&quot;&gt;초기 location 중 가능한 모든 값과, g$_0$(초기 Cond(Var)) 중 가능한 모든 값들의 cartesian production이다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;text-align: left;&quot;&gt;&amp;rarr; (transition relation)&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;text-align: left;&quot;&gt; 분모의 좌항은 Program Graph의 &amp;rarr;에 해당하는 항으로 conditional transition relation을 의미한다. 여기에서 g를 만족하는 $\eta$만 이 conditional transition을 사용할 수 있기 때문에 g와 and 연산을 취한다.&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;text-align: left;&quot;&gt;즉, g를 만족하는 변수값들만 해당 conditional transition relation을 탈 수 있다는 말이다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;text-align: left;&quot;&gt;분자 부분은 한 state &amp;lt;l, $\eta$&amp;gt;에서 action $\alpha$를 사용해 &amp;lt;l', Effect($\alpha, \eta$)&amp;gt;로 간다는 것을 의미한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;text-align: left;&quot;&gt;이 분수가 의미하는 것은, [분자 부분의 조건이 만족하면 분모 부분의 조건도 만족한다]는 것이다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;text-align: left;&quot;&gt;이 때 Transition System의 transition relation은 해당 분수식, 즉 다음 식을 만족하는 제일 작은 relation이다. - if $\text{l} \overset{g:\alpha}{\rightarrow} \text{l;}$ &amp;and; $\eta \models$ g then &amp;lt;l, $\eta$&amp;gt; $\overset{\alpha}{\rightarrow}$ &amp;lt;l', Effect($\alpha, \eta$)&amp;gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;AP는 Loc &amp;cup; Cond(Var)이다.&lt;/li&gt;
&lt;li&gt;labeling function은 location l과모든 Cond(Var)를 만족하는 현재의 evaluation $\eta$들의 합집합이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위 방법을 사용해 모든 Program Graph를 Transition System으로 바꿀 수 있다. 이 때 evaluation function에서 정수와 같은 무한한 집합을 사용하기 때문에 - Eval(Var)가 무한하기 때문에 state set S도 무한하다. 즉 이렇게 변환한 transition system은 infinite state를 가진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Project/Model Checking</category>
      <author>hyelie</author>
      <guid isPermaLink="true">https://hyelie.tistory.com/627</guid>
      <comments>https://hyelie.tistory.com/entry/Model-Checking-Transition-System%EA%B3%BC-Program-Graph#entry627comment</comments>
      <pubDate>Mon, 11 Sep 2023 03:12:33 +0900</pubDate>
    </item>
    <item>
      <title>23.09.10. 풀었던 문제들</title>
      <link>https://hyelie.tistory.com/entry/230910-%ED%92%80%EC%97%88%EB%8D%98-%EB%AC%B8%EC%A0%9C%EB%93%A4</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;오늘은 프로그래머스 데브코스 PCCP를 풀었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;535&quot; data-origin-height=&quot;304&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oVYlh/btstCDVMUtF/8EWs2ZThpaTK5fVEh7YXEK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oVYlh/btstCDVMUtF/8EWs2ZThpaTK5fVEh7YXEK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oVYlh/btstCDVMUtF/8EWs2ZThpaTK5fVEh7YXEK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoVYlh%2FbtstCDVMUtF%2F8EWs2ZThpaTK5fVEh7YXEK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;535&quot; height=&quot;304&quot; data-origin-width=&quot;535&quot; data-origin-height=&quot;304&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;총 4문제였는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;1번, 2번, 3번을 풀었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;4번은 시간이 없더라. 구현이 너무 복잡했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;4번 문제를 손을 대야 lv4를 받고, 올솔해야 lv5를 받는다... 음... lv5는 기대 안했지만, lv4는 받을 줄 알았는데. 아쉽다.&lt;/p&gt;</description>
      <category>PS/PS Log</category>
      <author>hyelie</author>
      <guid isPermaLink="true">https://hyelie.tistory.com/622</guid>
      <comments>https://hyelie.tistory.com/entry/230910-%ED%92%80%EC%97%88%EB%8D%98-%EB%AC%B8%EC%A0%9C%EB%93%A4#entry622comment</comments>
      <pubDate>Sun, 10 Sep 2023 15:45:19 +0900</pubDate>
    </item>
    <item>
      <title>23.09.09. 풀었던 문제들</title>
      <link>https://hyelie.tistory.com/entry/230909-%ED%92%80%EC%97%88%EB%8D%98-%EB%AC%B8%EC%A0%9C%EB%93%A4</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Programmers PCCP 모의고사 #1 외톨이 알파벳, 5분&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;a href=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/12973&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;프로그래머스 짝지어 제거하기&lt;/a&gt;를 풀어봤다면 바로 이어져 나오는 중복 char를 쉽게 줄일 수 있다. stack을 쓰든, string의 뒤에 중복을 빼고 붙여넣든. 이 방법을 사용하면 중복을 제거할 수 있고, 그러면 map으로 count만 하면 된다. 간단한 손풀기 문제.&lt;/p&gt;
&lt;pre id=&quot;code_1660456126668&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;string&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;map&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;stack&amp;gt;

using namespace std;

string solution(string str) {
    stack&amp;lt;char&amp;gt; stk;
    for(char c : str){
        if(!stk.empty() &amp;amp;&amp;amp; stk.top() == c) continue;
        stk.push(c);
    }
    
    map&amp;lt;char, int&amp;gt; m;
    while(!stk.empty()){
        m[stk.top()]++;
        stk.pop();
    }
    
    string answer = &quot;&quot;;
    for(auto &amp;amp;[key, value] : m) {
        if(value &amp;gt; 1) answer += key;
    }
    sort(answer.begin(), answer.end(), less&amp;lt;char&amp;gt;());
    
    return answer == &quot;&quot; ? &quot;N&quot; : answer;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;시간복잡도&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;string 중복 제거에 O(n), map에 넣는 데 O(n)이 걸린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;Programmers PCCP 모의고사 #1 체육대회, 18분&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;처음에는 이걸 어떻게 시간 내에 풀지..? 하고 나중에 풀었었는데. 그냥 단순한 순열 문제다! 순열은 뭐.. 예전에 &lt;a href=&quot;https://hyelie.tistory.com/entry/%EC%88%9C%EC%97%B4-%EC%A1%B0%ED%95%A9-%EC%A4%91%EB%B3%B5%EC%88%9C%EC%97%B4-%EC%A4%91%EB%B3%B5%EC%A1%B0%ED%95%A9-%EC%BD%94%EB%93%9C&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;포스팅&lt;/a&gt;했었고 별로 어렵지 않게 DFS로 코드를 짤 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;문제 조건은 10이므로 10!, 약 3백만으로 넉넉하게 풀 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1694249568328&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;string&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;iostream&amp;gt;

using namespace std;

// stat[i][j] : i번째 학생이 종목 j의 능력치
// vertex가 
/*
DP vs graph vs BF
앞 단에서 최대값을 택한다고, 전체가 최대가 되는 게 아님. -&amp;gt; BF 써야 하긴 하는데.
worst 10^10
*/

// 일단 DFS
int answer = -1e9;
vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; stats;
vector&amp;lt;bool&amp;gt; isSelected; // isSelected[i] : i번째 학생을 골랐는지 여부
int student_num, event_num;
void DFS(int cur_d, int max_d, int result){
    if(cur_d == max_d){
        answer = max(answer, result);
        return;
    }
    
    for(int i = 0; i&amp;lt;student_num; i++){
        if(!isSelected[i]){
            isSelected[i] = true;
            DFS(cur_d + 1, max_d, result + stats[i][cur_d]);
            isSelected[i] = false;
        }
    }
}


int solution(vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; input) {
    stats = input;
    student_num = input.size();
    event_num = input[0].size();
    isSelected.resize(student_num);
    fill(isSelected.begin(), isSelected.end(), false);
    
    DFS(0, event_num, 0);
    
    return answer;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot;&gt;시간복잡도&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;10!의 순열이므로 O(10!), 약 3백만&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;후기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;일단은 naive하게 되는 대로 DFS로 풀었는데, 바로 맞아서 다행이다. 역시 아무것도 안 하는 것보다는 BF로 부분점수라도 받는 마인드가 맞다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;Programmers PCCP 모의고사 #1 유전법칙, 31분&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;규칙 찾기 문제. 각 그룹은 무조건 1개의 parent와 4개의 child로 이루어져 있으므로 몇 번째 generation의 몇 번째 index인지만 찾으면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;사실 진법 변환이랑 같은 류의 로직. 언제 끝내고 어떻게 처리할지만 잘 처리해 주면 된다. 나는 map에 vector를 넣어서 해결했다!&lt;/p&gt;
&lt;pre id=&quot;code_1694249579018&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;string&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;cmath&amp;gt;
#include &amp;lt;map&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;iostream&amp;gt;

using namespace std;

// 4^15는 1억이 넘으므로 전부 다 계산하고 뽑는 것은 안 된다. 적당히 찾아야 한다.

// record[i] : i+1번째 generation에서 어떤 index를 가지는지
// ex) record[2] == 2 : 3번째 generation에서 index가 2
// 2번째 generation까지 계산함. 1번째 generation은 있기 때문.
vector&amp;lt;int&amp;gt; getRecords(int n, int p){
    vector&amp;lt;int&amp;gt; records; 
    records.push_back((p-1) % 4);
    while(n &amp;gt; 2){
        int before = ceil(((double)p)/4);
        records.push_back((before-1) % 4);
        
        p = before;
        n--;
    }
    reverse(records.begin(), records.end());
    return records;
}

// n : 세대, p : 개체
// 윗세대가 어떤 것인지 찾아야 한다.
string process(int n, int p){
    if(n == 1) return &quot;Rr&quot;;
    
    map&amp;lt;string, vector&amp;lt;string&amp;gt;&amp;gt; m;
    m[&quot;RR&quot;] = {&quot;RR&quot;, &quot;RR&quot;, &quot;RR&quot;, &quot;RR&quot;};
    m[&quot;Rr&quot;] = {&quot;RR&quot;, &quot;Rr&quot;, &quot;Rr&quot;, &quot;rr&quot;};
    m[&quot;rr&quot;] = {&quot;rr&quot;, &quot;rr&quot;, &quot;rr&quot;, &quot;rr&quot;};
    
    vector&amp;lt;int&amp;gt; records = getRecords(n, p);
    
    string cur = &quot;Rr&quot;;
    for(int record : records){
        //cout&amp;lt;&amp;lt;&quot;cur : &quot;&amp;lt;&amp;lt;cur&amp;lt;&amp;lt;&quot;, record : &quot;&amp;lt;&amp;lt;record&amp;lt;&amp;lt;endl;
        cur = m[cur][record];
        
    }
    //cout&amp;lt;&amp;lt;endl;
    
    return cur;
}

vector&amp;lt;string&amp;gt; solution(vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; queries) {
    vector&amp;lt;string&amp;gt; answer;
    for(vector&amp;lt;int&amp;gt; query : queries){
        answer.push_back(process(query[0], query[1]));
    }
    return answer;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot;&gt;시간복잡도&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;n이 15이므로, getRecords() 함수의 while loop는 15번 돈다. 그러니까 O(n). map에 접근하는 것은 map size가 3이므로 사실상 O(1)로 치고. 그러면 O(n)이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;Programmers PCCP 모의고사 #1 운영체제, 38분&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;많이 봤던 simulation 문제. pq를 써서 주어진 대로 풀면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;실수했던 점은, future와 wait 2개의 queue에 다른 우선순위를 적용시켜줘야 했는데, 같은 우선순위를 썼었다. future의 경우에는 빨리 오는 게 먼저 와야 하고, wait queue의 경우에는 점수가 낮은 것이 먼저 와야 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이것 말고는 뭐.. 딱히 실수한 것이 없었고, 잘 구현했던 것 같다. 디버깅도 빨리 해서 다행이다.&lt;/p&gt;
&lt;pre id=&quot;code_1694249588135&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;string&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;iostream&amp;gt;

using namespace std;

typedef long long ll;

struct info{
    int point;
    int income_time;
    int running_time;
};

// 정렬 기준 : 1. 빨리 오는 것 2. 우선순위 높은 것
struct futurecmp{
    bool operator()(info &amp;amp;a, info &amp;amp;b){
        if(a.income_time == b.income_time) return a.point &amp;gt; b.point; // 우선순위 숫자 작은 게 top에
        return a.income_time &amp;gt; b.income_time; // income time 빠른 게 top에
    }
};

// 정렬 기준 : 1. 우선순위 높은 것 2. 빨리 오는 것 
struct waitcmp{
    bool operator()(info &amp;amp;a, info &amp;amp;b){
        if(a.point == b.point) return a.income_time &amp;gt; b.income_time; // income time 빠른 게 top에
        return a.point &amp;gt; b.point; // 우선순위 숫자 작은 게 top에
    }
};

// 초기화 : 시작 시간이 t보다 작은 것들을 waits에 넣음
void pushIntoWaitsLessThanT(int t, priority_queue&amp;lt;info, vector&amp;lt;info&amp;gt;, waitcmp&amp;gt; &amp;amp;waits, priority_queue&amp;lt;info, vector&amp;lt;info&amp;gt;, futurecmp&amp;gt; &amp;amp;future){
    while(1){
        if(future.empty()) break;
        if(future.top().income_time &amp;gt; t) break;
        if(future.top().income_time &amp;lt;= t){
            waits.push(future.top());
            future.pop();
        }
    }
}

vector&amp;lt;long long&amp;gt; solution(vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; program) {
    priority_queue&amp;lt;info, vector&amp;lt;info&amp;gt;, waitcmp&amp;gt; waits; // 실행 대기 중인 thread queue
    priority_queue&amp;lt;info, vector&amp;lt;info&amp;gt;, futurecmp&amp;gt; future; // 미래에 thread queue
    for(vector&amp;lt;int&amp;gt; p : program){
        info i;
        i.point = p[0];
        i.income_time = p[1]; 
        i.running_time = p[2];
        future.push(i);
    }
    
    // 초기화
    int time = 0;
    pushIntoWaitsLessThanT(time, waits, future);
    cout&amp;lt;&amp;lt;endl;
    
    // answer[0] : 모든 프로그램 종료 시간, answer[i] : 점수가 i인 프로그램 대기시간 합
    vector&amp;lt;long long&amp;gt; answer(11, 0);
    while(1){
        if(waits.empty()){
            // 종료조건
            if(future.empty()) break;
            
            // future가 남아있으면 그것 넣음
            waits.push(future.top()); 
            time = future.top().income_time;
            future.pop();
        }
        
        // waits 중 제일 높은 것 실행 중으로 변경
        info cur = waits.top(); 
        waits.pop();
        // cout&amp;lt;&amp;lt;&quot;현재 시간 : &quot;&amp;lt;&amp;lt;time&amp;lt;&amp;lt;endl;
        // cout&amp;lt;&amp;lt;&quot;현재 실행 중 : &quot;&amp;lt;&amp;lt;cur.point&amp;lt;&amp;lt;&quot;, &quot;&amp;lt;&amp;lt;cur.income_time&amp;lt;&amp;lt;&quot;, &quot;&amp;lt;&amp;lt;cur.running_time&amp;lt;&amp;lt;endl;
        
        // 해당 thread의 대기시간 추가
        int wait_time = time - cur.income_time;
        answer[cur.point] += wait_time;
        // cout&amp;lt;&amp;lt;&quot;대기시간 : &quot;&amp;lt;&amp;lt;wait_time&amp;lt;&amp;lt;endl;
        
        time += cur.running_time;
        // cout&amp;lt;&amp;lt;&quot;끝난 시간 : &quot;&amp;lt;&amp;lt;time&amp;lt;&amp;lt;endl;
        pushIntoWaitsLessThanT(time, waits, future);
        
        
        // cout&amp;lt;&amp;lt;endl;
    }
    answer[0] = time;
    
    return answer;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot;&gt;시간복잡도&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;n이 10만이고, pq의 각 연산이 O(logn)이므로 O(nlogn)에 얼추 풀린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;후기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;디버깅이 빨리 끝나서 다행이다. 구조화를 잘 해놔서..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Programmers PCCP 모의고사 #2 실습용 로봇, 10분&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;손 푸는 문제. 2D 좌표에서 이동하는 방법만 알면 된다. 실수했던 건 direction-- 이후 modular 연산을 한 것. C++에서는 음수의 modular 연산을 하면 a = bq + r에서 r = a - bq로 연산한다. 이것 때문에 한 3분? 날린 듯. 그래도 빨리 찾아서 다행이다. 디버깅도 안 찍었고.&lt;/p&gt;
&lt;pre id=&quot;code_1660456126668&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;string&amp;gt;
#include &amp;lt;vector&amp;gt;

using namespace std;

int direction = 0; // dx, dy의 index. R이면 +1, L이면 -1
int dx[4] = {0, 1, 0, -1};
int dy[4] = {1, 0, -1, 0};

vector&amp;lt;int&amp;gt; position = {0, 0};

void rotate(char command){
    if(command == 'R'){
        direction++;
        if(direction == 4) direction = 0;
    }
    else if(command == 'L'){
        direction--;
        if(direction &amp;lt; 0) direction = 3;
    }
}

void move(char command){
    int d = direction;
    if(command == 'B'){
        d += 2;
        d %= 4;
    } 
    position[0] += dx[d];
    position[1] += dy[d];
}

vector&amp;lt;int&amp;gt; solution(string command) {
    for(char c : command){
        if(c == 'R' || c == 'L'){
            rotate(c);
        }
        else if(c == 'G' || c == 'B'){
            move(c);
        }
    }
    return position;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;Programmers PCCP 모의고사 #2 신입사원 교육, 5분&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;문제를 딱 보면 greedy같다는 느낌이 든다. 증명해볼까?&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;점수가 a, b, c, ... 순서로 오름차순 정렬되어 있다고 할 때,&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;a와 b를 합치는 경우 a+b, a+b, c, ...가 된다.&lt;/li&gt;
&lt;li&gt;a와 c를 합치는 경우 a+c, a+c, b, ...가 된다.&lt;/li&gt;
&lt;li&gt;이 둘의 차이는 c-b인데, 이는 무조건 양수이다. 따라서 최소인 것을 고르지 않으면 sum이 최소가 되지 않는다는 것을 proof by contradiction으로 보일 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그러면 min값 2개를 뽑으면 되는데, 이건 pq를 쓰면 쉽다.&lt;/p&gt;
&lt;pre id=&quot;code_1694262921893&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;string&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;queue&amp;gt;

using namespace std;

int solution(vector&amp;lt;int&amp;gt; ability, int n) {
    priority_queue&amp;lt;int, vector&amp;lt;int&amp;gt;, greater&amp;lt;int&amp;gt;&amp;gt; pq;
    for(int a : ability) pq.push(a);
    
    while(n--){
        int first = pq.top(); pq.pop();
        int second = pq.top(); pq.pop();
        pq.push(first + second);
        pq.push(first + second);
    }
    
    int answer = 0;
    while(!pq.empty()){
        answer += pq.top();
        pq.pop();
    }
    return answer;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;시간복잡도&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;ability는 백만, n은 1만. 한 번의 연산에 O(log(1백만))이고 이 연산을 O(n)번 하므로 O(1만 * log(1백만))이다. 여기서 log(1백만)이 20 정도로, O(20만)으로 처리할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;Programmers PCCP 모의고사 #2 실습용 로봇, 18분 풀고 이후 40분, 총 58분&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;처음에는 간단한 시뮬레이션 문제인 줄 알고 풀었는데.. 생각보다 까다로운 문제였다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;중요한 건 예외가 하나 있었다는 건데, 만들고 나가는 사람 + 들어오는 사람이 있으면 무조건 만들고 나가는 사람이 먼저 나간다는 것이다. 때문에 이에 대한 예외 연산을 빼놓지 않고 풀었어야 했다. 다행인 건 예제에 이 edge case를 저격하는 예외가 있었다는 것. 덕분에 쉽게 풀었다.&lt;/p&gt;
&lt;pre id=&quot;code_1694263111625&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;string&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;iostream&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;algorithm&amp;gt;

using namespace std;

int solution(vector&amp;lt;int&amp;gt; menu, vector&amp;lt;int&amp;gt; order, int k) {
    int total_num = order.size();
    
    queue&amp;lt;int&amp;gt; waits; // 대기자 목록. 들어오는 것은 index
    int future_idx = 0; // 제일 근미래에 올 사람의 index
    int time = 0;
    
    int answer = -1;
    
    while(1){
        // 종료조건
        if(time &amp;gt;= total_num * k) break;
        
        // time보다 빨리 와서 그동안 줄을 선 손님들 waitQ에 추가
        while(1){
            if(future_idx &amp;lt; total_num &amp;amp;&amp;amp; future_idx * k &amp;lt;= time){
                waits.push(future_idx);
                future_idx++;
            }
            else break;
        }
        
        // 만듬
        if(!waits.empty()){
            time += menu[order[waits.front()]];
        }
        
        // 만들 동안 와서 그동안 줄을 선 손님들 waitQ에 추가
        while(1){
            if(future_idx &amp;lt; total_num &amp;amp;&amp;amp; future_idx * k &amp;lt;= time){
                waits.push(future_idx);
                future_idx++;
            }
            else break;
        }
        
        // 대기열 계산. 단, 만들고 나간 시간 == 들어온 시간이면 1을 빼 주어야 함.
        int isDuplicated = (time%k == 0 ? 1 : 0);
        answer = max(answer, (int)waits.size() - isDuplicated);
        
        // 만들었다면, 빼고, 그렇지 않다면 타임트립.
        if(!waits.empty()) waits.pop();
        else{
            time = future_idx * k;
        }
    }
    
    return answer;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;시간복잡도&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;menu length는 100, order length는 1만으로, time을 1씩 늘려가면서 계산해도 1백만 정도로 시간은 여유롭다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;후기&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 문제는 진짜 아슬아슬했다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;Programmers PCCP 모의고사 #2 보물 지도, 50분&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;a href=&quot;https://www.acmicpc.net/problem/2206&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Acmicpc 벽 부수고 이동하기&lt;/a&gt;를 풀어봤다면 매우 쉽게 접근할 수 있는 문제. 아이템의 사용 여부도 visited 배열에 넣음으로써 쉽게 풀 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;단.. 이 문제의 경우 (x, y) 좌표로 주어지는데, 이를 (r, c) 좌표로 변환해야 했다. 이것 때문에 시간을 많이 잡아먹었다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;처음에 hole 위치를 초기화 할 때 seg fault가 나는 것을 보고 엥? 싶었는데.. 이런... 역시 문제를 잘 읽어야 한다. 항상 인지하고 문제를 읽지만 이런 &quot;당연히 이러겠지&quot; 싶은 것에서 항상 뒤통수를 맞는 것 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1694263210043&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;string&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;iostream&amp;gt;
using namespace std;

int INF = 1e9;
int dr[4] = {1, 0, -1, 0};
int dc[4] = {0, 1, 0, -1};

struct info{
    int r;
    int c;
    int used;
    int dist;
};

int solution(int n, int m, vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; holes) {
    vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; board(m, vector&amp;lt;int&amp;gt;(n, 0)); // 0 : 빈칸, 1 : 함정
    // (m-1, 0)부터 (0, n-1)까지 가야 함.
    for(vector&amp;lt;int&amp;gt; hole : holes){
        int r = m - hole[1];
        int c = hole[0]-1;
        // cout&amp;lt;&amp;lt;r&amp;lt;&amp;lt;&quot;, &quot;&amp;lt;&amp;lt;c&amp;lt;&amp;lt;endl;
        board[r][c] = 1;
    }
    
    vector&amp;lt;vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt;&amp;gt; visited(m, vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt;(n, vector&amp;lt;int&amp;gt;(2, 0)));
    // visited[r][c][u] : (r, c) 위치를 방문했는지 여부. u가 1이면 신발 쓴 것이고, 0 신발 안 쓴 것.
    queue&amp;lt;info&amp;gt; q;
    info i; i.r = m-1; i.c = 0; i.used=0; i.dist=0;
    q.push(i);
    visited[m-1][0][0] = true;
    int answer = INF;
    while(!q.empty()){
        info cur = q.front(); q.pop();
        // cout&amp;lt;&amp;lt;&quot;현재위치 : &quot;&amp;lt;&amp;lt;cur.r&amp;lt;&amp;lt;&quot;, &quot;&amp;lt;&amp;lt;cur.c&amp;lt;&amp;lt;&quot;, 사용여부 : &quot;&amp;lt;&amp;lt;cur.used&amp;lt;&amp;lt;&quot;, dist : &quot;&amp;lt;&amp;lt;cur.dist&amp;lt;&amp;lt;endl;
        
        // 종료조건
        if(cur.r == 0 &amp;amp;&amp;amp; cur.c == n-1) answer = min(answer, cur.dist);
        
        // 안 쓰고 넘어가는 경우
        for(int d = 0; d&amp;lt;4; d++){
            int nr = cur.r + dr[d];
            int nc = cur.c + dc[d];
            if(0 &amp;lt;= nr &amp;amp;&amp;amp; nr &amp;lt; m &amp;amp;&amp;amp; 0 &amp;lt;= nc &amp;amp;&amp;amp; nc &amp;lt; n &amp;amp;&amp;amp; board[nr][nc] == 0 &amp;amp;&amp;amp; !visited[nr][nc][cur.used]){
                // 사용 여부는 이전 상태 유지
                visited[nr][nc][cur.used] = true;
                info next; next.r = nr; next.c = nc; next.used = cur.used; next.dist = cur.dist+1;
                q.push(next);
            }
        }
        
        // 쓰고 넘어가는 경우
        if(cur.used == 1) continue;
        for(int d = 0; d&amp;lt;4; d++){
            int nr = cur.r + 2*dr[d];
            int nc = cur.c + 2*dc[d];
            if(0 &amp;lt;= nr &amp;amp;&amp;amp; nr &amp;lt; m &amp;amp;&amp;amp; 0 &amp;lt;= nc &amp;amp;&amp;amp; nc &amp;lt; n &amp;amp;&amp;amp; board[nr][nc] == 0 &amp;amp;&amp;amp; !visited[nr][nc][1]){
                visited[nr][nc][1] = true;
                info next; next.r = nr; next.c = nc; next.used = 1; next.dist = cur.dist+1;
                q.push(next);
            }
        }

    }

    return answer == INF ? -1 : answer;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;시간복잡도&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;m, n이 각각 1000 총 vertex size는 mn이고, O(1백만)이고,&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;각 vertex당 아이템을 사용하지 않을 때 4개 방향 + 아이템을 사용할 때 4개 방향으로 8개의 edge가 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;BFS의 시간복잡도는 O(V+E)이므로 O(9mn), O(mn)이다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;후기&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;왜 문제를 (x, y)로 냈을 까... 그냥 푸는 사람들을 괴롭히기 위해서가 아닐까?&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Leetcode 377. Combination Sum IV, 30분&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;첫 접근 : duplicated permutation&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;일단은 문제를 딱 보고... [순서를 신경쓰는 + 합이 k가 되게 만드는 조합]이라길래 중복순열 딱 생각나서 중복순열로 풀었따. 그러나 문제의 input은 nums.size()가 200이기 때문에... 절대 풀 수 없다. 중복순열의 시간복잡도는 O(n$^n$)이니까. DFS 스택이 너무 많이 터져서 MLE가 떴다.&lt;/p&gt;
&lt;pre id=&quot;code_1694264937554&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Solution {
public:
    vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; answer;
    vector&amp;lt;int&amp;gt; nums;
    int target;
    void duplicatePermutation(int cur_d, vector&amp;lt;int&amp;gt; result, int sum){
        if(sum == target){
            answer.push_back(result);
            return;
        }

        for(int i = 0; i&amp;lt;nums.size(); i++){
            if(nums[i] + sum &amp;gt; target) continue;

            result.push_back(nums[i]);
            duplicatePermutation(cur_d + 1, result, sum + nums[i]);
            result.pop_back();
        }
    }
    int combinationSum4(vector&amp;lt;int&amp;gt;&amp;amp; i_nums, int i_target) {
        nums = i_nums;
        target = i_target;
        sort(nums.begin(), nums.end(), less&amp;lt;int&amp;gt;());

        duplicatePermutation(0, {}, 0);

        return answer.size();
    }
};

/*
중복순열 문제 같은데. + 합이 k가 되는...
*/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;두 번째 접근 : DP&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;다른 방법이 필요하다... DFS의 tree를 보면 sum이 같을 때, 같은 연산을 반복하는 &lt;b&gt;중복&lt;/b&gt;이 꽤나 발생하는 것을 알 수 있다. 이를 줄이기 위해서는? memoization이다. memozation? DP다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;앞선 관측에서 중복되는 부분은 [남은 값]으로 판별했다. 그러면, dp[i]를, [남은 값이 i일 때 만들 수 있는 경우의 수]로 세우면 될 것이다! 이것만 만들면 일사천리다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;점화식은 다음과 같으며, 이를 코드로 나타내면 된다. dp[0] = 1이니까 top-down이 쉬울 것 같다!&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;dp[0] = 1. (초기값)&lt;/li&gt;
&lt;li&gt;dp[i] = 모든 n에 대해 dp[i-n]의 합&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1660456126668&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Runtime 3 ms Beats 50.37%
// Memory 6.7 MB Beats 20.91%

class Solution {
public:
    vector&amp;lt;int&amp;gt; nums;
    int INF = -1;
    vector&amp;lt;int&amp;gt; dp;

    int recurse(int remain){
        if(dp[remain] != INF) return dp[remain];

        int num_cases = 0;
        for(int n : nums){
            if(n &amp;gt; remain) break;
            num_cases += recurse(remain - n);
        }
        dp[remain] = num_cases;
        return dp[remain];
    }
    int combinationSum4(vector&amp;lt;int&amp;gt;&amp;amp; i_nums, int target) {
        nums = i_nums;
        dp.resize(target + 1, INF);
        dp[0] = 1;
        sort(nums.begin(), nums.end(), less&amp;lt;int&amp;gt;());

        return recurse(target);
    }
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;시간복잡도&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;nums.size()가 n, target이 t일 때, recurse()를 수행하기 위해서 O(n)이 걸리고, 모든 dp 배열을 채우기 위해서 O(t)가 필요하므로 O(nt)이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;후기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;dp 으악&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>PS/PS Log</category>
      <category>ㅜ</category>
      <author>hyelie</author>
      <guid isPermaLink="true">https://hyelie.tistory.com/621</guid>
      <comments>https://hyelie.tistory.com/entry/230909-%ED%92%80%EC%97%88%EB%8D%98-%EB%AC%B8%EC%A0%9C%EB%93%A4#entry621comment</comments>
      <pubDate>Sat, 9 Sep 2023 22:18:07 +0900</pubDate>
    </item>
    <item>
      <title>23.09.08. 풀었던 문제들</title>
      <link>https://hyelie.tistory.com/entry/230908-%ED%92%80%EC%97%88%EB%8D%98-%EB%AC%B8%EC%A0%9C%EB%93%A4</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Programmers 자물쇠와 열쇠, 53분&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;자물쇠에 열쇠를 넣어서 맞는지 보면 되는 문제. 문제 접근 자체는, N과 M이 20으로 매우 작아서 brute-force로 다 돌릴 수 있었고, 결국 구현 문제였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;열쇠를 특정 크기로 자르는 것은 매우 귀찮고 힘들기 때문에, 그렇게 하는 것보다는 아래와 같이 for문으로 돌리는 것이 편하다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;총 N + 2M - 2 크기의 배열을 만들고, 가운데에 자물쇠를 배치한다. 자물쇠에 해당하는 좌표는 [M-1, M-1]부터 [N+M-2, N+M-2]까지다.&lt;/li&gt;
&lt;li&gt;이후 열쇠를 모든 위치에 두고, 자물쇠를 해제할 수 있는지 본다. 열쇠는 [0, 0]부터 [N+M-1, N+M-1]까지 가능하다.&lt;/li&gt;
&lt;li&gt;열쇠 위치를 옮겨가면서, 자물쇠에 해당하는 모든 좌표들의 숫자가 1이면 OK이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1660456126668&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;string&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;iostream&amp;gt;

using namespace std;

int N, M;

// 오른쪽으로 90도 회전
vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; rotate(vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; &amp;amp;key){
    vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; rotated_key(M, vector&amp;lt;int&amp;gt;(M, 0));
    for(int i = 0; i&amp;lt;M; i++){
        for(int j = 0; j&amp;lt;M; j++){
            rotated_key[j][M-1-i] = key[i][j];
        }
    }
    return rotated_key;
}

// total 초기화 함수
// total 중에서 lock의 시작 위치는 (M-1, M-1)부터 (M+N-2, M+N-2)까지임.
void initTotal(vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; &amp;amp;total, vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; &amp;amp;lock){
    for(int i = 0; i&amp;lt;N; i++){
        for(int j = 0; j&amp;lt;N; j++){
            total[i+M-1][j+M-1] = lock[i][j];
        }
    }
}

// lock이 가득 찼는지 확인하는 함수
// total 중에서 lock의 시작 위치는 (M-1, M-1)부터 (M+N-2, M+N-2)까지임.
bool isLockFull(vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; &amp;amp;total){
    int end = M+N-2;
    for(int i = M-1; i&amp;lt;=end; i++){
        for(int j = M-1; j&amp;lt;=end; j++){
            if(total[i][j] != 1) return false;
        }
    }
    return true;
}

// total에서 key의 시작 위치가 (r, c)일 때 key를 더하고 확인하는 함수
bool isMatch(vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; total, vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt;&amp;amp; key, int r, int c){
    for(int i = 0; i&amp;lt;M; i++){
        for(int j = 0; j&amp;lt;M; j++){
            total[i+r][j+c] += key[i][j];
        }
    }
    return isLockFull(total);
}


bool solution(vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; key, vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; lock) {
    M = key.size(); N = lock.size();
    
    // 회전된 key들 생성
    vector&amp;lt;vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt;&amp;gt; keys;
    keys.push_back(key);
    for(int i = 0; i&amp;lt;3; i++){
        key = rotate(key);
        keys.push_back(key);
    }
    
    // size 충분한 한 배열 생성. 가운데에 lock이 들어가고, key들은 위치를 옮겨가며 가득 차는지 볼 것임.
    int total_size = N+2*M-2;
    vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; total(total_size, vector&amp;lt;int&amp;gt;(total_size));
    initTotal(total, lock);
    
    // key의 시작 위치는 (0, 0부터) (M+N-1, M+N-1)까지 가능.
    int end = M+N-1;
    for(int i = 0; i&amp;lt;end; i++){
        for(int j = 0; j&amp;lt;end; j++){
            for(int k = 0; k&amp;lt;4; k++){
                if(isMatch(total, keys[k], i, j)) return true;
            }
        }
    }
    return false;
}
/*
가운데 lock 두고
상하좌우에 key만큼 size 추가함
그러면 N + 2M짜리. 
*/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;시간복잡도&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;키가 열쇠에 맞는지 확인하는 로직이 O(N$^2$), 키를 옮길 때 O((M+N)$^2$)가 걸린다. O((M+N)$^2$)이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;공간복잡도&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;추가 공간은 O((M+N)$^2$)만큼 쓴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;후기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;53분... 너무 오래 걸렸다. 일단 실수한 건, 좌표를 확실하게 생각하지 않고 시작한 것. 이게 열쇠가 들어갈 공간을 M-1 by M-1로 겹쳐 두니 계산이 조금 힘들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;또, 다른 실수한 것은 lock이 가득 찼는지 확인하는 함수에서 != 1로 해야 하는데 != 0으로 했던 것도 실수.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이런 문제를 35분 내에 풀어야 하는데.. 구현 문제 속도는 언제쯤 빨라질까. 그래도 모듈화를 잘 해놨어서 실수를 빨리 잡긴 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Programmers GPS&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;문제를 처음 딱 보면 brute-force인 것 같다. bellman ford부터 접근할 생각을 하는데, 그러면 안 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;sweeping 느낌으로 DP를 써야 한다. 점화식은 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;dp[t][l] : 시간 t에서 위치가 l일 때 최소로 고치는 개수라고 두자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그러면 점화식은, 모든 dp[t-1][l의 neighbor]에 대해 gps_log[t] == l의 neighbor이면 +0, 아니면 +1. 그 중에서 최소값을 뽑으면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;점화식 자체는 간단한데, 이걸 생각하는 과정이... 모든 DP 문제가 그렇듯 점화식을 세우는 게 어렵다.&lt;/p&gt;
&lt;pre id=&quot;code_1660456126668&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;vector&amp;gt;

using namespace std;

/*
거점 개수가 200개
도로 개수는 10000개
BF같은데
dp[t][l] : 시간 t에서 위치가 l일 때
모든 dp[t-1][l의 neighbor]에 대해서 (이전단계)
gps_log[t] == l이면 +1
아니면 +0

*/

int INF = 1e9;

// 전역 변수를 정의할 경우 함수 내에 초기화 코드를 꼭 작성해주세요.
int solution(int n, int m, vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; edge_list, int k, vector&amp;lt;int&amp;gt; gps_log) {
    vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; edges(n+1);
    for(vector&amp;lt;int&amp;gt; e : edge_list){
        edges[e[0]].push_back(e[1]);
        edges[e[1]].push_back(e[0]);
    }
    for(int i = 1; i&amp;lt;=n; i++){
        edges[i].push_back(i);
    }
    
    vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; dp(k, vector&amp;lt;int&amp;gt;(n+1, INF));
    dp[0][gps_log[0]] = 0;
    for(int t = 1; t&amp;lt;k; t++){
        for(int l = 1; l&amp;lt;=n; l++){
            int isLonT = gps_log[t] == l ? 0 : 1;
            for(int neighbor : edges[l]){
                dp[t][l] = min(dp[t-1][neighbor] + isLonT, dp[t][l]);
            }
        }
    }
    
    if(dp[k-1][gps_log[k-1]] &amp;gt;= INF) return -1;
    return dp[k-1][gps_log[k-1]];
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;시간복잡도&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;모든 DP를 채우는 데 O(k$^2$n) 만큼의 시간이 든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;공간복잡도&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;추가 공간은 O(kn)과 O(n)만큼 쓰니까 O(kn).&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;후기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;어떻게 이런 문제가 lv 3에 있는 건지 모르겠다. DP인 걸 알 수 있는 단서가 너무나도 적은데..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Programmers 다리를 지나는 트럭, 20분&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;문제에서 주는 대로 구현만 하면 되는 문제. queue를 사용하면 굉장히 편?리하게 풀 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1660456126668&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;string&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;vector&amp;gt;

using namespace std;

typedef pair&amp;lt;int, int&amp;gt; pii;
int solution(int len, int weight, vector&amp;lt;int&amp;gt; truck_weights) {
    int cur_sum = 0; // 현재 다리 위에 있는 트럭 무게
    queue&amp;lt;pii&amp;gt; curs; //.first : 무게, .second : 들어온 시간 t
    
    // 대기열
    queue&amp;lt;int&amp;gt; waits;
    for(int wait : truck_weights) waits.push(wait);
    
    int t = 1;
    while(1){
        // 다리에서 나가는 경우
        if(!curs.empty() &amp;amp;&amp;amp; t &amp;gt;= curs.front().second + len){
            cur_sum -= curs.front().first;
            curs.pop();
        }
        
        // 다리에 진입하는 경우
        if(curs.size() &amp;lt; len &amp;amp;&amp;amp; !waits.empty() &amp;amp;&amp;amp; cur_sum + waits.front() &amp;lt;= weight){
            curs.push({waits.front(), t});
            cur_sum += waits.front();
            waits.pop();
        }
        
        // 종료조건
        if(waits.size() == 0){
            return t + len;
        }
        
        t++;
    }
    return t;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;시간복잡도&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;while문은 worst case 1억 정도로 돈다. 만약 최적화가 필요하면, 다리에 진입하는 트럭이 없는 경우에 curs.front()를 내보내는 시간으로 설정하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;후기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이건 왜이렇게 빨리 풀렸지?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Programmers 짝지어 제거하기, 35분&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;첫 번째 접근은 s.substr()을 이용해서 겹치는 부분을 삭제하고, index를 앞으로 당기는 방법이었다. 시간복잡도가 O(n)일 것이라 생각했으나... substr() 함수는 문자열을 자르고 복사하기 때문에 O(n)만큼의 시간이 걸렸다. 때문에 다른 방법을 택해야 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;stack을 사용하면 아주 쉽게 풀린다. 설명할 필요도 없고, 코드 보면 바로 이해가 될 것이다.&lt;/p&gt;
&lt;pre id=&quot;code_1660456126668&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;iostream&amp;gt;
#include &amp;lt;stack&amp;gt;
#include&amp;lt;string&amp;gt;
using namespace std;

int solution(string s)
{
    stack&amp;lt;char&amp;gt; stk;
    for(char c : s){
        if(!stk.empty() &amp;amp;&amp;amp; stk.top() == c){
            stk.pop();
        }
        else stk.push(c);
    }
    
    return stk.empty();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;시간복잡도&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;s를 순회하므로 O(n)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;공간복잡도&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;stack size는 worst case O(n)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;후기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;안 되는 풀이를 너무 오래 붙들고 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Leetcode 118, Pascal's Triancle, 10분&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그냥 배열 쓸 줄 알면 풀리는 문제.&lt;/p&gt;
&lt;pre id=&quot;code_1660456126668&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Runtime 0 ms Beats 100%
// Memory 6.9 MB Beats 20.86%

class Solution {
public:
    vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; generate(int n) {
        vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; answer;
        answer.push_back({1});

        for(int l = 1; l&amp;lt;n; l++){
            vector&amp;lt;int&amp;gt; layer(l+1);
            layer[0] = layer[l] = 1;
            for(int i = 1; i&amp;lt;l; i++){
                layer[i] = answer[l-1][i] + answer[l-1][i-1];
            }
            answer.push_back(layer);
        }

        return answer;
    }
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;시간복잡도&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;2차원 배열 채우는 거니 O(n^$2$).&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>PS/PS Log</category>
      <author>hyelie</author>
      <guid isPermaLink="true">https://hyelie.tistory.com/620</guid>
      <comments>https://hyelie.tistory.com/entry/230908-%ED%92%80%EC%97%88%EB%8D%98-%EB%AC%B8%EC%A0%9C%EB%93%A4#entry620comment</comments>
      <pubDate>Sat, 9 Sep 2023 01:31:22 +0900</pubDate>
    </item>
    <item>
      <title>23.09.07. 복학 후 계획 - 끝!</title>
      <link>https://hyelie.tistory.com/entry/230907-%EB%B3%B5%ED%95%99-%ED%9B%84-%EA%B3%84%ED%9A%8D</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;당분간 할 일들&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Naver2Tistory 리팩토링 과정/선택/결과 블로그에 포스팅하기 - 23.09.05. &lt;b&gt;끝!&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;large query vs small 2 query 실험하기 - 23.09.06. &lt;b&gt;끝!&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;위 2개 끝나면 포폴 다듬기 - 전역/복학한 내용 + resume만 고치고 업로드하면 됨. - 23.09.07.&lt;b&gt; 끝!&lt;/b&gt;&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Naver2Tistory 부분 다듬기 + 포스팅 링크도 달기&lt;b&gt; - 끝!&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;다 끝나면 plan에 있는 포스팅 싹 읽으면서 정리하고, 당분간 또 할 일들 정리하기. - 23.09.07. &lt;b&gt;끝!&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후에 할 일들&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;하반기 채용 기업들 &lt;b&gt;지원서&lt;/b&gt; 쓰기 - 끝!&lt;/li&gt;
&lt;li&gt;지원서 쓴 이후 &lt;b&gt;코테&lt;/b&gt; 공부하기 (하반기 공채가 시작했으니 다시 달려보자.) - 끝!
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;아마 프로그래머스 부계정 판 거로 풀어갈 것 같다. lv2 + lv3 350문제 정도였던 것 같은데... 해보자.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메이커스페스 3D 프린터로 하우징 출력하기&lt;/li&gt;
&lt;li&gt;과제연구 진행하기 - 끝!&lt;/li&gt;
&lt;li&gt;DB 정리 - 코테를 패스하고, 면접을 가면 CS 공부하면서 채워봐야 할 것 같다. 최후순위로 밀린 것이라.. - &lt;b&gt;끝!&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>내가 하고싶은 것!/Plan</category>
      <author>hyelie</author>
      <guid isPermaLink="true">https://hyelie.tistory.com/619</guid>
      <comments>https://hyelie.tistory.com/entry/230907-%EB%B3%B5%ED%95%99-%ED%9B%84-%EA%B3%84%ED%9A%8D#entry619comment</comments>
      <pubDate>Thu, 7 Sep 2023 18:06:19 +0900</pubDate>
    </item>
    <item>
      <title>[N2T] 리팩토링 기록</title>
      <link>https://hyelie.tistory.com/entry/N2T-%EB%A6%AC%ED%8C%A9%ED%86%A0%EB%A7%81-%EA%B8%B0%EB%A1%9D</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;리팩토링 하게 된 계기&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;개발 동기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;a href=&quot;https://github.com/hyelie/Naver2Tistory&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Naver2Tistory&lt;/a&gt;는 사실 내가 사용하기 위해 만든 프로그램이다. 2022년 10월쯤에 개발 블로그를 네이버에서 티스토리로 옮기기로 결정하면서 안에 있는 포스팅들을 다 가져오고 싶었는데, 그동안 작성했던 포스팅이 약 300개 가량 되다 보니 수작업으로 일일히 옮길 수 없었다. 이를 위해 Naver2Tistory를 당시 내가 구현할 수 있는 수준으로 만들었었다. 만들다 보니 블로그를 나만 옮기는 게 아니니까 다른 사람들도 쓸 수 있겠다 싶어 리드미도 열심히 꾸몄고&amp;nbsp;형식을 맞춰서 오픈소스로 배포도 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;기존 Naver2Tistory의 문제점&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이후에 Clean Code, Clean Architecture 같은 개발 서적을 읽고, 객체지향의 5대 원칙을 공부하면서 내가 짰던 코드가 많이 부족함을 느꼈다. 리팩토링을 시작했을 때 코드를 읽어봤을 때 모듈화만 적당히 잘 되었지 아래와 같은 단점들이 보였다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;필요없는 주석들이 너무 많았다. 주석은 유지보수되지 않는 경우가 대부분이므로 없는 것이 더 낫다.&lt;/li&gt;
&lt;li&gt;예외처리가 너무 난잡하다. try-catch문을 너무 많이 사용해 가독성이 떨어진다는 느낌을 받았다.&lt;/li&gt;
&lt;li&gt;concrete class에 의존한다. 때문에 확장성이 없다시피 했으며 유지보수도 힘들었다.&lt;/li&gt;
&lt;li&gt;if-else문으로 대부분의 로직을 처리한다.&lt;/li&gt;
&lt;li&gt;테스트 코드가 없다. 때문에 소스코드를 수정한 후 검증하는 과정이 오래 걸린다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Group 2682.png&quot; data-origin-width=&quot;910&quot; data-origin-height=&quot;475&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IyFuO/btstayuPzGW/xmSCqDvdiPGDFMLFD2Xjuk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IyFuO/btstayuPzGW/xmSCqDvdiPGDFMLFD2Xjuk/img.png&quot; data-alt=&quot;기존 Naver2Tistory의 dependency tree&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IyFuO/btstayuPzGW/xmSCqDvdiPGDFMLFD2Xjuk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIyFuO%2FbtstayuPzGW%2FxmSCqDvdiPGDFMLFD2Xjuk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;603&quot; height=&quot;315&quot; data-filename=&quot;Group 2682.png&quot; data-origin-width=&quot;910&quot; data-origin-height=&quot;475&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;기존 Naver2Tistory의 dependency tree&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;position: absolute;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;예시 - 너무 긴 if-else문&lt;/h4&gt;
&lt;pre id=&quot;code_1693847822829&quot; class=&quot;aspectj&quot; style=&quot;text-align: start;&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Converter {
    /**
     * Traverse childs of given parameter 'elements'.
     * While traversal, reform it's HTML element to Tistory style.
     * 
     * @param elements is which want to traverse.
     * @see Converter#convertTable(Element)
     * @see Converter#convertQUOTATION(Element)
     * @see Converter#convertTEXT(Element)
     * @see Converter#convertCODE(Element)
     * @see Converter#convertIMAGE(Element)
     * @see Converter#convertHORIZONTALLINE(Element)
     * @see Converter#convertLINK(Element)
     */
    private void dfsDOM(Elements elements) {
        for(Element element : elements){
            ContentType elementContentType = getContentType(element);

            // If section type exists, then stylize
            if(elementContentType == ContentType.TABLE){
                this.convertTable(element);
            }
            else if(elementContentType == ContentType.QUOTATION){
                this.convertQUOTATION(element);
            }
            else if(elementContentType == ContentType.TEXT){
                this.convertTEXT(element);
            }
            
            else if(elementContentType == ContentType.CODE){
                this.convertCODE(element);
            }
            
            else if(elementContentType == ContentType.IMAGE){
                this.convertIMAGE(element);
            }
            
            else if(elementContentType == ContentType.HORIZONTALLINE){
                this.convertHORIZONTALLINE(element);
            }
            
            else if(elementContentType == ContentType.LINK){
                this.convertLINK(element);
            }

            // else then traverse deeper.
            else{ // elementContentType == ContentType.NOTHING
                dfsDOM(element.children());
            }
        }
    }
    // ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;기존에는 table, quotation, text, code, image, horizontal line, link 정도의 스타일을 지원했다. 이 때&amp;nbsp;&lt;span style=&quot;text-align: start;&quot;&gt;버전업을 하면서 새로운 스타일을 추가한다고 하자.&amp;nbsp;그러면 if문에 또 붙어야 하고, 해당하는 함수를 만들어야 한다. 숫자가 적을 때는 괜찮겠지만, 만약 모든 HTML의 스타일을 사용하게 된다면? if-else문만 엄청나게 길어질 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;수정을 할 때 Converter 객체를 직접 수정해야 하기 때문에 SOLID 원칙에도 위배된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;목표&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이러한 문제들을 해결하기 위해 다음과 같은 목표를 세우고 리팩토링했다. 클린 코드에서 읽었던 [주석 최소화, 경계 처리, 테스트 코드 작성]을 적용하고자 했다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;주석 삭제. 의도를 설명한 주석, 유지보수 시 참고가 될 만한 주석만 남기고 모두 삭제. JavaDoc은 필요한 public 함수에만 남기고자 했다.&lt;/li&gt;
&lt;li&gt;예외처리를 간소하게. 곳곳에 퍼져 있는 try-catch문을 응집시키고자 했다.&lt;/li&gt;
&lt;li&gt;최대한 의존성을 줄이고&lt;b&gt;&amp;nbsp;확장성 있게&lt;/b&gt;&amp;nbsp;코드를 작성하고자 했다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 프로그램은 Naver2Tistory이지만 추후에 Naver2Notion처럼 목적지 블로그를 추가할 수도 있고, Medium2Tistory처럼 출발지 블로그를 추가할 수도 있다고 가정했다.&lt;/li&gt;
&lt;li&gt;현재 지원하는 스타일 이외에 다른 스타일들이 추가될 수 있다고 가정했다.&lt;/li&gt;
&lt;li&gt;의존성을 줄이기 위해서 interface나 abstract class를 사용하고, if-else문을 줄이기 위해 map을 사용하는 방식을 채택했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;테스트 코드 작성. 사용자 아이디/패스워드가 필요한 TistoryClient와 TistoryClient를 사용하는 class를 제외하고는 최대한 unit test를 진행하고자 했다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;리팩토링 결과&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;지금부터 어떻게 리팩토링했는지 각 module에 대해 인상깊었고, 기억해 둘 만한 변경점만 기술할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Migrator&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Group 2697.png&quot; data-origin-width=&quot;1716&quot; data-origin-height=&quot;477&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/caxl3G/btsteNLHkEb/cqmEbAP0cNnksG1cxmGfMK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/caxl3G/btsteNLHkEb/cqmEbAP0cNnksG1cxmGfMK/img.png&quot; data-alt=&quot;리팩토링 결과 Migrator의 dependency tree&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/caxl3G/btsteNLHkEb/cqmEbAP0cNnksG1cxmGfMK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcaxl3G%2FbtsteNLHkEb%2FcqmEbAP0cNnksG1cxmGfMK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1716&quot; height=&quot;477&quot; data-filename=&quot;Group 2697.png&quot; data-origin-width=&quot;1716&quot; data-origin-height=&quot;477&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;리팩토링 결과 Migrator의 dependency tree&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;기존에는 N2T class로 묶었던 것을 &lt;b&gt;목적지 블로그/출발지 블로그가 추가될 수 있다&lt;/b&gt;고 가정했기 때문에 명칭을 더 포괄적으로 바꾸었다. 그리고 각 module들에도 변화가 생겼고, 각 module들은 VO를 사용해 통신한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;각 module들은 exception을 던지기만 하고, Migrator class에서 이를 받아 처리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;먼저 각 module들을 살펴보고 다시 Migrator를 살펴볼 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;UrlProcessor&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;기존에는 네이버 블로그만 처리할 수 있었던 URLProcessor를 UrlProcessor, BlogUrlProcessor로 나누었다. 명명도 Java naming convention에 따라 UrlProcessor로 바꾸었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Group 2693.png&quot; data-origin-width=&quot;1213&quot; data-origin-height=&quot;634&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FxNLf/btss73vxQC6/kpfHLVOkpXiAp2sclCMyjK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FxNLf/btss73vxQC6/kpfHLVOkpXiAp2sclCMyjK/img.png&quot; data-alt=&quot;리팩토링 결과 UrlProcessor의 dependency tree&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FxNLf/btss73vxQC6/kpfHLVOkpXiAp2sclCMyjK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFxNLf%2Fbtss73vxQC6%2FkpfHLVOkpXiAp2sclCMyjK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;684&quot; height=&quot;358&quot; data-filename=&quot;Group 2693.png&quot; data-origin-width=&quot;1213&quot; data-origin-height=&quot;634&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;리팩토링 결과 UrlProcessor의 dependency tree&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위 그림에 적혀 있듯 UrlProcessor는 Naver2Tistory가 지원하는 블로그 형식에 맞춰 크롤링할 URL로 변환하고, 블로그 형식과 변환한 URL을 UrlVO로 래핑해 리턴한다.&amp;nbsp;만약 지원하지 않는 형식이거나 크롤링 중 오류가 발생할 경우 exception을 날린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;분명 dependency를 줄이겠다고 했는데, UrlProcessor는 abstract class가 아니라 concrete class로 구현했다. 이렇게 구현한 이유는 &lt;b&gt;UrlProcessor는 추상화 수준이 매우 높게&lt;/b&gt; 설계했기 때문이며, 따라서 &lt;b&gt;내부 로직이 변하지 않을 것으로 예측&lt;/b&gt;된다. 만약 다른 종류의 블로그가 추가되면 BlogUrlProcessor의 구현체를 추가하고, UrlProcessor의 blogUrlProcessors에 추가만 하면 된다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;UrlVO는 해당 URL이 어떤 블로그의 URL인지, 그리고 URL 처리 결과 2개의 attribute가 있다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;디자인 패턴으로는 UrlProcessor는 BlogUrlProcessor를 생성하므로 &lt;b&gt;Template method pattern&lt;/b&gt;을, 블로그 종류에 따라 다른 BlogUrlProcessor interface의 구현체를 호출해 URL을 처리하므로 &lt;b&gt;Strategy pattern&lt;/b&gt;을 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;SOLID 원칙을 생각해 보면, UrlProcessor는 [inputUrl을 UrlVO로 변환한다]는 역할만 수행하므로 SRP를 만족하는 것 같다. UrlProcessor의 생성자만 수정하면 다른 종류의 BlogUrlProcessor를 추가할 수 있기 때문에 OCP도 만족하는 것 같다. interface에 의존하고 있기 때문에 DIP도 만족하는 것 같다. LSP는 해당 사항 없는 것 같고, BlogUrlProcessor interface의 모든 method를 사용하므로 ISP도 만족하는 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1693919963232&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class UrlProcessor {
    private static final List&amp;lt;BlogUrlProcessor&amp;gt; blogUrlProcessors = new ArrayList&amp;lt;&amp;gt;();

    public UrlProcessor(){
        blogUrlProcessors.add(new NaverUrlProcessor());
        // Append other blog url processors here
    }

    public UrlVO process(String inputUrl) throws Exception {
        for(BlogUrlProcessor blogUrlProcessor : blogUrlProcessors){
            if(blogUrlProcessor.matches(inputUrl)){
                return new UrlVO(blogUrlProcessor.getUrlType(), blogUrlProcessor.process(inputUrl));
            }
        }
        throw new Exception(&quot;[Url 작업 중 오류] : 지원하지 않는 블로그의 Url입니다.&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1693920134726&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Implement this interface to preprocess other types of blog URLs.
public interface BlogUrlProcessor {
    /**
     * @return blog type of each url processor
     */
    public BlogType getUrlType();

    /**
     * Return true if inputUrl matches type of blog URL format, otherwise false.
     */
    Boolean matches(String inputUrl);

    /**
     * Process inputUrl to crawlable URL and return. 
     * @throws Exception when error occurs while processing URL.
     */
    String process(String inputUrl) throws Exception;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;코드는 위와 같으며 다음 링크에서 열어볼 수도 있다. &lt;a href=&quot;https://github.com/hyelie/Naver2Tistory/blob/main/src/main/java/urlprocessor/UrlProcessor.java&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;UrlProcessor 코드&lt;/a&gt;, &lt;a href=&quot;https://github.com/hyelie/Naver2Tistory/blob/main/src/main/java/urlprocessor/BlogUrlProcessor.java&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;BlogUrlProcessor 코드&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;고민했던 점&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;BlogUrlProcessor를 interface로 사용할지, abstract class로 사용할지 고민했다. 둘 다 구현체/상속체에게 특정 method의 구현을 강제할 수 있으며 polymorphism을 사용할 수 있기 때문이었는데, 여기서는 BlogUrlProcessor들이 공통 기능이 필요하지 않다고 생각했기 때문에 interface를 사용했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Scrapper&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;기존에는 네이버 블로그에서 크롤링한 결과를 바로 티스토리 형식으로 바꾸었는데, &lt;b&gt;출발지/목적지 블로그가 추가될 수 있다고 가정했기 때문에&lt;/b&gt; 기존 Converter를 Scrapper와 Converter 2가지로 나누었다. &lt;b&gt;Scrapper는 출발지 블로그에서 크롤링한 후 공통 형식으로 바꾸고, Converter는 공통 형식을 목적지 블로그로 변환하는 역할&lt;/b&gt;을 한다. 일단 Scrapper부터 먼저 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;기존에는 크롤링만 수행했기 때문에 Crawler였지만, 이제는 &lt;b&gt;크롤링한 후 정보를 가공하는 부분까지 진행&lt;/b&gt;하기 때문에 &lt;b&gt;Scrapper&lt;/b&gt;로 명명했으며 내부적으로 크롤링과 파싱을 진행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Group 2694 (2).png&quot; data-origin-width=&quot;1611&quot; data-origin-height=&quot;1031&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ceXhxI/btss92Jtv7a/GRhzmyMMstJuMyYBBf1XiK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ceXhxI/btss92Jtv7a/GRhzmyMMstJuMyYBBf1XiK/img.png&quot; data-alt=&quot;리팩토링 결과 Scrapper의 dependency tree&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ceXhxI/btss92Jtv7a/GRhzmyMMstJuMyYBBf1XiK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FceXhxI%2Fbtss92Jtv7a%2FGRhzmyMMstJuMyYBBf1XiK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;844&quot; height=&quot;540&quot; data-filename=&quot;Group 2694 (2).png&quot; data-origin-width=&quot;1611&quot; data-origin-height=&quot;1031&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;리팩토링 결과 Scrapper의 dependency tree&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Scrapper는 UrlVO를 입력으로 받아 BlogType에 해당하는 BlogScrapper abstract class의 변환 method를 호출한다. 만약 지원하지 않는 형식이거나 크롤링 중 오류가 발생할 경우 exception을 날린다.&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;UrlProcessor과 마찬가지로 Scrapper도 &lt;b&gt;추상화 수준을 매우 높게 설계&lt;/b&gt;했기 때문에 &lt;b&gt;내부 로직이 바뀌지 않을 것으로 예상&lt;/b&gt;한다. 만약 다른 종류의 블로그가 추가되면 BlogScrapper의 derived class를 추가하고, BlogScrapper의 blogScrapperMap에 추가만 하면 되기 때문이다.&lt;/p&gt;
&lt;blockquote style=&quot;text-align: left;&quot; data-ke-style=&quot;style2&quot;&gt;BlogPost는 게시글 제목, 그리고 HTML DOM 트리를 커스텀한 ConvertedTree의 root node 2개의 attribute를 가진다.&lt;br /&gt;ConvertedTreeNode는 해당 node가 어떤 스타일을 가지는지, 내용은 어떤 것인지, 그리고 child node list를 attribute로 가진다.&lt;/blockquote&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;디자인 패턴으로는 UrlProcessor과 마찬가지로 BlogScrapper를 사용하므로 &lt;b&gt;Template&lt;/b&gt;&lt;b&gt;&amp;nbsp;method pattern&lt;/b&gt;을 사용하고, 블로그 종류에 따라 다른 BlogScrapper abstract class의 derived class를 호출해 URL을 처리하므로&amp;nbsp;&lt;b&gt;Strategy pattern&lt;/b&gt;을 사용한다.&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;SOLID 원칙을 생각해 보면, Scrapper는 [UrlVO를 입력으로 받아 크롤링/파싱한다]는 역할만 수행하므로 SRP는 만족하지 않는 것 같다. 그러나 &lt;b&gt;SRP를 만족시키기 위해 크롤링 기능을 분리하는 것에는 overhead가 더 크다고 생각&lt;/b&gt;했는데, 그 이유는 아래의 고민했던 점에서 설명하겠다. 다른 종류의 BlogScrapper를 추가하기 위해서는 BlogScrapper를 상속해 구현하고 Scrapper의 생성자에만 추가하면 다른 종류의 BlogScrapper를 추가할 수 있기 때문에 OCP도 만족하는 것 같다. Scrapper는 abstract class인 BlogScrapper에 의존하므로 DIP도 만족하는 것 같다. LSP는 해당사항 없는 것 같고, ISP BlogScrapper의 모든 method를 사용하므로 만족하는 것 같다.&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1693921927400&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Scrapper {
    private static Map&amp;lt;BlogType, BlogScrapper&amp;gt; blogScrapperMap = new HashMap&amp;lt;&amp;gt;();

    public Scrapper(){
        blogScrapperMap.put(BlogType.NAVER, new NaverScrapper());
        // Append other blog scrappers here
    }

    public BlogPost scrap(UrlVO urlVO) throws Exception {
        BlogType blogType = urlVO.getUrlType();
        String url = urlVO.getUrl();

        if(blogScrapperMap.containsKey(blogType)){
            BlogScrapper blogScrapper = blogScrapperMap.get(blogType);
            return blogScrapper.scrap(url);
        }
        throw new Exception(&quot;[스크래핑 중 오류] : 지원하지 않는 블로그입니다.&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1693921938806&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Inherit this abstract class to scrap other types of blogs.
public abstract class BlogScrapper {
    private static final String defaultErrorMessage = &quot;스크래핑 중 알 수 없는 오류가 발생했습니다.&quot;;

    protected BlogScrapper(){
        initializeErrorMessages();
    }

    public BlogPost scrap(String url) throws Exception{
        Document document = crawl(url);
        return parse(document);
    }
    
    protected Document crawl(String url) throws Exception {
        try{
            Connection con = Jsoup.connect(url).timeout(5000).ignoreHttpErrors(true);
            Response response = con.execute();
            if (response.statusCode() == 200) {
                return con.get();
            }
            else{ // page not found
                throw new Exception(getErrorMessage(response.statusCode()));
            }
        } catch(IOException e) { // 이외 connection 중 발생하는 오류
            throw new Exception(getErrorMessage(500));
        }
    }
    protected abstract BlogPost parse(Document document) throws Exception ;

    protected HashMap&amp;lt;Integer, String&amp;gt; errorMessages = new HashMap&amp;lt;Integer, String&amp;gt;();
    protected abstract void initializeErrorMessages();
    protected String getErrorMessage(int statusCode){
        if(errorMessages.containsKey(statusCode)){
            return errorMessages.get(statusCode);
        }
        return defaultErrorMessage;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&amp;nbsp;코드는 위와 같으며 다음 링크에서 열어볼 수도 있다.&lt;span&gt; &lt;a href=&quot;https://github.com/hyelie/Naver2Tistory/blob/main/src/main/java/convert/scrappers/Scrapper.java&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Scrapper 코드&lt;/a&gt;, &lt;a href=&quot;https://github.com/hyelie/Naver2Tistory/blob/main/src/main/java/convert/scrappers/BlogScrapper.java&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;BlogScrapper 코드&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;고민했던 점&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;앞서&amp;nbsp;&lt;span style=&quot;text-align: start;&quot;&gt;크롤링 기능 Scrapper에서 분리하는 것에는 overhead가 더 크다고 생각했는데, 그 이유를 여기에서 설명하겠다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1693922148017&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class NaverScrapper extends BlogScrapper {
    @Override
    protected void initializeErrorMessages() {
        errorMessages.put(204, &quot;[네이버 블로그 오류] : 삭제되거나, 존재하지 않거나, 비공개 글이거나, 구버전 포스팅입니다.&quot;);
        errorMessages.put(404, &quot;[네이버 블로그 오류] : 유효하지 않은 요청입니다. 해당 블로그가 없습니다. 블로그 아이디를 확인해 주세요.&quot;);
        errorMessages.put(500, &quot;[네이버 블로그 오류] : 예상치 못한 에러가 발생했습니다.&quot;);
        // Append other blog error code and message mapping here
    }
    
    private Elements extractPost(Document document) throws Exception {
        Elements post = document.select(&quot;.se-viewer&quot;); // naver blog 포스트 부분
        if(post.size() == 0){
            throw new Exception(getErrorMessage(204));
        }
        return post;
    }

    private String extractTitle(Elements post) throws Exception {
        Elements head = post.select(&quot;.pcol1&quot;); // naver blog 제목
		if(head.size() == 0){
			throw new Exception(getErrorMessage(204));
		}
        return head.text();
    }
    ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위 코드는 NaverScrapper의 코드 일부분이며, 다음 링크에서 열어볼 수도 있다. &lt;a href=&quot;https://github.com/hyelie/Naver2Tistory/blob/main/src/main/java/convert/scrappers/naver/NaverScrapper.java&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;NaverScrapper 코드&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;initializeErrorMessage()에서 네이버 블로그에서 사용하는 에러 메시지들을 초기화하고 있다. extractPost()와 extractTitle()은 크롤링 결과로부터 필요한 정보를 따 오는 것이므로 Crawler에 들어가는 것이 아니라 Parser에 들어갈 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;반면, BlogScrapper의 crawl() method를 보자. 내부적으로 getErrorMessage()를 호출하는데, 여기에서 사용할 메시지들이 initializeErrorMessage()에서 초기화한 에러 메시지들이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;만약 Crawler와 Parser 2개로 나눈다면 &lt;b&gt;에러 메시지를 중복해서 적어야 한다!&lt;/b&gt; 중복된다면, 필연적으로 관리하기 어려워진다. 만약 &lt;b&gt;에러 메시지를 공유한다고 치면, 어디서 관리할 것인가?&lt;/b&gt; Crawler와 Parser 2개 class만 사용하는데 Migrator에서 이를 관리할 수도 없고, Crawler에서 에러 메시지를 초기화하고 Parser에게 건네주는 방식이라면 두 클래스가 아주 강한 의존관계를 지니게 된다고 생각했다. 때문에 &lt;b&gt;Scrapper를 Crawler와 Parser로 분리하는 것보다 일원화해서 관리하는 편이 더 좋다고 생각&lt;/b&gt;했다. Scrapping이라는 명칭의 역할도 달성할 수 있다는 부가적인 효과도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이후 따라온 고민은 BlogScrapper를 interface로 사용할지, abstract class로 사용할지였다. BlogScrapper의 parse()는 derived class에서 구현되어야 하는 것이 명확했다. 그러나 crawl() method가 애매했다. HTTP status code가 200인 경우 크롤링에 성공한 것이 99.9% 확정이기 때문에, BlogScrapper를 interface로 두면 &lt;b&gt;crawl() method의 구현이 거의 중복될 것이라 예측&lt;/b&gt;했다. 때문에 BlogScrapper를 abstract class로 두었고, 꼭 구현해야 하는 initializeErrorMessage()와 parse()만 abstract method로 두었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;NaverScrapper&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&amp;nbsp;앞서 Template method pattern과 Strategy pattern을 사용했었는데, NaverScrapper도 마찬가지이다. 예를 들어 image에 해당하는 section에서는 src를 추출해 이미지를 다운로드 해야 하고, table에 해당하는 section에서는 row/column/content를 추출해야 하는데, 이렇듯&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;text-align: start;&quot;&gt;section의 종류에 따라 공통 형식으로 변환하는 형식이 다르기 때문에 앞과 같은 방법을 사용했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&amp;nbsp;SOLID 원칙을 생각해 보면, NaverScrapper는 Scrapper와 같은 역할을 가지므로 SRP는 조금 애매하다. 그러나 crawl() method는 Scrapper에서 처리하고, NaverScrapper는 네이버 블로그의 DOM tree를 파싱&lt;b&gt;만&lt;/b&gt; 하므로 SRP를 만족하는 것 같다. 다른 종류의 SectionParser를 추가하기 위해서는 SectionParser의 구현체를 생성자에만 추가하면 되므로 OCP도 만족하는 것 같다. abstract class인 SectionParser에 의존하므로 DIP도 만족한다. NaverScrapper는 Scrapper의 자리에 들어가 사용될 수 있기 때문에 LSP 또한 만족한다. ISP는 해당 사항이 없다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;디자인 패턴으로는 SectionParser를 사용하므로 &lt;b&gt;Template &lt;/b&gt;&lt;b&gt;method pattern&lt;/b&gt;을 사용하고, 블로그 종류에 따라 다른 BlogScrapper abstract class의 derived class를 호출해 URL을 처리하므로&amp;nbsp;&lt;b&gt;Strategy pattern&lt;/b&gt;을 사용한다.&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1693929437806&quot; class=&quot;reasonml&quot; style=&quot;text-align: start;&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class NaverScrapper extends BlogScrapper {
    private static Map&amp;lt;String, SectionParser&amp;gt; parserMap = new HashMap&amp;lt;&amp;gt;();
    private static final String DEFAULT = &quot;&quot;;

    static{
        initializeParserMap();
    }

    private static void initializeParserMap(){
        parserMap.put(&quot;table&quot;, new TableParser());
        parserMap.put(&quot;quotation&quot;, new QuotationParser());
        parserMap.put(&quot;text&quot;, new TextParser());
        parserMap.put(&quot;code&quot;, new CodeParser());
        parserMap.put(&quot;image&quot;, new ImageParser());
        parserMap.put(&quot;horizontalLine&quot;, new HorizontalLineParser());
        parserMap.put(&quot;oglink&quot;, new OglinkParser());
        parserMap.put(DEFAULT, new DefaultParser());
        // Append other [naver blog section name to StyleType mapping] here
    }

    @Override
    protected BlogPost parse(Document document) throws Exception {
        Elements post = extractPost(document);
        String title = extractTitle(post);
        Element content = extractContent(post);
        ConvertedTreeNode root = parseToTree(content);
        
        return new BlogPost(title, root);
    }
    
    private ConvertedTreeNode parseToTree(Element curElement){
        ConvertedTreeNode rootNode = ConvertedTreeNode.builder().type(StyleType.NONE).build();

        for(Element child : curElement.children()){
            Element sectionElement = child.child(0).child(0);
            if(isSection(sectionElement)){
                SectionParser sectionParser = getSectionParser(getSection(sectionElement));
                ConvertedTreeNode sectionNode = sectionParser.parseToTreeNode(sectionElement);
                rootNode.appendChild(sectionNode);
            }
        }

        return rootNode;
    }

    private static final Pattern sectionPattern = Pattern.compile(&quot;se-section se-section-([A-za-z]*)&quot;);
    private Boolean isSection(Element element){
        Matcher sectionMatcher = sectionPattern.matcher(element.className());
        if(sectionMatcher.find()) return true;
        return false;
    }

    private String getSection(Element element){
        Matcher sectionMatcher = sectionPattern.matcher(element.className());
        if(sectionMatcher.find()){
            return sectionMatcher.group(1);
        }
        return DEFAULT;
    }

    private SectionParser getSectionParser(String section){
        return parserMap.getOrDefault(section, parserMap.get(DEFAULT));
    }
    
    ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;코드는 위와 같으며 다음 링크에서 열어볼 수도 있다. &lt;a href=&quot;https://github.com/hyelie/Naver2Tistory/blob/main/src/main/java/convert/scrappers/naver/NaverScrapper.java&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;NaverScrapper 코드&lt;/a&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;고민했던 점&lt;/h4&gt;
&lt;pre id=&quot;code_1693923707165&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Inherit this abstract class to parse other types of naver blog sections.
public abstract class SectionParser {
    abstract public ConvertedTreeNode parseToTreeNode(Element element);

    // Regular expression filtering [se-text-paragraph se-text-paragraph-{ALIGN-TYPE}] format
    private static final Pattern paragraphPattern = Pattern.compile(&quot;se-text-paragraph se-text-paragraph-align-([A-za-z]*)&quot;);
    private static Map&amp;lt;String, StyleType&amp;gt; styleMap = new HashMap&amp;lt;&amp;gt;();
    static{
        initializeStyleMap();
    }

    private static void initializeStyleMap(){
        styleMap.put(&quot;&quot;, StyleType.PARAGRAPH_DEFAULT);
        styleMap.put(&quot;right&quot;, StyleType.PARAGRAPH_RIGHT);
        styleMap.put(&quot;justify&quot;, StyleType.PARAGRAPH_LEFT);
        styleMap.put(&quot;center&quot;, StyleType.PARAGRAPH_CENTER);
        styleMap.put(&quot;a&quot;, StyleType.LINK);
        styleMap.put(&quot;b&quot;, StyleType.BOLD);
        styleMap.put(&quot;i&quot;, StyleType.TILT);
        styleMap.put(&quot;u&quot;, StyleType.UNDERLINE);
        styleMap.put(&quot;strike&quot;, StyleType.STRIKE);
        // Append other [naver blog style to StyleType mapping] here
    }

    protected ConvertedTreeNode parseTextModule(Element textModule) { // se-module-text
        ConvertedTreeNode textNode = ConvertedTreeNode.builder().type(StyleType.TEXT).build();

        for(Element textParagraph : textModule.children()){ // se-text-paragraph
            String align = getAlignFromElement(textParagraph);
            StyleType alignType = getAlignTypeFromMap(align);
            ConvertedTreeNode paragraphNode = parseTextParagraph(textParagraph, alignType);
            textNode.appendChild(paragraphNode);
        }

        return textNode;
    }

    private ConvertedTreeNode parseTextParagraph(Element textParagraph, StyleType alignType){
        ConvertedTreeNode paragraphNode = ConvertedTreeNode.builder().type(alignType).build();
        for(Element spanElement : textParagraph.children()){
            paragraphNode.appendChild(parseSpanElementToTreeNode(spanElement));
        }
        return paragraphNode;
    }

    // span 이하 element들에 대해 DFS 이후 tagname을 style로 지정한 tree node return
    private ConvertedTreeNode parseSpanElementToTreeNode(Element element){
        StyleType styleType = getTextStyleFromMap(element.tagName());
        
        if(element.childrenSize() == 0){
            String content = element.text();
            return ConvertedTreeNode.builder().type(styleType).content(content).build();
        }
        
        ConvertedTreeNode curNode = ConvertedTreeNode.builder().type(styleType).build();
        for(Element child : element.children()){
            curNode.appendChild(parseSpanElementToTreeNode(child));
        }
        return curNode;
    }

    private String getAlignFromElement(Element textParagraph){
        Matcher paragraphMatcher = paragraphPattern.matcher(textParagraph.className());

        if(paragraphMatcher.find()){
            return paragraphMatcher.group(1);
        }
        return &quot;&quot;;
    }

    private StyleType getAlignTypeFromMap(String align){
        return styleMap.getOrDefault(align, StyleType.PARAGRAPH_DEFAULT);
    }
    
    private StyleType getTextStyleFromMap(String style){
        return styleMap.getOrDefault(style, StyleType.CONTENT);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위 코드는 SectionParser 코드의 일부분이며, 다음 링크에서 열어볼 수 있다. &lt;a href=&quot;https://github.com/hyelie/Naver2Tistory/blob/main/src/main/java/convert/scrappers/naver/SectionParser.java&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;SectionParser 코드&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;사실상 리팩토링하면서 제일 힘들었던 부분이고, 마땅히 좋은 해결책을 찾지 못해서 조금 맘에 들지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;네이버 블로그의 HTML 구조는 다음과 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1693924522664&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;- se-main-container                                                     # main text
    - se-component se-[TYPE]
        - se-component-content
            - se-section se-section-[TYPE]
                - se-module se-module-text                              # text
                  - se-text-paragraph                                       # paragraph
                - se-module se-module-code                              # source code
                  - se-code-source                                  
                      - __se_code_view
                - se-module se-module-image                             # image
                  se-module se-module-text se-caption                   # image caption
                - se-module se-module-horizontalLine                    # horizontal line
                - se-module se-module-oglink                            # auto generated shortcut 
                - se-quotation-container                                # quotation container
                    - se-module se-module-text se-quote                     # quote
                    - se-module se-module-text se-cite                      # cite
                - se-table-container                                    # table container
                    - se-table-content                                      # table body&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;[se-main-container - se-component se-type - se-component-content - se-section se-section-type] 이후에 module이 오는 형태인데, image module의 경우 se-section 아래에 se-module-image와 se-module-text가 같이 오기 때문에, 글의 구성요소를 section으로 판정해야만 했다. 문제는 이미지 캡션, 인용구, 표의 각 셀 등 모든 평문이 들어가는 부분은 se-module-text로 구성되어 있다는 것이다. 즉, &lt;b&gt;TextParser가 TableParser나 ImageParser 내부에 들어가야 한다.&lt;/b&gt; SectionParser interface의 구현체에 TextParser가 들어가야 하는 것인데... 네이버 블로그 HTML의 구성상 의존성이 있는 것은 맞지만, 이것이 너무 마음에 들지 않아서 다른 방법을 생각했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;내가 제시한 방법은 TextModule을 파싱하는 부분이 공통으로 사용되므로, &lt;b&gt;SectionParser를 abstract class로 바꾸는 것&lt;/b&gt;이었다. 때문에 SectionParser는 각 section을 파싱하는 parseToTreeNode() method를 강제함과 동시에 text module을 파싱하는, 2가지 역할을 가지게 된 것이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;디자인 패턴으로는 딱히 적용된 게 없다. Strategy patten이 적용된 것 같은데, 구현이 encapsulation되지 않았기 때문에 그렇다고 볼 수 없다. Template method pattern은 맞다고 볼 수 있다. SectionParser의 derived class들이 parseToTreeNode()의 동작을 다르게 구현하기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;SOLID 원칙을 생각해 보면, SectionParser는 abstract class의 역할과 text module을 파싱하는 역할 2개를 수행하므로 SRP는 만족하지 않는 것 같다. 이를 위배한 이유는 위와 같이 설명했지만, 더 좋은 방법을 찾지 못한 것이 아쉽다. 이외에 다른 OCP, LSP, ISP, DIP는 딱히 해당사항 없는 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;AuthClient &amp;amp; Converter&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;AuthClient&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;앞서 [공통 형식]을 목적지 블로그에 맞는 형식으로 변환하는 역할을 Converter가 한다고 했다. Converter를 살펴보기 전에 먼저 AuthClient부터 살펴보겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Group 2695.png&quot; data-origin-width=&quot;1984&quot; data-origin-height=&quot;821&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/brRwIZ/btstcQBX62F/pPz2W0XsalK24VAAfkkH7k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/brRwIZ/btstcQBX62F/pPz2W0XsalK24VAAfkkH7k/img.png&quot; data-alt=&quot;리팩토링 결과 AuthClient &amp;amp;amp; Converter의 dependency tree&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/brRwIZ/btstcQBX62F/pPz2W0XsalK24VAAfkkH7k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbrRwIZ%2FbtstcQBX62F%2FpPz2W0XsalK24VAAfkkH7k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1984&quot; height=&quot;821&quot; data-filename=&quot;Group 2695.png&quot; data-origin-width=&quot;1984&quot; data-origin-height=&quot;821&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;리팩토링 결과 AuthClient &amp;amp; Converter의 dependency tree&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1693930665913&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Implement this interface to post other types of blogs.
public interface AuthClient {
    /**
     * Authorize to upload post
     */
    public void authorize() throws Exception;

    /**
     * Upload post in each blog.
     * @param title : post title
     * @param content : post content
     */
    public void post(String title, String content) throws Exception;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;AuthClient는 authorize()와 post() 2개의 method만 가진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;디자인 패턴으로는 딱히 적용된 것이 없다.&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;SOLID 원칙을 생각해 보면, AuthClient는 [목적지 블로그의 API를 사용한다]는 역할만 수행하므로 SRP를 만족하는 것 같다. AuthClient의 구현체는 AuthClient를 수정하지 않고 interface method를 구현해야 하므로 확장할 수 있기 때문에 OCP도 만족하는 것 같다. interface를 사용하므로 DIP도 만족하는 것 같다. LSP나 ISP는 해당 사항 없는 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Converter&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;다음으로 Converter를 살펴보자.&amp;nbsp;Converter는 BlogPost를 받아 목적지 블로그 형식으로 변환한다. 오직 하나의 함수만 있으면 되므로 interface로 이를 구현했다.&lt;/p&gt;
&lt;pre id=&quot;code_1693926315588&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Implement this interface to convert ConvertedTreeNode to other types of blogs.
public interface Converter {
    public String convert(BlogPost blogPost);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;TistoryConverter는 Converter의 구현체로, 입력으로 받은 BlogPost를 Tistory 형식으로 변환한다. 일단 눈여겨 볼 점은 &lt;b&gt;TistoryClient에 의존하고 있다&lt;/b&gt;는 것인데, 이는 조금 이따 설명하겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;TistoryConverter는 ConvertedTree의 root부터 특정 StyleType이 나오면 해당 TypeConverter를 호출한다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;ConvertResultVO는 변환 결과 Element와 다음으로 탐색할 ConvertedTreeNode list 2개의 attribute를 가진다.&lt;/blockquote&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;디자인 패턴으로는 TistoryConverter는 TypeConverter를 생성하므로&amp;nbsp;&lt;b&gt;Template method pattern&lt;/b&gt;을 사용하고, StyleType에 따라 다른 TypeConverter interface의 구현체를 호출해 URL을 처리하므로&amp;nbsp;&lt;b&gt;Strategy pattern&lt;/b&gt;을 사용한다.&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;SOLID 원칙을 생각해 보면, TistoryConverter는 [BlogPost의 ConvertedTree root를 String으로 변환한다]는 역할만 수행하므로 SRP를 만족하는 것 같다. TistoryConverter의 생성자만 수정하면 다른 종류의 &lt;span style=&quot;text-align: start;&quot;&gt;TypeConverter&lt;/span&gt;를 추가할 수 있기 때문에 OCP도 만족하는 것 같다. interface에 의존하고 있기 때문에 DIP도 만족하는 것 같다. LSP는 해당 사항 없는 것 같고, &lt;span style=&quot;text-align: start;&quot;&gt;TypeConverter&amp;nbsp;&lt;/span&gt;interface의 모든 method를 사용하므로 ISP도 만족하는 것 같다.&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1693926331251&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class TistoryConverter implements Converter {
    private TistoryClient tistoryClient;
    private static Map&amp;lt;StyleType, TypeConverter&amp;gt; converterMap = new HashMap&amp;lt;&amp;gt;();

    public TistoryConverter(TistoryClient tistoryClient){
        this.tistoryClient = tistoryClient;
        initializeConverterMap();
    }

    private void initializeConverterMap(){
        converterMap.put(StyleType.TABLE, new TableConverter());
        converterMap.put(StyleType.ROW, new RowConverter());
        converterMap.put(StyleType.COLUMN, new ColumnConverter());

        converterMap.put(StyleType.QUOTATION, new QuotationConverter());

        converterMap.put(StyleType.PARAGRAPH_DEFAULT, new DefaultParagraphConverter());
        converterMap.put(StyleType.PARAGRAPH_LEFT, new LeftParagraphConverter());
        converterMap.put(StyleType.PARAGRAPH_RIGHT, new RightParagraphConverter());
        converterMap.put(StyleType.PARAGRAPH_CENTER, new CenterParagraphConverter());

        converterMap.put(StyleType.CONTENT, new ContentConverter());
        converterMap.put(StyleType.LINK, new LinkConverter());
        converterMap.put(StyleType.BOLD, new BoldConverter());
        converterMap.put(StyleType.TILT, new TiltConverter());
        converterMap.put(StyleType.UNDERLINE, new UnderlineConverter());
        converterMap.put(StyleType.STRIKE, new StrikeConverter());
        
        converterMap.put(StyleType.CODE, new CodeConverter());
        converterMap.put(StyleType.IMAGE, new ImageConverter(tistoryClient));
        converterMap.put(StyleType.HORIZONTALLINE, new HorizontalLineConverter());
        converterMap.put(StyleType.NONE, new DefaultConverter());
        // Append other TypeConverter implements here
    }

    @Override
    public String convert(BlogPost blogPost) {
        Element convertedElement = traverseAndConvert(blogPost.getRoot());
        
        return encodeToUTF8(convertedElement.outerHtml());
    }

    private Element traverseAndConvert(ConvertedTreeNode curNode){
        TypeConverter typeConverter = getTypeConverter(curNode.getType());

        ConvertResultVO convertResult = typeConverter.convertAndReturnNextNodes(curNode);
        Element curElement = convertResult.getResult();

        for(ConvertedTreeNode nextNode : convertResult.getNextNodes()){
            Element nextElement = traverseAndConvert(nextNode);
            curElement.appendChild(nextElement);
        }

        return curElement;
    }

    private TypeConverter getTypeConverter(StyleType supportType){
        return converterMap.getOrDefault(supportType, converterMap.get(StyleType.NONE));
    }

    private String encodeToUTF8(String origin){
        try{
            return Utils.encodeToUTF8(origin);
        }
        catch(Exception e) {
            Utils.printMessage(e.getMessage() + &quot; 인코딩을 취소합니다.&quot;);
        }
        return origin;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&amp;nbsp;위 코드는 TistoryConverter 코드의 일부분이며, 다음 링크에서 열어볼 수 있다.&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;a href=&quot;https://github.com/hyelie/Naver2Tistory/blob/main/src/main/java/convert/converters/tistory/TistoryConverter.java&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;TistoryConverter 코드&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;TypeConverter 코드는 아래와 같고, TypeConverter의 구현체들은 딱히 특이한 내용이 없으므로 생략하겠다.&lt;/p&gt;
&lt;pre id=&quot;code_1693926347478&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Implement this interface to convert other kind of StyleTypes.
public interface TypeConverter {
    abstract public ConvertResultVO convertAndReturnNextNodes(ConvertedTreeNode node);

    public static String convertContent(String content){
        return content.isEmpty() ? System.lineSeparator() : content;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;고민했던 점 - interface + static method&lt;/h4&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;TypeConverter의 경우 static method를 사용했는데, ConvertedTreeNode를 변환할 때 content를 변환하는 로직은 모두 공통이기 때문이다. BlogScrapper나 SectionParser의 경우 이 경우와 같이 공통 로직이 있어 abstract class를 사용했다. 그러나 TypeConverter의 경우 interface를 쓰고 공통 로직에 static method를 사용했다.&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Java의 경우 상속은 좋은 기능이지만 class 간 의존관계가 명확해지고, 다중상속이 허용되지 않기 때문에 &lt;b&gt;class hierarchy보다는 interface를 사용하는 것이 좋다&lt;/b&gt;고 생각한다. 따라서 class hierarchy를 최소한으로 만들어야 했다.&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그럼에도 불구하고 BlogScrapper나 SectionParser를 abstract class로 만든 이유는 &lt;b&gt;공통 멤버 변수가 필요했기 때문&lt;/b&gt;이었다. BlogScrapper는 에러 메시지 처리를 위해 errorMessages, SectionParser는 평문의 스타일을 매핑한 styleMap을 사용했는데, &lt;b&gt;만약 interface로 사용하면&lt;/b&gt; 이들의 &lt;b&gt;초기화 함수를 생성자에 넣는 과정을 implement할 때마다 추가해야 한다&lt;/b&gt;. 이는 번거로운 과정이며, 컴파일러가 경고하지 않기 때문에 초기화 함수를 구현했더라도 생성자에 넣지 않을 수 있기 때문에 초기화가 올바르게 되지 않을 가능성이 더 크다고 생각해 abstract class로 구현했다.&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;반면 &lt;b&gt;TypeConverter의 경우&lt;/b&gt; 이러한 과정이 필요 없다. 단순히 input에 대해 비었는지 검사하고 없으면 System.lineSeparator()를 호출하는 함수가 필요했을 뿐이기에 &lt;b&gt;interface + static method로 구현&lt;/b&gt;했다.&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;Migrator&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Migrator는 사용자 입력을 받고, migration 작업을 수행하는 class이며, 각 module들을 조립한다. 제일 큰 class이며, 각 module&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;UrlProcessor나 Scrapper는 URL에 해당하는 BlogType에 따라 다른 구현체를 호출해야 하지만, 이 각각의 로직은 UrlProcessor와 Scrapper 내부에서 처리하므로 Migrator는 이 class들만 부르면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;디자인 패턴으로는 TargetBlogConfigFactory를 사용하므로 &lt;b&gt;Template method pattern&lt;/b&gt;을 사용하고, 목적지 블로그 종류에 따라 다른 TargetBlogConfigFactory의 구현체를 호출해 처리하므로 &lt;b&gt;Strategy pattern&lt;/b&gt;을 사용한다.&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;SOLID 원칙을 생각해 보면, Migratior class는 Migrator 작업을 담당하므로 SRP를 만족한다고 볼 수 있지만, 사용자 입력과 파일 파싱, 모든 module 호출 등 여러 가지를 수행하므로 좀 애매하다. OCP는 만족하는데, 새로운 목적지 블로그가 추가되면 해당 TargetBlogConfigFactory만 구현하면 되기 때문이다. TargetBlogConfigFactory나 AuthClient, Converter를 사용하므로 ISP는 만족하고, LSP는 해당되는 부분이 없다. DIP는 반만 준수하는데, AuthClient나 Converter는 interface인 반면 UrlProcessor와 Scrapper는 concrete class이기 때문이다.&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;비록 DIP를 준수하지는 않지만 UrlProcessor나 Scrapper는 추상화 수준이 매우 높기 때문에 변경되지 않을 가능성이 농후하다. 때문에 유지보수에는 문제가 없을 것이라 예측된다.&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1693931818929&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Migrator {
    private final UrlProcessor urlProcessor = new UrlProcessor();
    private final Scrapper scrapper = new Scrapper();
    private AuthClient authClient;
    private Converter converter;

    private final List&amp;lt;BlogType&amp;gt; targetBlogTypes = new ArrayList&amp;lt;&amp;gt;();
    private final Map&amp;lt;BlogType, TargetBlogConfigFactory&amp;gt; targetBlogConfigFactoryMap = new HashMap&amp;lt;&amp;gt;();

    public Migrator(){
        targetBlogTypes.add(BlogType.NONE);
        targetBlogTypes.add(BlogType.TISTORY);
        // append other target blog types here

        targetBlogConfigFactoryMap.put(BlogType.TISTORY, new TistoryConfigFactory());
        // append other target blog factories here
    }

    public void migrate(){
        BlogType targetBlogType = getTargetBlogType();
        configureTargetBlog(targetBlogType);

        List&amp;lt;String&amp;gt; urls = loadUrlList();

        Utils.printMessage(&quot;[작업 시작] 작업을 시작합니다.&quot;);
        for(String url : urls){
            Utils.printMessage(&quot;[작업 중] : &quot; + url);
            migratePost(url);
        }
        Utils.getInput(&quot;[작업 완료] 작업을 완료했습니다. 프로그램을 종료합니다.&quot;);
    }

    private BlogType getTargetBlogType(){
        Utils.printMessage(&quot;==============================&quot;);
        printTargetBlogTypes();
        Utils.printMessage(&quot;==============================&quot;);

        String input = Utils.getInput(&quot;업로드할 블로그를 선택하세요. 종료를 원하면 0을 입력하세요.&quot;);
        BlogType targetBlogType = parseInput(input);

        if(targetBlogType == null){
            Utils.getInput(&quot;지원하지 않는 블로그 유형입니다. 프로그램을 종료합니다.&quot;);
            System.exit(1);
        }
        if(targetBlogType == BlogType.NONE){
            Utils.getInput(&quot;사용자 입력으로 프로그램을 종료합니다.&quot;);
            System.exit(1);
        }

        return targetBlogType;
    }

    private void configureTargetBlog(BlogType targetBlogType){
        TargetBlogConfigFactory targetBlogConfigFactory = getTargetBlogConfigureFactory(targetBlogType);
        if(targetBlogConfigFactory == null){
            Utils.getInput(&quot;지원하지 않는 블로그 유형입니다. 프로그램을 종료합니다.&quot;);
            System.exit(1);
        }

        this.authClient = targetBlogConfigFactory.createAuthClient();
        this.converter = targetBlogConfigFactory.createConverter(this.authClient);
        if(this.authClient == null || this.converter == null){
            Utils.getInput(&quot;[오류 발생] 초기화 중 오류가 발생해 프로그램을 종료합니다.&quot;);
            System.exit(1);
        }
    }

    private TargetBlogConfigFactory getTargetBlogConfigureFactory(BlogType targetBlogType){
        if(targetBlogConfigFactoryMap.containsKey(targetBlogType)){
            return targetBlogConfigFactoryMap.get(targetBlogType);
        }
        return null;
    }

    private void migratePost(String url){
        try{
            BlogPost blogPost = scrapper.scrap(urlProcessor.process(url));
            String title = blogPost.getTitle();
            String convertedContent = converter.convert(blogPost);
            authClient.post(title, convertedContent);
        }
        catch(Exception e){
            Utils.printMessage(e.getMessage() + &quot; &quot; + url);
        }
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위 코드는 Migrator 코드의 일부분이며, 다음 링크에서 열어볼 수 있다. &lt;a href=&quot;https://github.com/hyelie/Naver2Tistory/blob/main/src/main/java/migrator/Migrator.java&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Migrator 코드&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;고민했던 점 - TistoryClient에 의존하는 TistoryConverter&lt;/h4&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;티스토리의 포스트 구조를 보면 이미지는 첨부 파일의 형식으로 올린 후 해당 첨부 파일을 미리 보는 replacer라는 것을 사용해 이미지를 표시한다. 따라서 &lt;b&gt;이미지를 업로드하기 위해&lt;/b&gt; Tistory API를 사용하는 &lt;b&gt;TistoryClient에 의존&lt;/b&gt;해야만 했다. 반면 Migrator에서는 Converter와 AuthClient만 호출하는 상황이고, &lt;b&gt;Converter와 AuthClient는 직접적으로 의존관계가 없다&lt;/b&gt;. &lt;b&gt;의존관계는 TistoryConverter와 TistoryClient에만 있다&lt;/b&gt;.&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이를 해결하기 위해 &lt;b&gt;Factory method pattern&lt;/b&gt;을 사용했다.&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Group 2696.png&quot; data-origin-width=&quot;790&quot; data-origin-height=&quot;594&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lQ1ip/btsteNLJd1t/nSBLfLiBiCCHO86diq4mi1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lQ1ip/btsteNLJd1t/nSBLfLiBiCCHO86diq4mi1/img.png&quot; data-alt=&quot;Converter, AuthClient의 Factory&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lQ1ip/btsteNLJd1t/nSBLfLiBiCCHO86diq4mi1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlQ1ip%2FbtsteNLJd1t%2FnSBLfLiBiCCHO86diq4mi1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;408&quot; height=&quot;307&quot; data-filename=&quot;Group 2696.png&quot; data-origin-width=&quot;790&quot; data-origin-height=&quot;594&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Converter, AuthClient의 Factory&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Abstract factory pattern&lt;/b&gt;을 사용한다. UrlProcessor나 Scrapper는 URL에 해당하는 BlogType에 따라 다른 구현체를 호출해야 하지만, 목적지 블로그는 하나이므로 Converter와 AuthClient는 구현체를 바로 불러오면 된다. 이 때 TistoryConverter와 TistoryClient의 명시적인 의존성을 줄이기 위해 Abstract factory pattern을 사용했다.&lt;/p&gt;
&lt;pre id=&quot;code_1693931267233&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public interface TargetBlogConfigFactory {
    public AuthClient createAuthClient();
    public Converter createConverter(AuthClient authClient);
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1693931282221&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class TistoryConfigFactory implements TargetBlogConfigFactory {
    @Override
    public AuthClient createAuthClient() {
        try{
            return new TistoryClient();
        }
        catch(Exception e){
            Utils.printMessage(e.getMessage());
        }
        return null;
    }

    @Override
    public Converter createConverter(AuthClient authClient) {
        try{
            return new TistoryConverter((TistoryClient) authClient);
        }
        catch(Exception e){
            Utils.printMessage(e.getMessage());
        }
        return null;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;테스트 코드&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/n9yel/btsteMzhYpH/FSrEKxD0du61HRqL5QAYak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/n9yel/btsteMzhYpH/FSrEKxD0du61HRqL5QAYak/img.png&quot; style=&quot;width: 52.1021%; margin-right: 10px;&quot; data-origin-width=&quot;406&quot; data-origin-height=&quot;705&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;52.72&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/n9yel/btsteMzhYpH/FSrEKxD0du61HRqL5QAYak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fn9yel%2FbtsteMzhYpH%2FFSrEKxD0du61HRqL5QAYak%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;406&quot; height=&quot;705&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rLElc/btstba1DJ8O/uxC16YMYYVuA8H22LkaZR1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rLElc/btstba1DJ8O/uxC16YMYYVuA8H22LkaZR1/img.png&quot; style=&quot;width: 46.7351%;&quot; data-origin-width=&quot;421&quot; data-origin-height=&quot;815&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;47.28&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rLElc/btstba1DJ8O/uxC16YMYYVuA8H22LkaZR1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrLElc%2Fbtstba1DJ8O%2FuxC16YMYYVuA8H22LkaZR1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;421&quot; height=&quot;815&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;Naver2Tistory Unit Test&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;TistoryClient가 들어있는 부분 빼고는 가능한 한 많은 module들에 unit test code를 작성했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;coverage를 측정하고 싶은데&amp;nbsp;순수 java 프로젝트는 jacoco test coverage를 확인할 수가 없어서, 임시로 maven 프로젝트를 하나 만들고 그 내부에서 jacoco coverage를 돌렸다. 패키지 앞의 aaaa.는 임시로 붙여둔 거니까 무시하고 생각하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1277&quot; data-origin-height=&quot;305&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lYcHs/btsFLoy8eDg/ucW8Mcn2uRZReaxhtm9k1k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lYcHs/btsFLoy8eDg/ucW8Mcn2uRZReaxhtm9k1k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lYcHs/btsFLoy8eDg/ucW8Mcn2uRZReaxhtm9k1k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlYcHs%2FbtsFLoy8eDg%2FucW8Mcn2uRZReaxhtm9k1k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1277&quot; height=&quot;305&quot; data-origin-width=&quot;1277&quot; data-origin-height=&quot;305&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;직접 API 통신을 해야 하는 auth 패키지, 전체를 통합하는 migrator 패키지, http 연결 등이 있는 util 패키지를 제외하고는 &lt;b&gt;85%&lt;/b&gt;의 커버리지가 나온다. 낮게 나온 3가지 패키지를 확인해 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;scrappers 패키지&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;951&quot; data-origin-height=&quot;141&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dddiGv/btsFJthiXaV/zl3cLUulkkyrk1MIXXrOpk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dddiGv/btsFJthiXaV/zl3cLUulkkyrk1MIXXrOpk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dddiGv/btsFJthiXaV/zl3cLUulkkyrk1MIXXrOpk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdddiGv%2FbtsFJthiXaV%2Fzl3cLUulkkyrk1MIXXrOpk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;951&quot; height=&quot;141&quot; data-origin-width=&quot;951&quot; data-origin-height=&quot;141&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Bva06/btsFI3b5mEs/KjVts8ReZ5JxXSdpQ5chk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Bva06/btsFI3b5mEs/KjVts8ReZ5JxXSdpQ5chk1/img.png&quot; data-origin-width=&quot;658&quot; data-origin-height=&quot;495&quot; data-is-animation=&quot;false&quot; style=&quot;width: 57.1795%; margin-right: 10px;&quot; data-widthpercent=&quot;57.85&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Bva06/btsFI3b5mEs/KjVts8ReZ5JxXSdpQ5chk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBva06%2FbtsFI3b5mEs%2FKjVts8ReZ5JxXSdpQ5chk1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;658&quot; height=&quot;495&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/v5PrX/btsFIjfagDV/KH7htak1kskObKDsNNqHOk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/v5PrX/btsFIjfagDV/KH7htak1kskObKDsNNqHOk/img.png&quot; data-origin-width=&quot;798&quot; data-origin-height=&quot;824&quot; data-is-animation=&quot;false&quot; style=&quot;width: 41.6577%;&quot; data-widthpercent=&quot;42.15&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/v5PrX/btsFIjfagDV/KH7htak1kskObKDsNNqHOk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fv5PrX%2FbtsFIjfagDV%2FKH7htak1kskObKDsNNqHOk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;798&quot; height=&quot;824&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;직접 블로그에서 scrap하는 동작이 없기 때문에 전체가 coverage 0%로 나온다. `BlogScrapper.java`는 오류 부분만 빨간 줄.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;음.. 내가 작성한 것은 unit test고, 이쪽은 통합 테스트 쪽에 가까워서.. 추후 시간이 나면 통합 테스트를 작성하는 식으로 가야 하나 고민이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;tistory 패키지&lt;span&gt; - &lt;/span&gt;&lt;/span&gt;TistoryConverter&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;976&quot; data-origin-height=&quot;167&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c95SzY/btsFLUR4Xq0/Y6NKM63toAkuAfonBrgrK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c95SzY/btsFLUR4Xq0/Y6NKM63toAkuAfonBrgrK0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c95SzY/btsFLUR4Xq0/Y6NKM63toAkuAfonBrgrK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc95SzY%2FbtsFLUR4Xq0%2FY6NKM63toAkuAfonBrgrK0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;976&quot; height=&quot;167&quot; data-origin-width=&quot;976&quot; data-origin-height=&quot;167&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnORCa/btsFIu8ELf4/v1UNmqmb8kZ4ULFGxsVjs1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnORCa/btsFIu8ELf4/v1UNmqmb8kZ4ULFGxsVjs1/img.png&quot; data-origin-width=&quot;769&quot; data-origin-height=&quot;831&quot; data-is-animation=&quot;false&quot; style=&quot;width: 40.5249%; margin-right: 10px;&quot; data-widthpercent=&quot;41&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnORCa/btsFIu8ELf4/v1UNmqmb8kZ4ULFGxsVjs1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbnORCa%2FbtsFIu8ELf4%2Fv1UNmqmb8kZ4ULFGxsVjs1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;769&quot; height=&quot;831&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/k0pmG/btsFIyiWuYi/4B84lmS0q34hv1Ea8JkMV1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/k0pmG/btsFIyiWuYi/4B84lmS0q34hv1Ea8JkMV1/img.png&quot; data-origin-width=&quot;755&quot; data-origin-height=&quot;567&quot; data-is-animation=&quot;false&quot; style=&quot;width: 58.3123%;&quot; data-widthpercent=&quot;59&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/k0pmG/btsFIyiWuYi/4B84lmS0q34hv1Ea8JkMV1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fk0pmG%2FbtsFIyiWuYi%2F4B84lmS0q34hv1Ea8JkMV1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;755&quot; height=&quot;567&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;TypeConverter 패키지는 3항 연산자에서 하나를 확인 안해서 71%이고, `TistoryConverter.java`의 경우 위와 마찬가지로 통합 테스트가 없다보니 `convert()`가 호출되지 않아서 그런 듯 하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;tistory.typeConverter&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1034&quot; data-origin-height=&quot;297&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6eqWK/btsFK41Ybua/FsKmhTCPnWbkf2RVzqsF8K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6eqWK/btsFK41Ybua/FsKmhTCPnWbkf2RVzqsF8K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6eqWK/btsFK41Ybua/FsKmhTCPnWbkf2RVzqsF8K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6eqWK%2FbtsFK41Ybua%2FFsKmhTCPnWbkf2RVzqsF8K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1034&quot; height=&quot;297&quot; data-origin-width=&quot;1034&quot; data-origin-height=&quot;297&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;925&quot; data-origin-height=&quot;865&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kxLGv/btsFJqSoTaa/0MSxLZaidZqXM25SomJ9o1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kxLGv/btsFJqSoTaa/0MSxLZaidZqXM25SomJ9o1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kxLGv/btsFJqSoTaa/0MSxLZaidZqXM25SomJ9o1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkxLGv%2FbtsFJqSoTaa%2F0MSxLZaidZqXM25SomJ9o1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;925&quot; height=&quot;865&quot; data-origin-width=&quot;925&quot; data-origin-height=&quot;865&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;`ImageConverter`만 매우 낮게 나오는데, 다른 converter들은 string을 입력으로 받는 테스트 케이스를 작성했는데, image는 테스트 코드에 resource를 할당하지 않았기 때문이다. 그래서 동작만 확인해서 밑에 다 빨간 줄 나는 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;총평&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;시간이 나면 통합 테스트를 작성해서 전체 커버리지를 올릴 수 있도록 하는 게 좋을 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;후기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이렇게 모든 리팩토링한 내용을 살펴봤다. 코드를 짜면서 Clean Code를 읽으면서 느꼈던 읽기 좋은 코드, Clean Architecture를 읽으면서 느꼈던 유지보수하기 좋은 코드, SOLID principles를 보면서 배웠던 점들을 최대한 적용하려 고민했고, 그 결과로 &lt;span style=&quot;text-align: start;&quot;&gt;마음에 드는 부분도, 배운 것도 많은 과정이었다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;목표 달성?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;리팩토링을 시작했을 때 아래와 같은 4가지를 달성하고자 했었다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;주석 삭제. 의도를 설명한 주석, 유지보수 시 참고가 될 만한 주석만 남기고 모두 삭제. JavaDoc은 필요한 public 함수에만 남기고자 했다.&lt;/li&gt;
&lt;li&gt;예외처리를 간소하게. 곳곳에 퍼져 있는 try-catch문을 응집시키고자 했다.&lt;/li&gt;
&lt;li&gt;최대한 의존성을 줄이고&lt;b&gt;&amp;nbsp;확장성 있게&lt;/b&gt;&amp;nbsp;코드를 작성하고자 했다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 프로그램은 Naver2Tistory이지만 추후에 Naver2Notion처럼 목적지 블로그를 추가할 수도 있고, Medium2Tistory처럼 출발지 블로그를 추가할 수도 있다고 가정했다.&lt;/li&gt;
&lt;li&gt;현재 지원하는 스타일 이외에 다른 스타일들이 추가될 수 있다고 가정했다.&lt;/li&gt;
&lt;li&gt;의존성을 줄이기 위해서 interface나 abstract class를 사용하고, if-else문을 줄이기 위해 map을 사용하는 방식을 채택했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;테스트 코드 작성. 사용자 아이디/패스워드가 필요한 TistoryClient와 TistoryClient를 사용하는 class를 제외하고는 최대한 unit test를 진행하고자 했다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;일단 필요없는 주석들은 대부분 지웠고, 주석은 최대한 지우되 유지보수하는 사람의 입장에서 필요한 부분에만 남기고자 했다. 예를 들면 interface의 구현체를 어디에 추가하면 되는지 등등.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;try-catch문도 상당히 응집시켰다. UrlProcessor, Scrapper, Converter, AuthClient들은 모두 Exception을 던지기만 하고, 이 module을 직접적으로 사용하는 TargetBlogConfigFactory와 Migrator에서 모든 Exception들을 받아 처리하게 만들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;interface, abstract class 등을 다양하게 사용해 의존성을 줄였고, strategy pattern을 사용해 확장하기 쉬운 코드를 작성했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;테스트 코드도 작성했다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;초기에 설정한 목표들은 모두 달성한 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;배운 점들&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;interface와 abstract class의 사용 구분&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;일단 배운 점들 중 하나는 &lt;b&gt;언제 interface/interface + static method/abstract class를 사용하는지 나름의 철학이 생겼다&lt;/b&gt;는 것이다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;일단 interface를 사용하고&lt;/li&gt;
&lt;li&gt;공통 함수만 필요한 경우 static이나 default를 사용하고
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;만약 static으로 처리할 수 있는 변수면 interface를 사용한다.&lt;/li&gt;
&lt;li&gt;만약 abstract class를 사용하는 것이 유지보수에 이점이 있다고 판단하면 abstract class를 사용한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;멤버 변수가 필요한 경우 abstract class를 사용한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;strategy pattern&lt;/h4&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;다음으로 배운 것은 &lt;b&gt;Strategy pattern&lt;/b&gt;이었다. 사용하면 코드 복잡도가 올라간다는 단점이 있지만, &lt;b&gt;유지보수가 매우 쉬워지고&lt;/b&gt; 시간복잡도가 O(n)인 if-else문에 비해 map을 사용하면 O(logn)으로 처리할 수 있다는 &lt;b&gt;시간적 이점&lt;/b&gt;도 있었다.&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;한편, template method pattern과 abstract factory pattern도 사용했는데 이 둘의 차이점에 대해서도 알아야 할 것 같다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&amp;nbsp;template method pattern은 사실상 interface 구현체나 abstract class의 상속을 사용한 polymorphism을 사용하는 기법이고,&amp;nbsp;abstract factory pattern은 object를 생성하는 기법이다... 이 정도.&lt;/blockquote&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;느낀점&lt;/h3&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이렇게 확장성과 유지보수성을 신경쓰면서 코드를 짜니 닥치는 대로 구현하는 방식보다 배는 많은 시간이 들어갔다. 특히 초반에 설계 부분에서 많은 시간이 걸렸다. 그러나 이렇게 구조화하면서 짜니, 처음 설계가 어려웠지 코드 구현은 생각한 대로 쭉쭉 짜졌다. 물론 중간에 생각치 못했던 여러 이슈들이 있었지만(고민했던 점들) 모듈화가 잘 되어 있었기에 이들을 해결하기 쉬웠다.&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;또 하나 느낀 점은, unit test code를 꼭 작성해야 한다는 것이다. unit test를 실행함으로써 해당 module이 잘 작동한다는 것을 바로바로 알 수 있었기 때문에 테스트에 걸리는 시간이 확 줄게 되었다. 아무리 처음에 설계를 잘 짜더라도 구조를 조금씩 조금씩 바꿔가는 일이 무조건 생겼는데 unit test로 인해 원래 작동하던 것이 잘 작동함을 보장받으니 마음이 너무 편했다. &lt;span style=&quot;text-align: start;&quot;&gt;처음에 만드는 것은 귀찮았지만...&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&amp;nbsp;또, module을 쓰고 바로바로 테스트 코드를 돌리면서 결과를 볼 수 있었어서 더 좋았던 것 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Project/Project N2T</category>
      <author>hyelie</author>
      <guid isPermaLink="true">https://hyelie.tistory.com/618</guid>
      <comments>https://hyelie.tistory.com/entry/N2T-%EB%A6%AC%ED%8C%A9%ED%86%A0%EB%A7%81-%EA%B8%B0%EB%A1%9D#entry618comment</comments>
      <pubDate>Wed, 6 Sep 2023 02:32:43 +0900</pubDate>
    </item>
    <item>
      <title>23.08.21. 풀었던 문제들 복기</title>
      <link>https://hyelie.tistory.com/entry/230821-%ED%92%80%EC%97%88%EB%8D%98-%EB%AC%B8%EC%A0%9C%EB%93%A4-%EB%B3%B5%EA%B8%B0</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Leetcode 459. Repeated Substring Pattern, 15분&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;주어진 대로 구현만 하면 되는 문제.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;일단 substring으로 원 string을 표시하기 위해서는 substring의 길이가 s의 약수여야 한다. 만약 그렇다면, 해당 substring을 q개(s.length()/substr.length())만큼 만들어서 전체를 살펴보면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1660456126668&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Runtime 19 ms Beats 64.17%
// Memory 15.4 MB Beats 38.41%

class Solution {
public:
    bool repeatedSubstringPattern(string s) {
        int len = s.length();
        for(int i = 0; i&amp;lt;len/2; i++){
            if(len % (i+1) == 0){
                string substr = s.substr(0, i+1);
                bool repeated = true;
                for(int q = 1; q&amp;lt;len/(i+1); q++){
                    if(s.substr(q*(i+1), i+1) != substr) {
                        repeated = false;
                        break;
                    }
                }
                if(repeated) return true;
            }
        }
        return false;
    }
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>PS/PS Log</category>
      <author>hyelie</author>
      <guid isPermaLink="true">https://hyelie.tistory.com/616</guid>
      <comments>https://hyelie.tistory.com/entry/230821-%ED%92%80%EC%97%88%EB%8D%98-%EB%AC%B8%EC%A0%9C%EB%93%A4-%EB%B3%B5%EA%B8%B0#entry616comment</comments>
      <pubDate>Mon, 21 Aug 2023 17:37:42 +0900</pubDate>
    </item>
    <item>
      <title>23.08.17. 풀었던 문제들 복기</title>
      <link>https://hyelie.tistory.com/entry/230817-%ED%92%80%EC%97%88%EB%8D%98-%EB%AC%B8%EC%A0%9C%EB%93%A4-%EB%B3%B5%EA%B8%B0</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Leetcode 542. 01 Matrix, 20분&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;0과 1만 있는 matrix에서 0으로부터 거리를 리턴하면 되는 문제.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;나는 BFS를 사용했다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모든 0지점부터 BFS 탐색을 시작한다. 단, 갱신할 수 없는 위치라면(다른 0으로부터 더 가깝다면) 그곳은 탐색하지 않는다.&lt;/li&gt;
&lt;li&gt;하나의 최적화를 추가하는데, 0에서 BFS를 각각 수행하는 것이 아니라(그렇게 하면 BFS가 10000번 수행될 수도 있다.)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모든 0을 queue에 넣고 한 칸씩 BFS 해 나가는 것이다. 그러면 0에 인접한 칸은 그 개수만큼 방문하지만, 다음 갱신할 칸은 1번씩만 방문한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그러면 worst case 최대 1만번 겹치는 탐색이 존재하고, &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;모든 칸을 1만번씩 방문하게 되므로 시간 내에 충분히 풀 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1660456126668&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Runtime 62 ms Beats 91.36%
// Memory 30.2 MB Beats 86.75%

typedef pair&amp;lt;int, int&amp;gt; pii;
int dr[4] = {1, 0, -1, 0};
int dc[4] = {0, 1, 0, -1};

class Solution {
public:
    vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; updateMatrix(vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt;&amp;amp; mat) {
        int m = mat.size(), n = mat[0].size(), INF = 10001;
        vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; answer(m, vector&amp;lt;int&amp;gt;(n, INF));
        queue&amp;lt;pii&amp;gt; q;
        for(int i = 0; i&amp;lt;m; i++){
            for(int j = 0; j&amp;lt;n; j++){
                if(mat[i][j] == 0){
                     answer[i][j] = 0;
                     q.push({i, j});
                }
            }
        }

        while(!q.empty()){
            pii cur = q.front(); q.pop();
            for(int i = 0; i&amp;lt;4; i++){
                int nr = cur.first + dr[i];
                int nc = cur.second + dc[i];
                if(0 &amp;lt;= nr &amp;amp;&amp;amp; nr &amp;lt; m &amp;amp;&amp;amp; 0 &amp;lt;= nc &amp;amp;&amp;amp; nc &amp;lt; n &amp;amp;&amp;amp; answer[cur.first][cur.second] + 1 &amp;lt; answer[nr][nc]){
                    answer[nr][nc] = answer[cur.first][cur.second] + 1;
                    q.push({nr, nc});
                }
            }
        }

        return answer;
    }
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;시간복잡도&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;BFS에 O(V+E)인데 V = 1만, E=4만, 추가적으로 O(V)만큼의 시간이 필요하다. 그러니까 O(V)이다. (V는 1만)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;공간복잡도&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;정답 배열을 만들기 위해 O(mn) size 배열을 만들어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;BOJ 14725. 개미굴, 25분&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 문제는 그다?지 어렵지 않은 문제다. tree의 개념과 tree traverse를 알고 있다면 간단하게 풀 수 있는 문제. 단, [어떤 node의 child들이 여러 개가 들어갈 수 있다는 것 + child들이 이름 순으로 정렬되어야 한다는 것]을 유의해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;일단 child들이 여러개가 들어갈 수 있으며 정렬되어야 하기 때문에 vector는 사용하지 않았다. 정렬하기 힘들기 때문이었다. 그러면 set, map이 남는데... set은 비교 함수를 만들어야 해서 map을 사용했다. child의 먹이 이름을 key, child node를 value로 설정했다. 그러면 child 먹이 이름을 사용해 쉽게 접근할 수 있을 뿐더러 정렬도 되어있기 때문에 위 조건을 만족하기 쉬웠다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;메모리까지 최적화하기 위해서는 set을 사용하는 것이 낫다. 내가 구현한 것은 map key에 name이 들어가고, 또 node에도 name이 들어가기 때문이다. 그러나 속도를 위해 이렇게 구현했다.&lt;/p&gt;
&lt;pre id=&quot;code_1660456126668&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;struct Node{
    string name;
    map&amp;lt;string, Node*&amp;gt; childs;
};

Node* root = new Node();

int N;

// 순회하며 먹이 이름을 출력
void traverse(Node* cur, int depth){
    for(int i = 0; i&amp;lt;2*depth; i++) cout&amp;lt;&amp;lt;'-';
    cout&amp;lt;&amp;lt;cur-&amp;gt;name&amp;lt;&amp;lt;'\n';

    for(auto &amp;amp;elem : cur-&amp;gt;childs){
        traverse(elem.second, depth+1);
    }
}

void solve(){
    // 입력과 동시에 tree 구성
	cin&amp;gt;&amp;gt;N;
    int K;
    for(int i = 0; i&amp;lt;N; i++){
        cin&amp;gt;&amp;gt;K;
        string name;
        Node* cur = root;
        for(int j = 0; j&amp;lt;K; j++){
            cin&amp;gt;&amp;gt;name;
            if(cur-&amp;gt;childs.find(name) == cur-&amp;gt;childs.end()){ // 없는 경우 새로 만들어 넣음.
                Node* new_node = new Node();
                new_node-&amp;gt;name = name;
                cur-&amp;gt;childs[name] = new_node;
                cur = cur-&amp;gt;childs[name];
            } else{ // 있는 경우 기존 것으로 이동
                cur = cur-&amp;gt;childs[name];
            }
        }
    }

    // traverse 및 출력
    for(auto &amp;amp;elem : root-&amp;gt;childs){
        traverse(elem.second, 0);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;시간복잡도&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;먹이 정보 개수 N은 1000보다 작으모, 각 먹이 정보 개수의 길이는 15보다 작다. 즉, depth가 최대 15인 tree이며 width가 최대 1000이라는 뜻이다. 그러면 worst case node 개수는 15000이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;tree traverse에 O(V+E)가, tree에 넣을 때 O(depth * logN)만큼이 걸린다. 그러면 O(V+E)가 더 우세하므로 O(15000)정도 걸릴 것으로 예상된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;공간복잡도&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;node 개수는 worst case 15000개이므로 O(15000)이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>PS/PS Log</category>
      <author>hyelie</author>
      <guid isPermaLink="true">https://hyelie.tistory.com/615</guid>
      <comments>https://hyelie.tistory.com/entry/230817-%ED%92%80%EC%97%88%EB%8D%98-%EB%AC%B8%EC%A0%9C%EB%93%A4-%EB%B3%B5%EA%B8%B0#entry615comment</comments>
      <pubDate>Thu, 17 Aug 2023 18:11:36 +0900</pubDate>
    </item>
  </channel>
</rss>