<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Index's Tistory</title>
    <link>https://index1207.tistory.com/</link>
    <description>게임 서버 개발자 지망생의 공부 기록
작은것도 꼼꼼히</description>
    <language>ko</language>
    <pubDate>Sun, 16 Aug 2026 11:32:08 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>index1207</managingEditor>
    <image>
      <title>Index's Tistory</title>
      <url>https://tistory1.daumcdn.net/tistory/5784548/attach/50a5adfe50d54eacbbe0dd107f928c48</url>
      <link>https://index1207.tistory.com</link>
    </image>
    <item>
      <title>[Network] HTTP 프로토콜 분석</title>
      <link>https://index1207.tistory.com/20</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;HTTP 프로토콜이란&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹에서 이루어지는 통신에 사용되는 네트워크 프로토콜. TCP기반 소켓을 기반으로 구현된다.&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;HTTP 프로토콜에서 통신은 클라이언트 측이 요청(Request)을 보내면 서버가 그에 대한 응답(Response)을 보내 통신하는 방식을 사용한다. 요청과 응답은 CRLF(\r\n)를 사용해 개행을 구분한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Request&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Request의 구조는 Startline, Header, Body로 나뉘어져 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Start line
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ex.) GET / HTTP/1.1&lt;/li&gt;
&lt;li&gt;메서드, 경로, HTTP 프로토콜 버전이 포함된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Header&amp;nbsp;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Connection, Accept, Date등의 추가 정보가 포함되어 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Body: POST 메서드를 사용할 때 추가 정보를 담을 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1731155837126&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;GET / HTTP/1.1
Accept: text/plain
Connection: close&lt;/code&gt;&lt;/pre&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;Request Method&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Request를 보낼 때 start line에서 Method를 사용해 요청을 보낸다. 종류는 다음과 같이 있지만, 주로 쓰이는 것은 GET과 POST이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;GET: 리소스를 요청하는 기본적인 메서드. 리소스를 URL에서 query형태로 요청하므로 Body가 필요하지 않다.&lt;/li&gt;
&lt;li&gt;POST: 리소스를 요청하는 메서드. GET과 달리 URL에 표현하지 않고, Body에서 리소스를 요청한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Request Header&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Accept: 원하는 타입의 데이터의 우선순위
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ex.) text/html, text/plain, image/jpeg, ...&lt;/li&gt;
&lt;li&gt;ex.) text/*, */*&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Connection: 연결 유지(keep-alive)에 대한 옵션&lt;/li&gt;
&lt;li&gt;Date: 요청을 생성한 시간&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Response&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Status line&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;HTTP 프로토콜 버전, 상태 코드, 상태 메세지가 포함된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Header
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Content type, content length 등, 응답에 대한 추가 정보가 포함되어 있다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Body
&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;pre id=&quot;code_1731155891822&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;HTTP/1.1 200 OK
Content-Type: text/plain
Content-Length: 13

Hello, World!&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Response Header&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Content-Type: 응답 데이터에 대한 데이터 타입&lt;/li&gt;
&lt;li&gt;Content-Length: 응답 데이터에 대한 길이&lt;/li&gt;
&lt;li&gt;Date: 응답을 생성한 시간&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;URL 구조&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;URL의 구조는 기본적으로 다음과 같이 프로토콜, 호스트, 포트, 경로로 이루어져 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1731244169297&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;${프로토콜(https, https)}://${호스트}:${포트(기본80)}/${경로}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저에서 경로를 지정해 입력해보면 GET이나 POST를 사용해 Request를 지정된 서버로 송신하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GET 같은 경우 경로에 ?변수=값 과 같은 형태로 query를 추가하는 것도 가능하다.&lt;/p&gt;</description>
      <category>network</category>
      <category>HTTP</category>
      <author>index1207</author>
      <guid isPermaLink="true">https://index1207.tistory.com/20</guid>
      <comments>https://index1207.tistory.com/20#entry20comment</comments>
      <pubDate>Sun, 10 Nov 2024 22:11:15 +0900</pubDate>
    </item>
    <item>
      <title>[C#] Disposable과 using문</title>
      <link>https://index1207.tistory.com/19</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;Disposable&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C#에선 객체의 할당과 해제를 GC가 관리한다. 하지만 GC는 관리되지 않은 리소스를 인식하지 못하고, 자주 작동하게 되면 성능에 오버헤드가 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Disposable은 이런 단점을 해결한 인터페이스이다. using 블럭을 벗어나면 자동으로 Dispose메서드를 호출하고 할당해제하여 성능을 개선시킬 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인터페이스는 IDisposable과 await using에서 사용할 수 있는 IAsyncDisposable이 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;using문&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같이 Disposable객체를 생성하고 using블럭 안에서 사용이 가능하다.&lt;/p&gt;
&lt;pre id=&quot;code_1728875733855&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using (var object = new DisposableClass())
{
	...
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;예제&lt;/h3&gt;
&lt;pre id=&quot;code_1728876393038&quot; class=&quot;csharp&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;csharp&quot;&gt;&lt;code&gt;class DisposableClass : IDisposable
{
	public DisposableClass()
    {
    	Console.WriteLine(&quot;Constructor()&quot;);
    }
    
    public void Dispose()
    {
    	Console.WriteLine(&quot;Destroyed&quot;);
    }
}

class Program
{
	static void Main(String[] args)
    {
    	using (var disp = new DisposableClass())
        {
        }
        Console.WriteLine(&quot;End&quot;);
    }
}

/*
	결과:
    Constructor()
    Destroyed
    End
*/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;using문을 사용하지 않고 사용한다면 GC에 의해 객체가 관리되므로 Dispose메서드가 호출되지 않는다.&lt;/p&gt;
&lt;pre id=&quot;code_1728877431311&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static void Main(String[] args)
{
	var object = new DisposableClass();
    Console.WriteLine(&quot;End&quot;);
}
/*
	결과:
    Constructor()
    End
*/&lt;/code&gt;&lt;/pre&gt;</description>
      <category>C#</category>
      <category>c#</category>
      <author>index1207</author>
      <guid isPermaLink="true">https://index1207.tistory.com/19</guid>
      <comments>https://index1207.tistory.com/19#entry19comment</comments>
      <pubDate>Mon, 14 Oct 2024 12:44:45 +0900</pubDate>
    </item>
    <item>
      <title>[Network] 차세대 IO 모델</title>
      <link>https://index1207.tistory.com/18</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이전에는 윈도우 진영에선 IOCP, 리눅스 및 UNIX진영에서는 epoll, kqueue 등을 사용하고 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 2010년대에 들어 구식 모델보다 더 향상된 차세대 IO모델이 발표되었다. 윈도우에서는 Windows Server 2012에 맞춰 IOCP의 확장 격인 RIO(Registered I/O)를 발표, 리눅스에선 커널 5.1 버전부터 io-uring을 사용할 수 있게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;윈도우의 RIO는 기존 IOCP에서 잦은 시스템콜과 유저모드와 커널모드 간 컨텍스트 스위칭이 잦아 CPU의 점유율이 높은 단점아닌 단점이 있었다. RIO는 이를 해결하기 위해 CP(Completion Port)를 '완료(Completion)'와 '요청(Request)'에 대한 원형 큐로 나누고, 유저모드와 커널모드간 공유하는 메모리를 통한 IO로 컨텍스트 스위칭을 현저히 줄였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리눅스의 io-uring은 RIO와 같이 '완료(Completion)'와 '제출(Submission)'을 원형큐로 만들어 IO를 처리하고, 유저모드와 커널모드가 공유하는 메모리를 통한 컨텍스트 스위칭을 줄인 것은 동일하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RIO와의 차이점이라 한다면 RIO의 경우 쓰기/읽기 밖에 지원하지 않아 accept, connect와 같은 것은 이벤트를 사용하거나, IOCP를 이용해야 한다. 또한 IOCP와 혼합해서 사용한다면 코드가 복잡해지고, 개발 중 제한사항 등이 꽤 많이 존재한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;io-uring은 accept, connect, read, write 등을 모두 지원하고 인터페이스와 같은 것도 매우 심플해 사용하기 매우 간단하다. 그리고 리눅스 커뮤니티, man7 등 비교적 정보가 많다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 되도록이면 IOCP를 사용하는 것을 추천한다. RIO가 발표된지도 10년이 넘었지만 정보가 많이 부족하고, 또 실제로 사용하는 회사는 국내에 NC소프트밖에 없다 생각해도 될 정도로 없다. 게다가 RIO로 바꾸면서까지 얻는 효용성이 없어 추천하지 않는다.&lt;/p&gt;</description>
      <category>network</category>
      <category>io-uring</category>
      <category>Rio</category>
      <author>index1207</author>
      <guid isPermaLink="true">https://index1207.tistory.com/18</guid>
      <comments>https://index1207.tistory.com/18#entry18comment</comments>
      <pubDate>Wed, 11 Sep 2024 11:55:41 +0900</pubDate>
    </item>
    <item>
      <title>[C++] 메모리 뷰에서의 다중 상속, 캐스팅</title>
      <link>https://index1207.tistory.com/16</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;만약 public을 통해 다중상속을 한다면 static_cast를 통해 쉽게 상위 클래스로 형변환이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 부모의 멤버를 감추기 위해 private으로 상속한다면 static_cast를 사용할 수 없다.&lt;/p&gt;
&lt;pre id=&quot;code_1723705454102&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;struct A {
	unsigned int data;
};

struct B {
	unsigned short data;
};

class C : A, B {
public:
	C() {
		A::data = 0xA;
		B::data = 0xB;
		C::data = 0xC;
	}
	unsigned char data;
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같은 상황이라고 치자. 이때 클래스 C로 변수를 만들면 메모리 뷰 관점에서는 다음과 같을 것이다. (public 상속을 해도 같은 결과가 나온다)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;268&quot; data-origin-height=&quot;30&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lnltg/btsI4kIopo3/eJTZekNy4jmVUc2aLh79q0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lnltg/btsI4kIopo3/eJTZekNy4jmVUc2aLh79q0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lnltg/btsI4kIopo3/eJTZekNy4jmVUc2aLh79q0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Flnltg%2FbtsI4kIopo3%2FeJTZekNy4jmVUc2aLh79q0%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;268&quot; height=&quot;30&quot; data-origin-width=&quot;268&quot; data-origin-height=&quot;30&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[A:4byte][B:2byte][C:1byte]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 c의 주소를 통해 B로 reinterpret_cast 한다면 A의 주소값을 기반으로 캐스팅되기 때문에 컴파일은 가능하지만, 원하는 값을 얻어오기는 불가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉽게 해결하기 위해서는 그냥 public 상속을 하는 것이지만, 나는 어떻게는 상위 클래스를 감추고 싶다면 다음과 같은 방법이 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1723706271770&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;reinterpret_cast&amp;lt;B*&amp;gt;(reinterpret_cast&amp;lt;A*&amp;gt;(&amp;amp;c)+1)-&amp;gt;data = 0xD;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매-우 불편하지만 가능은 하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;과정은 A로의 형변환을 통해 주소크기를 4로 바꾼 뒤, 주소 다음값을 B로 형변환한 것이다.&lt;/p&gt;</description>
      <category>C++</category>
      <author>index1207</author>
      <guid isPermaLink="true">https://index1207.tistory.com/16</guid>
      <comments>https://index1207.tistory.com/16#entry16comment</comments>
      <pubDate>Thu, 15 Aug 2024 16:21:11 +0900</pubDate>
    </item>
    <item>
      <title>[C++] vcpkg 라이브러리 패키징</title>
      <link>https://index1207.tistory.com/15</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;소개&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;vcpkg는 microsoft에서 개발한 패키지 매니저로, 파이썬의 pip와 같이 커맨드라인을 통해 패키지를 설치할 수 있도록 하는 프로그램이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글에서는 내가 직접 Microsoft/vcpkg 레포지토리에 PR을 올려 머지된 경험을 바탕으로 작성되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더 자세한 내용은 &lt;a title=&quot;여기&quot; href=&quot;https://learn.microsoft.com/en-us/vcpkg/get_started/get-started-packaging?pivots=shell-cmd&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;여기&lt;/a&gt;를 참고해보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-08-06 164557.png&quot; data-origin-width=&quot;504&quot; data-origin-height=&quot;179&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bm1k1w/btsIYcbbpN6/7sOTVfBj1mZfjfnJ1jzeO1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bm1k1w/btsIYcbbpN6/7sOTVfBj1mZfjfnJ1jzeO1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bm1k1w/btsIYcbbpN6/7sOTVfBj1mZfjfnJ1jzeO1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbm1k1w%2FbtsIYcbbpN6%2F7sOTVfBj1mZfjfnJ1jzeO1%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;504&quot; height=&quot;179&quot; data-filename=&quot;스크린샷 2024-08-06 164557.png&quot; data-origin-width=&quot;504&quot; data-origin-height=&quot;179&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;순서&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;vcpkg에 새로운 패키지를 업로드하는 과정은 다음과 같다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;CMakeList 수정&lt;/li&gt;
&lt;li&gt;vcpkg 포크(fork) 후 리모트 변경&lt;/li&gt;
&lt;li&gt;port 작성 후 커밋 &amp;amp; push&lt;/li&gt;
&lt;li&gt;최종적 PR 보내기&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;vcpkg에서 port란&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;vcpkg에는 우리가 작성한 라이브러리 코드가 들어있지 않고, 저장소의 이름과 버전 SHA 해시를 가지고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 작성해야 하는 port는 각각의 라이브러리의 버전과 설치 방법에 대한 내용이 담겨있는 정보이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자는 개발자가 작성한 port를 통해 원격 저장소를 통해 라이브러리를 설치하게 된다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. CMakeList 수정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 라이브러리의 CMakeList.txt를 수정해보자. 만약 Visual Studio나 Xcode를 사용하고 있다면 CMake로 전환하자. CMakeList.txt에서 수정 할 것은 vcpkg의 규격에 맞도록 네임스페이스와 export, install 명령어를 추가해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대략적인 명령어 구문은 다음과 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1723020025574&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# definition
string(REPLACE &quot;/${CMAKE_LIBRARY_ARCHITECTURE}&quot; &quot;&quot; CMAKE_INSTALL_LIBDIR_ARCHIND &quot;${CMAKE_INSTALL_LIBDIR}&quot;)
set(EXPORT_NAMESPACE &quot;${PROJECT_NAME}::&quot;)
set(CMAKE_CONFIG_FILE_BASENAME &quot;${PROJECT_NAME}Config.cmake&quot;)
set(CMAKE_EXPORT_FILE_BASENAME &quot;${PROJECT_NAME}Export.cmake&quot;)

# install &amp;amp; export
install(TARGETS ${PROJECT_NAME}
        EXPORT ${PROJECT_NAME}
        INCLUDES DESTINATION &quot;${CMAKE_INSTALL_INCLUDEDIR}&quot;)
        
export(TARGETS &quot;${PROJECT_NAME}&quot;
        NAMESPACE &quot;${EXPORT_NAMESPACE}&quot;
        FILE &quot;${CMAKE_EXPORT_FILE_BASENAME}&quot;
        EXPORT_LINK_INTERFACE_LIBRARIES)

install(EXPORT &quot;${PROJECT_NAME}&quot;
        FILE &quot;${CMAKE_CONFIG_FILE_BASENAME}&quot;
        NAMESPACE &quot;${EXPORT_NAMESPACE}&quot;
        DESTINATION &quot;${CMAKE_INSTALL_LIBDIR_ARCHIND}/cmake/${PROJECT_NAME}&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;2. vcpkg 포크(fork) 후 리모트 변경&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;vcpkg가 설치되어있지 않다면 &lt;a href=&quot;https://jungwoong.tistory.com/77&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;먼저 &lt;a href=&quot;https://github.com/microsoft/vcpkg&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;vcpkg의 레포지토리&lt;/a&gt;에서 fork를 한 뒤, vcpkg가 설치된 디렉토리로 이동 후 다음 명령어를 통해 remote를 추가해주자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;618&quot; data-origin-height=&quot;290&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wjKQO/btsIYdH3eWe/JqISFKqHFoSUgZpUE8KDR0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wjKQO/btsIYdH3eWe/JqISFKqHFoSUgZpUE8KDR0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wjKQO/btsIYdH3eWe/JqISFKqHFoSUgZpUE8KDR0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwjKQO%2FbtsIYdH3eWe%2FJqISFKqHFoSUgZpUE8KDR0%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;618&quot; height=&quot;290&quot; data-origin-width=&quot;618&quot; data-origin-height=&quot;290&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1723020743782&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;gt; git remote add myfork &amp;lt;내가_포크한_vcpkg레포지토리_URL&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. port 작성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;port의 요소로는 portfile.cmake, usage, vcpkg.json이 있다. portfile.cmake는 라이브러리의 설치에 대한 정보를 작성하고, usage는 라이브러리의 타겟, vcpkg에는 라이브러리의 릴리즈 버전, 종속성을 작성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 작업할 branch를 만들고, vcpkg/ports/ 에 새로운 폴더를 만들어 작업하자.&lt;/p&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;color: #ee2323;&quot;&gt;&lt;b&gt;※주의 &lt;/b&gt;&lt;/span&gt;파일의 맨 끝은 공백줄로 끝나야 한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;vcpkg.json&lt;/h4&gt;
&lt;pre id=&quot;code_1723021175878&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;name&quot;: &amp;lt;이름&amp;gt;,
  &quot;version&quot;: &amp;lt;릴리즈_버전&amp;gt;,
  &quot;description&quot;: &amp;lt;소개_및_설명&amp;gt;,
  &quot;homepage&quot;: &amp;lt;홈페이지_URL&amp;gt;,
  &quot;license&quot;: &amp;lt;라이선스&amp;gt;,
  &quot;supports&quot;: &amp;lt;지원_플랫폼&amp;gt;
  &quot;dependencies&quot;: [
    {
      &quot;name&quot;: &quot;vcpkg-cmake&quot;,
      &quot;host&quot;: true
    },
    {
      &quot;name&quot;: &quot;vcpkg-cmake-config&quot;,
      &quot;host&quot;: true
    },
    &amp;lt;추가_종속성&amp;gt;...
  ]
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;visual studio로 작성하게 되면 scheme를 자동으로 인식한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;usage&lt;/h4&gt;
&lt;pre id=&quot;code_1723021338252&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;이름&amp;gt; provides CMake targets:

    find_package(&amp;lt;이름&amp;gt; CONFIG REQUIRED)
    target_link_libraries(main PRIVATE &amp;lt;CMAKE_네임스페이스&amp;gt;::&amp;lt;이름&amp;gt;)&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;portfile.cmake&lt;/h4&gt;
&lt;pre id=&quot;code_1723021602639&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;vcpkg_from_github(
    OUT_SOURCE_PATH SOURCE_PATH
    REPO &amp;lt;레포지토리_URL&amp;gt;
    REF &quot;${VERSION}&quot; # VERSION은 vcpkg.json에서 작성한 version값이다. 깃허브의 릴리즈 태그와 일치해야 한다.
    SHA512 0 # 나중에 채워 넣을 예정
    HEAD_REF &amp;lt;메인_브렌치&amp;gt;
)


vcpkg_cmake_configure(
    SOURCE_PATH &quot;${SOURCE_PATH}&quot;
)

vcpkg_cmake_install()
vcpkg_cmake_config_fixup(CONFIG_PATH lib/cmake/&amp;lt;이름&amp;gt; PACKAGE_NAME &amp;lt;이름&amp;gt;)

file(REMOVE_RECURSE &quot;${CURRENT_PACKAGES_DIR}/debug/include&quot;)
file(INSTALL &quot;${SOURCE_PATH}/LICENSE&quot; DESTINATION &quot;${CURRENT_PACKAGES_DIR}/share/${PORT}&quot; RENAME copyright)

configure_file(&quot;${CMAKE_CURRENT_LIST_DIR}/usage&quot; &quot;${CURRENT_PACKAGES_DIR}/share/${PORT}/usage&quot; COPYONLY)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가적인 vcpkg명령어는 learn.microsoft를 참고.&lt;/p&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;pre id=&quot;code_1723021888967&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;vcpkg install &amp;lt;이름&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 애러와 함께 다음이 출력될 것이다.&lt;/p&gt;
&lt;pre id=&quot;code_1723021983866&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Expected hash: 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
Actual hash: &amp;lt;해시값&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;portfile.cmake의 SHA512에서 0으로 지정된 값을 출력된 해시로 변경하자.&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;설치에 성공했다면 다음 명령어를 실행해 vcpkg의 버전 데이터베이스에 추가하자.&lt;/p&gt;
&lt;pre id=&quot;code_1723022157232&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;vcpkg x-add-version &amp;lt;이름&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 변경사항을 커밋 한 뒤 myfork로 push하자.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. PR 보내기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최종적으로 패키지를 publish하기 위해 github에서 내가 fork한 vcpkg로 가서 PR를 보내보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;914&quot; data-origin-height=&quot;666&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mL2j4/btsIWcYqD0k/Oly7vC1rwBAkeEvD5tbNRk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mL2j4/btsIWcYqD0k/Oly7vC1rwBAkeEvD5tbNRk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mL2j4/btsIWcYqD0k/Oly7vC1rwBAkeEvD5tbNRk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmL2j4%2FbtsIWcYqD0k%2FOly7vC1rwBAkeEvD5tbNRk%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;914&quot; height=&quot;666&quot; data-origin-width=&quot;914&quot; data-origin-height=&quot;666&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PR의 형식은 다음과 같이 [&amp;lt;이름&amp;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;만약 마이크로소프트에 처음 PR을 보내면 봇의 코멘트가 올 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마이크로소프트 기여에 대한 약관이므로 자세히 잃어본 뒤 @microsoft-github-policy-service agree 를 코멘트로 보내 약관을 수락하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PR을 보내거나, 새로 커밋을 한다면 깃허브에서 CI 액션이 시작될 것이다. PR 요청자는 모든 항목이 통과해야 master에 머지를 할 수 있게 된다. CI와 별개로 마이크로소프의 코드 리뷰어의 리뷰가 시작된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드를 수정하게 된다면, &lt;b&gt;반드시&lt;/b&gt; vcpkg x-add-version &amp;lt;이름&amp;gt; --overwrite-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;리뷰어의 모든 요청사항 수정을 마친다면 info:reviewed 태그가 붙게 된다. 다른 리뷰어의 approve를 받게 된다면 최종적으로 master에 머지가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이로써 vcpkg에서 내가 개발한 라이브러리를 설치할 수 있게 된다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음으로 마소에 PR을 보내는 정말 특별한 경험이였다. 물론 직접적인 기여는 아니지만, 대형 오픈소스의 개발 방식에 대한 경험을 쌓을 수 있는 정말 좋은 경험이 된 것 같다.&lt;/p&gt;</description>
      <category>C++</category>
      <category>C++</category>
      <category>vcpkg</category>
      <author>index1207</author>
      <guid isPermaLink="true">https://index1207.tistory.com/15</guid>
      <comments>https://index1207.tistory.com/15#entry15comment</comments>
      <pubDate>Wed, 7 Aug 2024 18:41:49 +0900</pubDate>
    </item>
    <item>
      <title>[C++] 제대로 알고 사용하자. atd::async</title>
      <link>https://index1207.tistory.com/14</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;서론&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;게임 서버 프로젝트에서 로직 스레드를 싱글로 변환했다. 로직스레드가 싱글로 돌아가기 때문에 I/O에서 블로킹이 일어나면 그만큼 처리의 레이턴시가 생기는 것이기 때문에 이를 개선하기 위해 데이터베이스 쿼리를 비동기로 실행하고, 완료 콜백을 호출할 시스템을 구현하기로 했다.&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;따로 데이터베이스 쿼리를 처리할 스레드를 생성할까 했지만, 스레드와 스레드가 데이터를 주고받는것 자체가 지연이 생길 수 밖에 없어 좋지 않아 보였다. 그렇다고 각 쿼리마다 스레드를 파는 것은 너무 효율이 떨어지는 방법이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 단순한 방법으로 task를 생각했다. task는 promise, future, packaged_task, async 등을 사용해 개발하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;task의 구현 자체는 스레드의 wrapper이므로 느릴 수 밖에 없지만, 가독성 측면에서 월등히 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 문제가 있다. cppreference에 따르면 std::async의 비동기적 실행은&amp;nbsp;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;potentially in a separate thread which might be a part of a thread pool, 즉 task에 대해 매번 새로운 스레드를 생성하지 않다고 된다는 것이다. 하지만 실제로는 GNU의 libstdc++의 경우, stackoverflow의 한 유저가 실험을 해본 후 올린 질문이 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;&lt;a href=&quot;https://stackoverflow.com/questions/67034861/why-would-concurrency-using-stdasync-be-faster-than-using-stdthread&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://stackoverflow.com/questions/67034861/why-would-concurrency-using-stdasync-be-faster-than-using-stdthread&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1720795752004&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Why would concurrency using std::async be faster than using std::thread?&quot; data-og-description=&quot;I was reading Chapter 8 of the &amp;quot;Modern C++ Programming Cookbook, 2nd edition&amp;quot; on concurrency and stumbled upon something that puzzles me. The author implements different versions of paral...&quot; data-og-host=&quot;stackoverflow.com&quot; data-og-source-url=&quot;https://stackoverflow.com/questions/67034861/why-would-concurrency-using-stdasync-be-faster-than-using-stdthread&quot; data-og-url=&quot;https://stackoverflow.com/questions/67034861/why-would-concurrency-using-stdasync-be-faster-than-using-stdthread&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/sCEZK/hyWzzjUD3c/iwQnlNxtMfuMn0pHHDhzJk/img.png?width=316&amp;amp;height=316&amp;amp;face=0_0_316_316&quot;&gt;&lt;a href=&quot;https://stackoverflow.com/questions/67034861/why-would-concurrency-using-stdasync-be-faster-than-using-stdthread&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://stackoverflow.com/questions/67034861/why-would-concurrency-using-stdasync-be-faster-than-using-stdthread&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/sCEZK/hyWzzjUD3c/iwQnlNxtMfuMn0pHHDhzJk/img.png?width=316&amp;amp;height=316&amp;amp;face=0_0_316_316');&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;Why would concurrency using std::async be faster than using std::thread?&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;I was reading Chapter 8 of the &quot;Modern C++ Programming Cookbook, 2nd edition&quot; on concurrency and stumbled upon something that puzzles me. The author implements different versions of paral...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;stackoverflow.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 질문에 대한 답변의 요약은 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;nbsp;현재&amp;nbsp;libstdc++&amp;nbsp;구현은&amp;nbsp;std::async는&amp;nbsp;각각의&amp;nbsp;task에&amp;nbsp;새로운&amp;nbsp;스레드가&amp;nbsp;필요하지&amp;nbsp;않다는&amp;nbsp;사실의&amp;nbsp;이점을&amp;nbsp;취하지&amp;nbsp;않습니다. &lt;br /&gt;-&amp;nbsp;현재&amp;nbsp;libstdc++&amp;nbsp;구현은&amp;nbsp;std::async는&amp;nbsp;std::thread가&amp;nbsp;수행하지&amp;nbsp;않는&amp;nbsp;일종의&amp;nbsp;잠금(lock)을&amp;nbsp;수행합니다. &lt;br /&gt;-&amp;nbsp;std::async와&amp;nbsp;std::launch::deferred는&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;답변자의 말에 따르면 std::async는 매번 호출될 때마다 새로운 스레드를 생성하고, 오히려 더 많은 스레드를 생성한다는 것이다. 만약 정말 그렇다면 비동기 호출의 결과를 받아온다는 점 말고는 std::thread를 사용하는 편이 더 효율적이라는 것이다.&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;인터넷에 널려있는 블로그를 보면 스레드를 풀링한다고 하는데 맞을 수도 있고 틀릴수도 있다. 결국 STL의 표준은 호출 인터페이스와 같은 것만 정해져 있기 때문에 내부 구현은 GNU나 Microsoft, Apple각각 모두 다를 수 있다. 그러므로 std::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;std::async를 사용하는것은 이러한 점을 숙지하고 프로젝트에 알맞은 방향으로 사용하도록 하자.&lt;/p&gt;</description>
      <category>C++</category>
      <category>asynchronous</category>
      <category>C++</category>
      <category>thread</category>
      <author>index1207</author>
      <guid isPermaLink="true">https://index1207.tistory.com/14</guid>
      <comments>https://index1207.tistory.com/14#entry14comment</comments>
      <pubDate>Sat, 13 Jul 2024 00:28:08 +0900</pubDate>
    </item>
    <item>
      <title>[C++] C++에서 라이브러리를 관리해보자</title>
      <link>https://index1207.tistory.com/13</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;C++ 라이브러리 시스템&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;C++은 다른 언어들과 다르게 라이브러리가 소스파일이 아니라 라이브러리 바이너리와 헤더로 나뉘어져 있고,&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;그로 인해 새로운 라이브러리를 사용할 때마다 따로 컴파일, include 경로, 라이브러리 링크 등을 설정해주어야 한다.&lt;/p&gt;
&lt;h3 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;내가 사용한 기존의 방법&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;git에서 submodule로 라이브러리를 추가하고, 배치파일 스크립트를 통해 CMake로 각각을 빌드하고 특정 폴더에 라이브러리 바이너리를 복사했다. 그 후 비주얼 스튜디오 프로젝트 설정에서 라이브러리를 등록해 사용했다.&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;비교적 최신에 개발된 언어들에는 패키지 매니저가 있어 커맨드라인이나 세팅 파일을 통해 모듈이나 라이브러리 등을 쉽게 추가할 수 있다. 예를 들어 python에서 stringcase라는 모듈을 추가하고 싶다면 pip install stringcase라는 하나의 커맨드라인으로 추가할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C++도 라이브러리 문제를 해결하기 위해 패키지매니저가 개발되었다. 대표적으로는 vcpkg와 conan이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글에선 가장 많이 쓰이고 있는 vcpkg를 중점적으로 알아볼 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;vcpkg는 비주얼 스튜디오와 통합하여 사용할 수 있는 패키지 매니저로, vcpkg로 다운받은 라이브러리를 바로 비주얼 스튜디오에서 바로 사용 가능하다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;자동화된 프로젝트 구성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 원하는 것은 전역 로컬에서만 관리되는 것이 아니라 다른 사람들과 공유되도록 프로젝트에 설치해 사용하는 라이브러리 관리이다. 배치파일로 vcpkg를 통해 필요한 라이브러리를 전역으로 다운하도록 하는것도 좋지만, 가장 명확한 방법으로는 프로젝트에 라이브러리를 설치하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;vcpkg는 'manifest 모드'라는 것을 지원한다. manifest모드는 vcpkg.json이라는 파일을 통해 사용할 라이브러리를 지정해 프로젝트에 설치할 수 있도록 해주는 기능이다. 이 기능을 사용한다면 다른 사람들의 환경의 차이를 어느정도 해결할 수 있고, 비주얼 스튜디오나 CMake에서도 vcpkg를 지원하기 때문에 CI 스크립트를 작성할 때도 용이하다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;끝으로&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수동으로 라이브러리를 설치하다가 자동화된 툴을 사용해보니 정말 편한것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글을 보신다면 vcpkg로 프로젝트를 구성해보는것을 추천한다.&lt;/p&gt;</description>
      <category>C++</category>
      <category>C++</category>
      <category>Conan</category>
      <category>manifest</category>
      <category>vcpkg</category>
      <author>index1207</author>
      <guid isPermaLink="true">https://index1207.tistory.com/13</guid>
      <comments>https://index1207.tistory.com/13#entry13comment</comments>
      <pubDate>Thu, 27 Jun 2024 10:43:25 +0900</pubDate>
    </item>
    <item>
      <title>[C++/Unreal] 커스텀 RPC 구현 기록</title>
      <link>https://index1207.tistory.com/12</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;개발 배경&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MMORPG 팀 프로젝트 기획 중 서버에서 혼자 구현하지 못할 만큼의 컨텐츠를 기획하게 되었다...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 상황을 해결하기 위해 고민하던 중 포톤에서 사용하던 RPC방식이 떠올렸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RPC는 네트워크에서 복제된 객체에 대해 원격으로 함수를 호출해 서버에서 따로 로직을 구현하지 않고 클라이언트에서&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;RPC를 구현하기 위해선 다음 정보를 직렬화해야 한다.&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;li&gt;RPC 호출자 아이디&lt;/li&gt;
&lt;li&gt;메서드 인수 목록&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트에서 RPC를 호출하면 데이터를 직렬화시켜 서버에게 보내고, 서버는 타깃에게 이 직렬화 데이터를 브로드캐스팅 하게 된다. 그 후 클라이언트에서 RPC 데이터를 받게 된다면 메서드 식별 아이디와 메서드 인수 목록을 역직렬화 시켜 호출하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;호출자 아이디가 자신이라면 내가 호출한 RPC이고, 아니라면 남이 호출한 RPC임을 알 수 있다.&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;첫번째로, 메서드 식별용 아이디를 만들어 보자. 나는 RPC 식별 아이디를 정수형 15자리의 비트로 만들어 줬다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜냐하면 RPC와 메세지 시스템을 같이 사용할 예정이기 때문에 상위 1비트를 통해 RPC인지, 메세지인지 판별하기 위함이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 매크로를 사용해 메서드에 RPC아이디를 부여해줄 것이다. 아래 매크로는 메서드 이름을 받아와&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;헬퍼 메서드를 만들어준다. 헬퍼 메서드는 메서드를 RpcView클래스에 등록하고 메서드 식별 아이디를 리턴한다.&lt;/p&gt;
&lt;pre id=&quot;code_1717136351618&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#define THIS_CLASS std::remove_pointer&amp;lt;decltype(this)&amp;gt;::type /* 내 클래스 타입 얻어오기 */
#define RPC(Func) _RPC##Func() /* 헬퍼 메서드 */
#define RPC_FUNCTION(func) uint16 RPC(Func)\
						   {\
							   static auto RpcId = RpcView::Register(&amp;amp;THIS_CLASS::func);\
							   return RpcId;\
						   }\&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;RpcView클래스는 RPC 메서드를 관리하는 정적 클래스이다. Register를 호출하면 메서드가 RPC로 등록이 되고, 메서드 식별 아이디가 리턴된다.&lt;/p&gt;
&lt;pre id=&quot;code_1717137738285&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;template&amp;lt;class T, class... Args&amp;gt;
static uint16 Register(void(T::*RpcFunc)(Args...))
{
    auto RpcObject = new Rpc&amp;lt;T, Args...&amp;gt;(RpcFunc, ++RpcId); // 새로운 Rpc 객체 생성
    RpcInterface Interface // 템플릿 타입 단순화용 객체
    {
        .Owner = RpcObject, // 템플릿 객체를 담기 위한 void*
        .Execute = std::bind(&amp;amp;Rpc&amp;lt;T, Args...&amp;gt;::Execute, RpcObject, std::placeholders::_1)
    };

    RpcFuncTable.Add(RpcId, Interface); // Rpc 함수 등록
    return RpcId; // RPC 아이디 리턴
}&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;그 다음으로 메서드 아이디, 브로드캐스팅 타입, RPC 호출자, 그리고 인수 목록을 캡슐화 시킨 클래스인 Rpc클래스를 구현했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 클래스는 템플릿으로 구현되어 있고, 주 역할은 인수를 직렬/역직렬화 시키고 메서드를 호출하는 역할을 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Register를 호출될 때 Rpc객체가 생성되고, RpcView는 이 Rpc객체를 저장하고 있게 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1717137930749&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;template&amp;lt;class T, class... Args&amp;gt; requires std::is_base_of_v&amp;lt;NetObject, T&amp;gt; // C++20's Concept
class Rpc : Packet // 직렬화 &amp;amp; 메세지 전송가능 클래스
{
public:
	Rpc(void(T::*RpcFunc)(Args...), uint16 Id) : Packet(Id, RPC), RpcFunc(RpcFunc)
	{
	}
public:
	void Execute(std::span&amp;lt;char&amp;gt; Buffer) // RPC 수신 후 호출
	{
    	...
	}
	void WriteParameter(Args... ArgsList)
	{
		*this &amp;lt;&amp;lt; static_cast&amp;lt;uint8&amp;gt;(Target) &amp;lt;&amp;lt; CallerId; // Target, CallerId 직렬화
		((*this &amp;lt;&amp;lt; ArgsList), ...); // 인수 목록 직렬화
	}
	void ReadParameter(Args&amp;amp;... ArgsList)
	{
    	*this &amp;gt;&amp;gt; reinterpret_cast&amp;lt;uint8&amp;amp;&amp;gt;(Target) &amp;gt;&amp;gt; CallerId;
		((*this &amp;gt;&amp;gt; ArgsList), ...); // 인수 목록 역직렬화
		Reset();
	}
public:
    void SetTarget(RpcTarget NotifyTarget) { ... }
	void SetBuffer(std::span&amp;lt;char&amp;gt; Buffer) { ... }
	void SetCaller(uint16 Id) { CallerId = Id; }
private:
	uint64 CallerId; // 호출자 아이디
	RpcTarget Target; // 타깃
	void(T::*RpcFunc)(Args...); // 메서드 포인터
};&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;마지막으로 RPC를 호출하는 과정이다.&lt;/p&gt;
&lt;pre id=&quot;code_1717138691782&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;template&amp;lt;class T, class... Args&amp;gt;
static void Execute(void(T::*)(Args...), T* Owner, uint16 Id, RpcTarget Target, Args... ArgsList)
{
    auto RpcObj = static_cast&amp;lt;Rpc&amp;lt;T, Args...&amp;gt;*&amp;gt;(RpcFuncTable[Id].Owner);
    RpcObj-&amp;gt;SetTarget(Target);
    RpcObj-&amp;gt;SetCaller(Owner-&amp;gt;GetId());
    RpcObj-&amp;gt;WriteParameter(Forward&amp;lt;Args&amp;gt;(ArgsList)...);
    SendRpc(reinterpret_cast&amp;lt;Packet*&amp;gt;(RpcObj));
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1717138731286&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#define CallRPC(Func, Target, ...) Execute(&amp;amp;THIS_CLASS::Func, this, RPC(Func), Target, __VA_ARGS__);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RpcView::CallRPC(메서드, 타깃, 인수 목록...)을 통해 호출하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 Execute메서드를 보면, 첫번째 인수로 메서드를 받고, 다음으로 소유자, 메서드 아이디, 타깃, 인수 목록을 받는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫번째 인수인 메서드는 꺾쇠(&amp;lt;,&amp;gt;)없이 템플릿 타입을 결정하기 위한용도이고, 3번째로 받는 아이디를 통해 RPC 데이터를 보내게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RpcView::Execute의 인수는 너무 길기 때문에 단축하기 위해 CallRPC라는 매크로를 만들었다.&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;h4 data-ke-size=&quot;size20&quot;&gt;마지막으로&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최종적으로 구현한 RPC의 예제를 보면 다음과 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1717139475114&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// PlayerCharacter.h

...
	void CallRpcTest();
    
	RPC_FUNCTION(TestRPC)
    void TestRPC(int Data);
...

-------------------------------------------------------------------------------------

// PlayerCharacter.cpp

...
APlayerCharacter::APlayerCharacter()
{
	...
    RPC(TestRPC);
}

void APlayerCharacter::CallRpcTest()
{
	RpcView::CallRPC(TestRPC, RpcTarget::All, 100);
}

void APlayerCharacter::TestRPC(int Data)
{
	GEngine-&amp;gt;AddOnScreenDebugMessage(-1, 5, FColor::White, FString::FromInt(Data));
}
...&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;BeginPlay이후에 CallRpcTest를 호출하면 TestRPC가 호출되는 것을 볼 수 있다.&lt;/p&gt;</description>
      <category>C++</category>
      <category>C++</category>
      <category>Network</category>
      <category>RPC</category>
      <author>index1207</author>
      <guid isPermaLink="true">https://index1207.tistory.com/12</guid>
      <comments>https://index1207.tistory.com/12#entry12comment</comments>
      <pubDate>Fri, 31 May 2024 16:13:00 +0900</pubDate>
    </item>
    <item>
      <title>[기타] 메모리 관리에 대하여</title>
      <link>https://index1207.tistory.com/11</link>
      <description>&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;이 글에선 메모리를 관리하는 시스템 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;&lt;b&gt;1. 참조 카운팅(Reference Counting)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방법은 주로 C++에서 사용되는 방법으로, 객체를 참조하는 카운팅을 통해 객체의 할당 해제 시점을 결정하는 방식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C++에서 '스마트 포인터'를 사용해 카운팅 한다. 객체를 &lt;b&gt;참조&lt;/b&gt;하면 카운트가 1이 늘어나고, 참조를 하는 객체가 &lt;b&gt;소멸&lt;/b&gt;된다면 카운트가 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;size18&quot;&gt;&lt;b&gt;장점&lt;/b&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;size18&quot;&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;&lt;b&gt;순환 참조&lt;/b&gt;: A라는 객체에선 B를 참조하고, B라는 객체에선 A를 참조하고 있을 때, 서로의 참조 카운트가 1이 되므로 영원히 해제가 되지 않는 문제가 발생한다. 물론 약한 참조라는 해결법도 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;성능 오버헤드&lt;/b&gt;: 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;&lt;b&gt;2. GC(Garbage Collection)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방법은 Java와 C#에서 사용되는 방법으로, Java로 개발한 프로그램을 작동시키면 &lt;b&gt;자바 가상 머신(JVM)&lt;/b&gt;에서 Garbage Collector가 알아서 불필요한 메모리를 해제하는 시스템이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GC가 있는 언어라면 메모리의 할당과 해제는 개발자가 직접 관리하지 않아도 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;장점&lt;/b&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;size18&quot;&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;&lt;b&gt;성능 오버헤드&lt;/b&gt;: Java에서 GC가 작동할 때 다른 동작이 중지됨과, 프로그램 이외로 GC가 작동해 CPU를 점유함으로 인해 성능의 오버헤드가 발생한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;메모리 오버헤드&lt;/b&gt;: GC의 메모리 관리 알고리즘으로 인해 프로그래머가 의도한 메모리보다 더 많은 메모리가 과다 할당되는 메모리 오버헤드가 발생한다.&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;b&gt;3. 소유권(Ownership)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방법은 Rust가 사용하는 &lt;b&gt;제3의 방식&lt;/b&gt;이다. 컴파일 타임에 '소유권 규칙'에 따라 문법을 체크하는 방식을 사용해 메모리가 유효한지 체크하는 방식이다. (C++로 치자면 모든 객체가 unique_ptr인 셈)&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;소유권 규칙&lt;/b&gt;&lt;br /&gt;- 각각의 '값'은 '오너(owner)'라고 불리는 변수를 갖고 있다.&lt;br /&gt;- 하나의 '값'에 대해서는 하나의 오너만 존재할 수 있다.&lt;br /&gt;- '오너'가 스코프 밖에 나갔을 때 '값'은 버려진다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;장점&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴파일 타임에 체크하므로, 런타임에 체크하는 GC방식에 비해 성능이 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;단점&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'소유권 규칙'으로 인해 문법 검사가 깐깐하게 진행된다.&lt;/p&gt;</description>
      <category>잡다지식</category>
      <category>C++</category>
      <category>Java</category>
      <category>memory</category>
      <category>rust</category>
      <author>index1207</author>
      <guid isPermaLink="true">https://index1207.tistory.com/11</guid>
      <comments>https://index1207.tistory.com/11#entry11comment</comments>
      <pubDate>Fri, 31 May 2024 12:28:37 +0900</pubDate>
    </item>
    <item>
      <title>[C++] 메모리 풀에서 new 무한 호출</title>
      <link>https://index1207.tistory.com/10</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;나는 메모리 풀을 구현할 때 다음과 같이 구현했다.&lt;/p&gt;
&lt;pre id=&quot;code_1714699788562&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static T* Pop()
{
	T* ptr = nullptr;
    if (!m_pool.empty())
    	ptr = m_pool.pop();
    else
    	ptr = new T;
    return ptr;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 Pop을 호출하는 순간 엄청난 new의 콜스택이 쌓여 &lt;span style=&quot;color: #f89009;&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;아무리 생각해도 new가 new를 호출할 일이 뭐가 있지 싶었지만, 얼마 지나지 않아 이유를 찾았다.&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;클래스에서&amp;nbsp;매크로를 사용해 new를 오버로딩하게 만들어 new를 호출할 때 오브젝트 풀에서 Pop을 호출하게 하는 식으로 구현했다.&lt;/p&gt;
&lt;pre id=&quot;code_1714700118584&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ptr = new T;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 이 코드에서 또 new를 호출하고 Pop을 하게 만드는 순환이 발생하게 된 것이였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 해결하려면 new 대신 크기로 메모리를 할당하는 전역 new나 malloc을 사용해주면 된다.&lt;/p&gt;</description>
      <category>C++/트러블슈팅</category>
      <category>C++</category>
      <category>트러블슈팅</category>
      <author>index1207</author>
      <guid isPermaLink="true">https://index1207.tistory.com/10</guid>
      <comments>https://index1207.tistory.com/10#entry10comment</comments>
      <pubDate>Fri, 3 May 2024 10:40:08 +0900</pubDate>
    </item>
  </channel>
</rss>