<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>cplusplus: 실전적 클래스 설계와 구현</title>
    <link>https://cplusplus.tistory.com/</link>
    <description>이론보다는 실무에서 클래스 설계와 구현을 다룹니다.</description>
    <language>ko</language>
    <pubDate>Thu, 9 Jul 2026 10:13:24 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>tys</managingEditor>
    <image>
      <title>cplusplus: 실전적 클래스 설계와 구현</title>
      <url>https://tistory1.daumcdn.net/tistory/4613682/attach/b68e9bd8e9e8465fbc387ef3939e22f1</url>
      <link>https://cplusplus.tistory.com</link>
    </image>
    <item>
      <title>On Golang: 구글이 만들면 다릅니다. - Things Are Different When Google Makes It -</title>
      <link>https://cplusplus.tistory.com/entry/On-Golang-Things-Are-Different-When-Google-Makes-It</link>
      <description>&lt;blockquote data-end=&quot;301&quot; data-start=&quot;243&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-end=&quot;301&quot; data-start=&quot;245&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR'; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;On Golang: Things Are Different When Google Makes It&lt;br /&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1537&quot; data-origin-height=&quot;1023&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qpEXS/dJMcaffv6Mq/34KhlPGYWcjUhtnIaPyR4k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qpEXS/dJMcaffv6Mq/34KhlPGYWcjUhtnIaPyR4k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qpEXS/dJMcaffv6Mq/34KhlPGYWcjUhtnIaPyR4k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqpEXS%2FdJMcaffv6Mq%2F34KhlPGYWcjUhtnIaPyR4k%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;1537&quot; height=&quot;1023&quot; data-origin-width=&quot;1537&quot; data-origin-height=&quot;1023&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Go는 만능이 아닙니다.&lt;/span&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&quot;Go는 백엔드 개발의 과도한 복잡성을 줄이는 선택지&quot;를 '복잡성이 사라진다' 라고 &quot;&lt;b&gt;잘못&quot;&lt;/b&gt;&amp;nbsp;받아들이는 경우가 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;복잡성 보존 법칙&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;a style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; href=&quot;https://en.wikipedia.org/wiki/Law_of_conservation_of_complexity&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-darkreader-inline-color=&quot;&quot;&gt;https://en.wikipedia.org/wiki/Law_of_conservation_of_complexity [복잡성 보존의 법칙]&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;a style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; href=&quot;https://pi-store.tistory.com/229&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-darkreader-inline-color=&quot;&quot;&gt;https://pi-store.tistory.com/229 [테슬러의 법칙: 복잡성은 사라지지 않는다]&lt;/a&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; color: #000000;&quot;&gt;복잡도는 사라진 것이 아니라, 표현 위치가 바뀐 것입니다&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; font-size: 17.92px; text-align: center; --darkreader-inline-color: var(--darkreader-text-333333, #c8c3bc);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;과도한 복잡성이 아니라 &lt;span style=&quot;text-align: start;&quot;&gt;현실&lt;/span&gt; 비즈니스 복잡성을 표현하기 어렵다는 것이 더 적절합니다. 따라서 Go언어는&amp;nbsp;&lt;span style=&quot;text-align: start; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;복잡도를 없애는 게 아니라 프로세스, 패키지, DB, 외부 서비스 경계로 밀어내는 것입니다.&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;&lt;span style=&quot;color: #000000; letter-spacing: 0px; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;다시 말해 Go는 레이어가 얇은 요청-응답 서비스에 잘 어울리는 언어입니다.&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;color: #000000; letter-spacing: 0px; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;복잡한 도메인은 다른 언어로 구현된 모듈, 별도 프로세스, DB, 외부 서비스 등 명확한 경계 안으로 두고, Go 프로세스는 네트워크 I/O, RPC, 요청-응답, 데이터 전달, orchestration 계층을 담당하게 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; letter-spacing: 0px; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; letter-spacing: 0px; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;--darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Go언어는 다음과 같은것은 잘 표현하기 어렵습니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt; 강한 lifecycle 불변식, 계층적 책임 분리, 여러 비즈니스 규칙과 상태 전이가 중심이 되는 상위 계층&lt;/span&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;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;letter-spacing: 0px; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;--darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;이러한 것을 표현하려면&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #fcfcfc; letter-spacing: 0px; --darkreader-inline-bgcolor: var(--darkreader-background-fcfcfc, #1a1c1d); --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-bgcolor=&quot;&quot; data-darkreader-inline-color=&quot;&quot;&gt;Accidental Complexity(&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;우발적 복잡도)&lt;/span&gt;&lt;span style=&quot;--darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;도 비용이 타 언어 보다도 매우 커지는 경향이 있습니다.&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;--darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&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;color: #000000; letter-spacing: 0px; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;--darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://corrode.dev/learn/migration-guides/go-to-rust/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span style=&quot;letter-spacing: 0px; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;--darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;--darkreader-inline-color: var(--darkreader-text-c8c3bc, #c1bcb4);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&quot;Go에서 Rust로 마이그레이션&quot; 에서 발췌&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; color: #000000;&quot;&gt;성공적인 언어이긴 하지만,&amp;nbsp;설계가 잘못 되었다고 생각합니다. Go는 사용&amp;nbsp;&lt;b&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://www.youtube.com/watch?v=SxdOUGdseq4&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer external&quot;&gt;편의성&lt;/a&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://www.youtube.com/watch?v=SxdOUGdseq4&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer external&quot;&gt;&amp;nbsp;과&amp;nbsp;&lt;/a&gt;&lt;/b&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://www.youtube.com/watch?v=SxdOUGdseq4&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer external&quot;&gt;&lt;b&gt;단순함&lt;/b&gt;&lt;/a&gt;을혼동하고 있으며 , 핵심적인 설계상의 몇 가지 문제점(&amp;nbsp;nil모든 곳에 존재하는 오류 처리 방식, 타입이 아닌 규칙으로 처리되는 오류 처리 방식, 오랫동안 제네릭이 없었던 점 등)은 제가 동의하지 않는 방향으로 나아가고 있습니다.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&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;&lt;span style=&quot;color: #000000; text-align: left; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;microsoft::os::windows11의 표현&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: left; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;예를 들어 microsoft::os::windows11 이런 표현이 불가능하기 때문에 아래처럼 하나의 레이어로 평탄화해 표현해야 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;microsoft::os_windows11&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;microsoft::office_excel&lt;br /&gt;&lt;span style=&quot;text-align: start;&quot;&gt;(여기서 OS 벤더가 microsoft만 있는 게 아니겠죠?)&amp;nbsp;&lt;/span&gt;&lt;/span&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;위처럼 예제처럼 층을 두고 만들지 않는다면, &lt;/span&gt;코드도 물리적으로 동일 위치에 존재해야 합니다. OS와 Office처럼 논리적으로 다른 영역도 같은 디렉토리에 강제 배치됩니다. 이런 경우 물리 구조 vs 논리 표현이 항상 어긋납니다.&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;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;패키지를 층을 두고 만든다 하더라도 즉, import path에는 계층을 허용하지만, 코드 사용부에서는 그 계층을 보존하지 못합니다. 이런 표현력의 한계는 &lt;span style=&quot;text-align: start;&quot;&gt;현실&lt;/span&gt;&amp;nbsp;비즈니스 모델링의 유지보수에 있어서 매우 치명적인 단점입니다.&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;color: #000000;&quot;&gt;비즈니스 표현&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Go는 문법을 단순하게 만들어 가독성을 높였다고 주장하지만, 실제로는 언어가 복잡성을 처리해주지 않기 때문에 그 복잡성이 개발자의 코드로 고스란히 넘어옵니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start; --darkreader-inline-color: var(--darkreader-text-333333, #c8c3bc);&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start; --darkreader-inline-color: var(--darkreader-text-333333, #c8c3bc);&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;따라서 현실 비즈니스를 Go로 표현한다면 코드는 읽기 쉬울지 몰라도, 시스템이나 어플리케이션 구조는 훨씬 더 이해하기 어렵고 복잡해진다고 말할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start; --darkreader-inline-color: var(--darkreader-text-333333, #c8c3bc);&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start; --darkreader-inline-color: var(--darkreader-text-333333, #c8c3bc);&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;인프라 도구는 상태 전이나 비즈니스 규칙이 복잡하지 않고 입출력(I/O) 성능이 중요하지만, 이를 현실 비즈니스 도메인에 대입한다면 여러 문법적 제약 때문에 아키텍처가 비정상적으로 평탄화되거나 왜곡되어 집니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start; --darkreader-inline-color: var(--darkreader-text-333333, #c8c3bc);&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://corrode.dev/learn/migration-guides/go-to-rust/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Go에서 Rust로 전환을 고려하는 팀들의 이유&lt;/a&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p style=&quot;color: #333333; text-align: start; --darkreader-inline-color: var(--darkreader-text-333333, #c8c3bc);&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Google과 Go&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;Go가 복잡한 도메인 표현에 큰 비용이 발생하는 언어적 한계를 갖고 있음에도, &quot;&lt;b&gt;&lt;span style=&quot;text-align: start;&quot;&gt;Google 이라는 이미지로&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&lt;b&gt; 그 한계가 마치 복잡도를 없애주는 것 마냥 포장되고, 다층 모델조차 단일 계층으로 몰아넣는 것이 설계 철학처럼 미화, &lt;span style=&quot;text-align: start;&quot;&gt;왜곡&lt;/span&gt;되어 소비되는 것&quot;&lt;/b&gt;은 큰 문제라고 볼 수 있습니다. Go를 비판하는 많은 개발자들도 이 부분을 지적하는 것이겠죠.&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;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;(Google 브랜드 이미지는 비판마저 불편하게 만듭니다.)&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;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;누군가는 이렇게 지적합니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&quot;Worse is Better&quot;(더 나쁜 것이 더 낫다-사용하는 개발자 입장에서 더 낫다는 뜻이 아님) 철학이 낳은 부작용&lt;br /&gt;&lt;br /&gt;단순함을 키워드와 문법의 개수 위주로 측정했기 때문에, 정작 도구가 해결해야 할 Accidental Complexity(우리가 쓰는 도구/언어/환경 때문에 추가로 생긴 것)가 애플리케이션 코드 베이스 전반에 흩어지게 되어 있습니다. 표면적인 학습은 쉽지만&amp;nbsp; 안전한 소프트웨어를 구축하기 위한 난이도는 결코 낮지 않게 설계된 형태&lt;/span&gt;&lt;/blockquote&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;[언어의 단순함을 얻는 대신, 그로 인해 발생하는 구조적 복잡성과&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&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;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;--darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Go는 도메인 설계보다는 클라우드 인프라나 대규모 트래픽 처리에 집중하느라 그런 유연성을 포기한 측면이 크다고 볼 수 있습니다. 다시 말해&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;--darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;범용 언어가 아닌 클라우드 인프라와 대규모 트래픽 처리에 특화된 언어라고 보는 것이 더 타당합니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;모든 도구가 그러듯이 적절한 위치를 알고 사용해야 합니다.&lt;br /&gt;당신의 조직은 나사를 망치로 때리고 있지는 않습니까?&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Backend</category>
      <category>go</category>
      <category>Golang</category>
      <category>백엔드</category>
      <category>철학</category>
      <author>tys</author>
      <guid isPermaLink="true">https://cplusplus.tistory.com/36</guid>
      <comments>https://cplusplus.tistory.com/entry/On-Golang-Things-Are-Different-When-Google-Makes-It#entry36comment</comments>
      <pubDate>Sat, 9 May 2026 18:39:49 +0900</pubDate>
    </item>
    <item>
      <title>Zero-cost 추상화: C++17 if constexpr을 활용한 lock guard</title>
      <link>https://cplusplus.tistory.com/entry/%EA%B0%84%EB%8B%A8%ED%95%9C-Zero-cost-%EC%B6%94%EC%83%81%ED%99%94-C17-constexpr%EC%9D%84-%ED%99%9C%EC%9A%A9%ED%95%9C-lock-guard</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000; text-align: left;&quot; data-darkreader-inline-color=&quot;&quot;&gt;요약&lt;/span&gt;&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;TPSMeter에서&amp;nbsp;락&amp;nbsp;사용&amp;nbsp;여부를&amp;nbsp;런타임&amp;nbsp;bool로&amp;nbsp;판단하던&amp;nbsp;걸&amp;nbsp;&amp;rarr;&amp;nbsp;Non-type&amp;nbsp;template&amp;nbsp;parameter로&amp;nbsp;바꿔서&amp;nbsp;컴파일&amp;nbsp;타임에&amp;nbsp;결정되게&amp;nbsp;함&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;if&amp;nbsp;constexpr로&amp;nbsp;분기해서&amp;nbsp;런타임&amp;nbsp;오버헤드를&amp;nbsp;완전히&amp;nbsp;제거&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;근데 중복 코드가 생기니까 &amp;rarr; constexpr_lock_guard를 11줄로 만들어서 중복 제거&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;std::monostate&amp;nbsp;+&amp;nbsp;RVO&amp;nbsp;+&amp;nbsp;타입&amp;nbsp;추론&amp;nbsp;활용한&amp;nbsp;제로&amp;nbsp;코스트&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;공수와&amp;nbsp;오용(misuse)을&amp;nbsp;줄이고&amp;nbsp;가독성을&amp;nbsp;높이면서도&amp;nbsp;기존&amp;nbsp;방식&amp;nbsp;대비&amp;nbsp;약&amp;nbsp;5.1배의&amp;nbsp;성능향상&lt;/span&gt;&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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;시작&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;C++17에 도입된 if constexpr은 기존의 const 보다 더 나은 퍼포먼스를 제공합니다. constexpr은 컴파일 타임에 평가를 보장합니다. 따라서 constexpr로 사용된 if문으로 분기되는 런타임 오버헤드를 없앨 수 있습니다.&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;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;다음과 같이 매우 빈번하게 사용되는 TPSMeter을 대상으로 할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1773818226260&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;size_t
TPSMeter::add_transaction(size_t size = 1)
{
  SpinLockGuard guard(lock_, use_lock_);
  for (size_t index = 0; index &amp;lt; size; ++index)
    transactions_.emplace_back(std::chrono::steady_clock::now());
  return get_tps_nolock();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;lt;&amp;lt; 이전 포스트의 내용 중... &amp;gt;&amp;gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; 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: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;위 코드는 SpinLockGuard를 만들어 use_lock을 이용해 락을 사용할지 여부를 판단하고 있습니다. 이런 경우 런타임시 매번 호출되어 런타임 오버헤드를 갖게 됩니다. 아주 빈번한 호출일 텐데 이런 오버헤드가 쌓이면 큰 오버헤드로 이어집니다.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;그래서 제가 하고 싶은 것은 사소한(?) 오버헤드지만 매우 빈번한 오버헤드를 최대한 줄이고 싶은 것입니다.&lt;/span&gt;&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;pre id=&quot;code_1773851756886&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class SpinLockGuard
{
public:
  explicit SpinLockGuard(SpinLock &amp;amp;lock, bool use_lock = true) // &amp;lt;-여기!
  : spin_(lock), use_lock_(use_lock) { acquire(); }
  
  ~SpinLockGuard() { ... } // &amp;lt;-여기!
  ........
  void acquire()
  {
    if (use_lock_ == false) return; // &amp;lt;-여기!
    if (acquire_  == true ) return;

    acquire_ = true;
    spin_.lock();
  }
  .....&lt;/code&gt;&lt;/pre&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;&lt;span style=&quot;color: #000000; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; font-size: 1.44em; letter-spacing: -1px;&quot; data-darkreader-inline-color=&quot;&quot;&gt;TPSMeter의 if constexpr 사용조건: Non-type template class&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;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;컴파일 타임에 if constexpr이 분기 조건을 알려면 TPSMeter가 락을 사용하는지 안 하는지 컴파일 타임에 알아야 합니다. 컴파일 타임에 알려면 사용자가 직접 사용여부를 기록하게 만들면 되겠죠?&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;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;Non-type template class로 만들면 사용자가 lock 사용 여부를 자연스럽게 기록하도록 합니다. :)&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1773818260213&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;template&amp;lt;bool use_lock_t&amp;gt;
class TPSMeter;

.........

TPSMeter&amp;lt;true&amp;gt; tps_meter_locked;&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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;이제 TPSMeter가 lock을 사용하는지 안 하는지 사용자가 정의해야 하므로 컴파일러는 컴파일 시에 바로 알 수 있게 됩니다!&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;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;여기에 가독성을 높이도록 조금 더 다듬어 볼까요?&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1773857956057&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;template&amp;lt;bool use_lock_t&amp;gt;
class TPSMeterTemplate;

using TPSMeterLocked = TPSMeterTemplate&amp;lt;true&amp;gt;;
using TPSMeter       = TPSMeterTemplate&amp;lt;false&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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;성능을 높이면 서도 동시에 가독성도 높여줍니다. 디자인(API 설계)&lt;span style=&quot;color: #000000; text-align: start;&quot; data-darkreader-inline-color=&quot;&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;if constexpr 사용&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;락 사용여부 조건을 알 수 있으니 if constexpr을 활용하면 다음과 같이 할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: right;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;lt;&amp;lt;실제 코드&amp;gt;&amp;gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1773818333832&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;template&amp;lt;bool use_lock_t&amp;gt; size_t
TPSMeterTemplate&amp;lt;use_lock_t&amp;gt;::add_transaction(size_t size = 1)
{
  if constexpr (use_lock_t == true) 
  {
    std::unique_lock&amp;lt;SpinLock&amp;gt; guard(lock_);
    for (size_t index = 0; index &amp;lt; size; ++index)
      transactions_.emplace_back(std::chrono::steady_clock::now());
    return get_tps_nolock();
  } 
  else
  {
    for (size_t index = 0; index &amp;lt; size; ++index)
      transactions_.emplace_back(std::chrono::steady_clock::now());
    return get_tps_nolock();
  }
}&lt;/code&gt;&lt;/pre&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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;컴파일러는 use_lock_t가 true인 경우 해당 블럭만 컴파일하게 되고 &lt;span style=&quot;letter-spacing: 0px;&quot;&gt;순수하게 락을 사용하는 부분만 적재되어 실행됩니다. false인 경우도 마찬가지겠죠.&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;color: #000000; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot;&gt;이렇게 런타임에서는 분기 없이 바로 실행되어 런타임 오버헤드가 없어집니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: right;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;lt;&amp;lt;분기에 따라 컴파일 되는 코드&amp;gt;&amp;gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1773852134410&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// true시 컴파일 되는 코드
size_t
TPSMeterTemplate&amp;lt;true&amp;gt;::add_transaction(size_t size = 1)
{
  std::unique_lock&amp;lt;SpinLock&amp;gt; guard(lock_);
  for (size_t index = 0; index &amp;lt; size; ++index)
    transactions_.emplace_back(std::chrono::steady_clock::now());
  return get_tps_nolock();
}

// false시 컴파일 되는 코드
size_t
TPSMeterTemplate&amp;lt;false&amp;gt;::add_transaction(size_t size = 1)
{
  for (size_t index = 0; index &amp;lt; size; ++index)
    transactions_.emplace_back(std::chrono::steady_clock::now());
  return get_tps_nolock();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;눈에 거슬리는 중복코드&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;예시로 든 코드는 짧기 때문에 중복코드를 충분히 제어할 수 있다고 생각할 수 있습니다. 그러나 변경사항이 생겨 수정 중에 약간의 실수라도 있다면 전혀 다른 코드가 될 수 있기 때문에 중복코드를 제거하려고 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: GungSeo, serif; color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;좋은 코드는&amp;nbsp;&lt;/span&gt;오용(misuse)할 기회마저 주지 않습니다.&lt;/span&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;먼저 중복코드를 어떻게 없애 볼지 생각해 봅니다. 저는 아래와 같은 사용 방법을 생각했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1773818376670&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;template&amp;lt;bool use_lock_t&amp;gt; size_t 
TPSMeterTemplate&amp;lt;use_lock_t&amp;gt;::add_transaction(size_t size = 1)
{
  auto guard = constexpr_lock_guard&amp;lt;use_lock_t&amp;gt;(lock_);
  for (size_t index = 0; index &amp;lt; size; ++index)
    transactions_.emplace_back(std::chrono::steady_clock::now());
  return get_tps_nolock();
}&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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;현재 조건에서는 제가 사용하기에 충분합니다. 여기서도 역시 RAII를 활용합니다. Non-type template class + if constexpr + RAII 입니다. &lt;span style=&quot;letter-spacing: 0px;&quot;&gt;guard는 add_transaction함수 &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;소멸 시&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&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;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;그래서 constexpr_lock_guard 함수는 아래와 같습니다.&lt;/span&gt;&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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;constexpr_lock_guard&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1773818414062&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;mutex&amp;gt;
#include &amp;lt;variant&amp;gt;

template&amp;lt;bool _use_lock, typename _lock_type&amp;gt; auto
constexpr_lock_guard(_lock_type &amp;amp;lock)
{
  if constexpr (_use_lock)
    return std::unique_lock&amp;lt;_lock_type&amp;gt;(lock);
  else
    return std::monostate{};
}&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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;단 11라인으로 중복 코드를 완전히 없앨 수 있다니. 엄청난 효율성입니다. :)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-start=&quot;0&quot; data-end=&quot;100&quot; data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;return std::unique_lock&amp;lt;_lock_type&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;리턴되면 unique_lock은 복사가 금지되어 있습니다. 여기서는 복사로 리턴되는 것처럼 보이지만 컴파일을 하게 되면 RVO가 적용되어 복사나 move시멘틱 없이 옮겨집니다. 이 또한 거의 제로 코스트입니다.&lt;/span&gt;&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;std::monostate{}&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;아무 의미 없는 값(빈 타입 객체) 하나를 만드는 표현입니다. &quot;내용 없는 값 하나 만들어서 쓰겠다&quot; 이런 의미입니다. 이 객체의 비용 또한 거의 0에 가깝습니다.&lt;/span&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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;함수의 return 타입은 auto 입니다. if constexpr 분기 이므로 컴파일러가 필요에 따라 return 타입을 알아서 맞추어 줍니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-start=&quot;0&quot; data-end=&quot;100&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;letter-spacing: 0px; color: #000000; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot; data-darkreader-inline-color=&quot;&quot;&gt;템플릿 인자는 2개인데 넘긴 인자는 1개?&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1773820953577&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;auto guard = constexpr_lock_guard&amp;lt;use_lock_t&amp;gt;(lock_);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-start=&quot;0&quot; data-end=&quot;100&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-start=&quot;0&quot; data-end=&quot;100&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;C++의 타입 추론(&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;template argument deduction)&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; letter-spacing: 0px;&quot;&gt; 기능 중 하나로 템플릿 함수인 경우 템플릿 타입을 추론하여 줍니다. 따라서 lock_의 타입을 지정하지 않아도 lock_ 타입으로 추론되어 적용됩니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-start=&quot;0&quot; data-end=&quot;100&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-start=&quot;0&quot; data-end=&quot;100&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;이제 쓸 때 없이 일어나는 SpinLockGuard의 생성 소멸은 사라졌습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-start=&quot;0&quot; data-end=&quot;100&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-start=&quot;0&quot; data-end=&quot;100&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;짧은 벤치마크&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;테스트 코드에서는 컴파일러가 루프 자체를 최적화하지 못하도록 DoNotOptimize를 적용하였습니다. -O2 옵션으로 컴파일 후 1억 번 반복 호출로 간단히 벤치마크 해봅니다.&lt;/span&gt;&lt;/p&gt;
&lt;table style=&quot;letter-spacing: 0px; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style11&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;방식&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;호출당 시간&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;차이&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot;&gt;SpinLockGuard(use_lock = false)&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot;&gt;1.18 ns&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;1x&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot;&gt;Direct (guard 없는 기본호출형식)&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot;&gt;0.23 ns&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;5.1x&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;if constexpr(use_lock = false)&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;0.23 ns&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot;&gt;5.1x&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-start=&quot;0&quot; data-end=&quot;100&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-start=&quot;0&quot; data-end=&quot;100&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;약 5.1배 차이입니다. 호출당 ~0.94ns의 차이가 발생합니다. 아 깔끔합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-start=&quot;0&quot; data-end=&quot;100&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-start=&quot;0&quot; data-end=&quot;100&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;끝&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;if constexpr을 사용하면서도 중복코드를 없애므로써 공수는 줄이고 가독성은 높이면서도 오용 가능성은 줄였습니다. 게다가 런타임 오버헤드는 크게 줄였습니다.&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;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;함수라는 형식을 빌려 코드의 가독성과 재사용성을 높였지만, 불필요한 코드를 통째로 날릴 수 있는 정보를 주어 컴파일러가 이를 분석해 실행 시점에는 최적화해 줍니다. 또 내부 로직(락을 걸지 말지 결정하는 조건들)을 감추고 사용자에게 &quot;자원 보호&quot;라는 하나의 일관된 개념만 제공합니다.&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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;추상화는 단순히 어려운 개념이거나 혹은 &quot;쉽게 사용한다.&quot;는 단순한 뜻이 아니라, &quot;사용자가 직관적으로 예상하는 결과를 얻을 수 있도록, 쉬운 사용성을 보장한다.&quot; 는 의미를 내포합니다.&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-start=&quot;0&quot; data-end=&quot;100&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-start=&quot;0&quot; data-end=&quot;100&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-start=&quot;0&quot; data-end=&quot;100&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot;&gt;※ 본 블로그의 소스코드는 자유롭게 사용하셔도 되나, 사용 시 발생하는 모든 문제에 대한 책임은 전적으로 사용자에게 있습니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>C++</category>
      <category>C++17</category>
      <category>constexpr</category>
      <category>Lock</category>
      <category>lock_guard</category>
      <category>Modern</category>
      <category>zero-cost</category>
      <category>제로</category>
      <author>tys</author>
      <guid isPermaLink="true">https://cplusplus.tistory.com/35</guid>
      <comments>https://cplusplus.tistory.com/entry/%EA%B0%84%EB%8B%A8%ED%95%9C-Zero-cost-%EC%B6%94%EC%83%81%ED%99%94-C17-constexpr%EC%9D%84-%ED%99%9C%EC%9A%A9%ED%95%9C-lock-guard#entry35comment</comments>
      <pubDate>Thu, 19 Mar 2026 01:33:41 +0900</pubDate>
    </item>
    <item>
      <title>C++17: 스레드 안전한 자동 플래그: toggler-atomic</title>
      <link>https://cplusplus.tistory.com/entry/C17-%EC%8A%A4%EB%A0%88%EB%93%9C-%EC%95%88%EC%A0%84%ED%95%9C-%EC%9E%90%EB%8F%99-%ED%94%8C%EB%9E%98%EA%B7%B8-toggler-atomic</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;생각보다 현업에서 많이 사소하게 사용하는 게 bool flag입니다.&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;color: #000000;&quot;&gt; 단순한 상태 저장용으로 쓰기 시작하지만 복잡한 루프나 멀티스레드 환경을 만나면 간단했던 플래그가 만만치 않게 됩니다. 또 상태 하나를 체크하기 위해 이전값과 같은지 수동으로 비교하고 값을 업데이트해주어야 합니다.&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;&lt;span style=&quot;color: #000000;&quot;&gt;일반적인 Flag 사용&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1766490381061&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;bool is_active = false; // 이전 상태 저장용

void on_receive_signal(bool status)
{
  // 켜지는 시점 감지: &quot;현재는 true인데, 이전엔 false였나?&quot;
  if (status == true &amp;amp;&amp;amp; is_active == false)
  {
    is_active = true; // 상태 수동 갱신
    std::cout &amp;lt;&amp;lt; &quot;상태 변경: 활성화&quot; &amp;lt;&amp;lt; std::endl;
  }
  // 꺼지는 시점 감지: &quot;현재는 false인데, 이전엔 true였나?&quot;
  else if (status == false &amp;amp;&amp;amp; is_active == true)
  {
    is_active = false; // 상태 수동 갱신
    std::cout &amp;lt;&amp;lt; &quot;상태 변경: 비활성화&quot; &amp;lt;&amp;lt; std::endl;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;1&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;생각보다 현업에서 사소하게 사용되는 것이 bool flag입니다. 단순한 상태 저장용으로 시작하지만, 사소하다는 이유로 버그가 생기면 쉽게 눈에 띄지 않습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;1&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;1&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;플래그 관리 실수로 무한 로그가 발생해 디스크가 가득 차거나, 비즈니스 로직에서 정반대의 결과가 나오기도 합니다. 결국 이전 값을 기억하고 변화 시점을 정확히 따져야 하는 번거로움이 실수를 유발합니다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;1&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;1&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;간단한 인터페이스&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그래서 이렇게 사용하면 쉽겠다는 생각을 해봅니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1766492325931&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;LockedToggler status_checker(false); // 초기값 지정

void on_receive_signal(bool status)
{
  if (status)
  {
    // 켰나? == true, 이미 켜져있나? == false
    if (status_checker.turn_on() == true)
      std::cout &amp;lt;&amp;lt; &quot;[LOG] 장치 활성화&quot; &amp;lt;&amp;lt; std::endl;
  }
  else
  {
    // 꺼진건가? == true, 이미 꺼저있나? == false
    if (status_checker.turn_off() == true)
      std::cout &amp;lt;&amp;lt; &quot;[LOG] 장치 비활성화&quot; &amp;lt;&amp;lt; std::endl;
  }
}&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;&lt;span style=&quot;color: #000000; text-align: start;&quot;&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;&lt;span style=&quot;color: #000000;&quot;&gt;플래그는 대체로 고빈도로 사용하는 경우가 많다. (초당 수백~십수만)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;고빈도 이므로 성능에 미치는 영향을 최소화시켜야 한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&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;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;조건들을 만족시키기 위해서 아래 두가지를 고려해 봅니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1766493092070&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 싱글 스레드 환경 (매우 가볍게 동작)
Toggler normal_status(false);

// 멀티스레드 환경 (가볍지만 스레드 안전하게 동작)
LockedToggler safe_status(false);&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;span style=&quot;color: #000000;&quot;&gt;싱글 스레드 Toggler 구현&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1766493228971&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Toggler
{
public:
   explicit Toggler(bool on = false) : on_(on) {}

  // 이미 켜져 있으면 false, 꺼졌다 켜지면 true
  bool turn_on()
  {
    if (on_ == true)
      return false;

    on_ = true;
    return true;
  }

  // 이미 꺼져 있으면 false, 켜졌다 꺼지면 true
  bool turn_off()
  {
    if (on_ == false)
      return false;

    on_ = false;
    return true;
  }

  bool is_turned_on () const { return on_; }
  bool is_turned_off() const { return on_ == false;}

private:
  bool 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;&lt;span style=&quot;color: #000000;&quot;&gt;여기에 std::mutex만 얹으면 사실상 thread-safe 하게 됩니다. 하지만 호출이 초당 수천~수십만에 이른다면 싱글에서도 불필요한 오버헤드를 발생시켜 성능에 큰 영향일 미칩니다.&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;color: #000000;&quot;&gt;std::atomic&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1766493484766&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;atomic&amp;gt;

template&amp;lt;typename T&amp;gt;
struct atomic;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;CPU 레벨의 하드웨어 명령어 덕분에 std::mutex처럼 운영체제의 커널 스케줄러가 개입하지 않습니다. 따라서 별도의 컨텍스트 스위칭(Context Switching) 비용이 발생하지 않습니다.&lt;/span&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;&lt;span style=&quot;color: #000000;&quot;&gt;std::atomic은 std::mutex보다는 매우 가볍지만 싱글스레드 환경에서는 의미 없이 소모되는 비용이기도 합니다.&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;color: #000000;&quot;&gt;다른 로직 같은 인터페이스 - 비타입 템플릿 인자 활용&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&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;&lt;span style=&quot;color: #000000;&quot;&gt;컴파일 타임에 사용자 선택에 따라 std::atomic 사용여부를 결정짓도록 합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;하나의 클래스로 인터페이스 일관성은 유지시킬 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;사실상 동일한 로직을 하나의 함수에 보이도록 합니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1766493899406&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;template&amp;lt;bool use_lock_t&amp;gt;
class TogglerImpl
{
public:
  // 컴파일 타임에 타입을 결정
  using bool_t = typename std::conditional_t&amp;lt;use_lock_t, std::atomic&amp;lt;bool&amp;gt;, bool&amp;gt;;

  explicit TogglerImpl(bool on = false) : on_(on) {}

  // 이미 켜져 있으면 false, 꺼졌다 켜지면 true, exchange는 이전 값 반환.
  bool turn_on()
  {
    // constexpr을 이용해 불필요한 로직은 컴파일 단계에서 제거
    if constexpr (use_lock_t)
    {
      return on_.exchange(true) == false;
    }
    else
    {
      if (on_ == true)
        return false;

      on_ = true;
      return true;
    }
  }
  ......
private:
  bool_t 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;&lt;span style=&quot;color: #000000; text-align: left;&quot;&gt;TogglerImpl&amp;lt;true&amp;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;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;text-align: left;&quot;&gt;use_lock_t를 true로 매핑되어 아래로 내려가며 적용됩니다.&lt;/span&gt;&lt;span style=&quot;text-align: left;&quot;&gt;&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;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: left;&quot;&gt;using bool_t = typename std::conditional_t&amp;lt;use_lock_t, std::atomic&amp;lt;bool&amp;gt;, bool&amp;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;&lt;span style=&quot;color: #000000;&quot;&gt;std::conditional_t&amp;lt;조건, 조건이_참일_때_타입, 조건이_거짓일_때_타입&amp;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;color: #000000;&quot;&gt;use_bool_t가 std::atomic&amp;lt;bool&amp;gt;이면 bool_t는 std::atomic&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;use_bool_t가 bool이면 bool_t는 bool&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;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;결과적으로 일관성 있는 두 클래스를 만들 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1766498018105&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;template&amp;lt;bool use_lock_t = false&amp;gt;
class TogglerImpl;

using Toggler       = TogglerImpl&amp;lt;false&amp;gt;; // thread-unsafe
using LockedToggler = TogglerImpl&amp;lt;true&amp;gt;;  // thread-safe&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;&lt;span style=&quot;color: #000000;&quot;&gt;코드에서도 보듯이 Toggler와 LockedToggler는 동일한 구현체 이므로 Toggler를 사용하다가 멀티스레드 환경으로 바뀐다면 이름만 LockedToggler로 바꾸어 주면 동일한 동작이 보장됩니다.&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;&lt;span style=&quot;color: #000000;&quot;&gt;이렇게&amp;nbsp;해서&amp;nbsp;제로코스트&amp;nbsp;추상화(Zero-cost&amp;nbsp;Abstraction)를&amp;nbsp;구현해&amp;nbsp;봤습니다.&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;런타임 오버헤드 부재&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt; if constexpr을 사용했기 때문에 컴파일 타임에 조건이 결정됩니다. use_lock_t가 false일 때는 atomic 관련 코드가 완전히 제거되고 일반 bool 연산만 남습니다. true일때는 그 반대입니다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;추가 메모리 비용 없음&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;두 가지 모두를 메모리에 올려 런타임에 분기처리가 되는 것이 아닌 컴파일 타임에 std::conditional_t를 통해 필요한 타입(bool 또는 atomic&amp;lt;bool&amp;gt;)만 선택해서 컴파일합니다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;최적화&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;동일한 동작, 다른 구현을 사용했지만, 컴파일러가 이를 인라인화하여 직접 변수를 제어하는 것과 동일한 수준의 기계어를 생성하여 줍니다.&lt;/span&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;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;구현&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1766497964948&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#pragma once

#include &amp;lt;atomic&amp;gt;

template&amp;lt;bool use_lock_t = false&amp;gt;
class TogglerImpl;

using Toggler       = TogglerImpl&amp;lt;false&amp;gt;; // thread-unsafe
using LockedToggler = TogglerImpl&amp;lt;true&amp;gt;;  // thread-safe

template&amp;lt;bool use_lock_t&amp;gt;
class TogglerImpl
{
public:
  using bool_t = typename std::conditional_t&amp;lt;use_lock_t, std::atomic&amp;lt;bool&amp;gt;, bool&amp;gt;;

  explicit TogglerImpl(bool on = false) : on_(on) {}

  TogglerImpl(const TogglerImpl&amp;amp;) = delete;
  TogglerImpl(TogglerImpl     &amp;amp;&amp;amp;) = delete;
  TogglerImpl &amp;amp;operator=(const TogglerImpl &amp;amp;) = delete;
  TogglerImpl &amp;amp;operator=(TogglerImpl      &amp;amp;&amp;amp;) = delete;

  // 이미 켜져 있으면 false, 꺼졌다 켜지면 true, exchange는 이전 값 반환.
  bool turn_on()
  {
    if constexpr (use_lock_t)
    {
      return on_.exchange(true) == false;
    }
    else
    {
      if (on_ == true)
        return false;

      on_ = true;
      return true;
    }
  }

  // 이미 꺼져 있으면 false, 켜졌다 꺼지면 true
  bool turn_off()
  {
    if constexpr (use_lock_t)
    {
      return on_.exchange(false) == true;
    }
    else
    {
      if (on_ == false)
        return false;

      on_ = false;
      return true;
    }
  }

  bool is_turned_on()  const
  {
    if constexpr (use_lock_t)
      return on_.load();
    else
      return on_;
  }

  bool is_turned_off() const
  {
    if constexpr (use_lock_t)
      return on_.load() == false;
    else
      return on_ == false;
  }

private:
  bool_t 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;&lt;span style=&quot;color: #000000; text-align: start;&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;있습니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>Atomic</category>
      <category>C++</category>
      <category>cpp</category>
      <category>template</category>
      <category>제로</category>
      <category>제로코스트추상화</category>
      <category>추상화</category>
      <category>코스트</category>
      <author>tys</author>
      <guid isPermaLink="true">https://cplusplus.tistory.com/34</guid>
      <comments>https://cplusplus.tistory.com/entry/C17-%EC%8A%A4%EB%A0%88%EB%93%9C-%EC%95%88%EC%A0%84%ED%95%9C-%EC%9E%90%EB%8F%99-%ED%94%8C%EB%9E%98%EA%B7%B8-toggler-atomic#entry34comment</comments>
      <pubDate>Tue, 23 Dec 2025 22:54:24 +0900</pubDate>
    </item>
    <item>
      <title>OOP 언어가 아니다 &amp;ne; 설계 원칙을 안 지켜도 된다</title>
      <link>https://cplusplus.tistory.com/entry/OOP-%EC%96%B8%EC%96%B4%EA%B0%80-%EC%95%84%EB%8B%88%EB%8B%A4-%E2%89%A0-%EC%84%A4%EA%B3%84-%EC%9B%90%EC%B9%99%EC%9D%84-%EC%95%88-%EC%A7%80%EC%BC%9C%EB%8F%84-%EB%90%9C%EB%8B%A4</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;OOP는 객체지향 언어에서 출발한 설계철학이자 개념이겠지만, 십수 년 전부터는 모든 언어뿐 아니라 아키텍처 또한&amp;nbsp;OOP와 함수형 프로그래밍(FP) 등 여러 패러다임에서 공유되는 근본 원칙 [[경계 정의, 복잡도 격리, 명확한 계약]] 을 중심으로&amp;nbsp;발전되어 왔으며 클린아키텍처,&amp;nbsp;DDD, DB설계 등 대부분의 소프트웨어/시스템&amp;nbsp;아키텍처에서&amp;nbsp;절차지향적 언어라도 동일한 개념으로 사용되어 왔습니다.&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;color: #000000; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot;&gt;적용 범위(스케일)만 다르지 근본 원칙은 동일하다고 보는 것입니다.&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;컨테이너 개념 또한 OOP와 유사한 원칙 위에 세워져 있습니다. (MSA 또한 OOP의 SRP를 시스템 레벨로 확장한 것이라 볼 수 있습니다.)&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;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;OOP의 캡슐화, 추상화, 다형성 같은 원칙들은 &quot;레벨&quot;만 다를 뿐 본질은 동일&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;소프트웨어 레벨: 클래스, 모듈&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;시스템 레벨: 컨테이너, MSA, 서비스&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;table style=&quot;border-collapse: collapse; width: 98.7181%; height: 126px;&quot; border=&quot;1&quot; data-ke-style=&quot;style12&quot; data-ke-align=&quot;alignCenter&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 11.8217%; height: 21px;&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;구분&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 42.302%; height: 21px; text-align: center;&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;소프트웨어(OOP)&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 52.969%; height: 21px; text-align: center;&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;시스템(Cloud/MSA)&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 11.8217%; height: 21px;&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;단위&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 42.302%; height: 21px; text-align: center;&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot; data-path-to-node=&quot;5,1,1,0&quot;&gt;클래스, 객체&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 52.969%; height: 21px; text-align: center;&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot; data-path-to-node=&quot;5,1,2,0&quot;&gt;컨테이너, 마이크로서비스&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 11.8217%; height: 21px;&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;캡슐화&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 42.302%; height: 21px; text-align: center;&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot; data-path-to-node=&quot;5,2,1,0&quot;&gt;Private 변수, 내부 로직 숨김&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 52.969%; height: 21px; text-align: center;&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot; data-path-to-node=&quot;5,2,2,0&quot;&gt;컨테이너 내부 구현, DB 격리&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 11.8217%; height: 21px;&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;추상화&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 42.302%; height: 21px; text-align: center;&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot; data-path-to-node=&quot;5,3,1,0&quot;&gt;인터페이스 (Interface)&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 52.969%; height: 21px; text-align: center;&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot; data-path-to-node=&quot;5,3,2,0&quot;&gt;API 명세 (REST, gRPC)&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 11.8217%; height: 21px;&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;다형성&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 42.302%; height: 21px; text-align: center;&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;전략 패턴, 오버라이딩&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 52.969%; height: 21px; text-align: center;&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot; data-path-to-node=&quot;5,4,2,0&quot;&gt;서비스 디스커버리, 로드 밸런스&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 11.8217%; height: 21px;&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;단일책임&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 42.302%; height: 21px; text-align: center;&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;SRP&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 52.969%; height: 21px; text-align: center;&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;마이크로서비스의 도메인 분리&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;color: #333333; text-align: center;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;lt;객체지향 사고의 아키텍처적 확장&amp;gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: center;&quot; data-darkreader-inline-color=&quot;&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; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&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;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;Go나 C, Perl, Lua등이 OOP 언어가 아니라서 &quot;이런 거 몰라도 된다&quot;가 아닙니다.&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;Go로 짜든 C로 짜든 Bash 스크립트로 짜든:&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;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;모듈 간 경계는 명확해야 합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;한 단위가 너무 많은 책임을 지면 안 됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;내부 구현은 숨기고 인터페이스만 노출해야 합니다&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;Go나 C, Perl, Lua&lt;/span&gt; 같은 언어 자체가 클래스 기반의 전통적 OOP 언어가 아니더라도, 그 안에서 구현하는 컴포넌트 간의 결합도를 낮추고 응집도를 높이는 행위는 결국 OOP와 같은 철학을 따르고 있습니다. &lt;span style=&quot;text-align: start;&quot;&gt;몰라도 된다면 Go로 만든 Kubernetes는 왜 그렇게 철저하게 이러한 원칙들을 지키고 있는지 되물어 봐야 합니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: left; --darkreader-inline-color: var(--darkreader-text-333333, #c8c3bc);&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;OOP 철학의 핵심인 캡슐화, 추상화, 다형성은 결국 [[서로 몰라도 되는 것들을 분리]] 하는 작업입니다. 이것은 인간의 인지적 한계가 명확하기 때문에 낮은 수준에서 관리할 수 있도록&amp;nbsp;&lt;span style=&quot;text-align: start;&quot;&gt;[[복잡도 관리]]를 위한 방법론 입니다.&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; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;이러한 설계 원칙을 이해하고 적용하는 것은 선택이 아닌 필수입니다.&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;즉, OOP와 같은 또는 그러한 소프트웨어 사고의 틀을 모르면, 이 개념이 머릿속에 없으면 아무리 좋은 클라우드 서비스나 최신 기술(MSA, K8s)을 가져다 써도 시스템은 결국 거대한 스파게티 덩어리가 돼버립니다. 마찬가지로 아무리 쉬운 언어를 쥐어줘도 그 개발자들이 이런 개념이나 사고가 없다면 유지보수가 불가능한 스파게티 코드만을 만들 것입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; letter-spacing: 0px;&quot; data-darkreader-inline-color=&quot;&quot;&gt;따라서 이러한 &lt;/span&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot;&gt;설계 원칙&lt;/span&gt;&lt;span style=&quot;color: #000000; letter-spacing: 0px;&quot; data-darkreader-inline-color=&quot;&quot;&gt;들은 언어의 종류나 아키텍쳐에 무관한 '공학적 필수요소'입니다 &quot;Go나 C 같은 언어는 OOP 언어가 아니니까 몰라도 된다&quot;는 생각은 잘못된 오해 입니다. 언어 자체가 클래스 문법을 강제하지 않더라도, 모듈 간의 경계를 명확히 하고 결합도를 낮추는 행위는 모든 개발(&lt;/span&gt;&lt;span style=&quot;text-align: start;&quot;&gt;구현과 아키텍쳐를 포함한)&lt;/span&gt;&lt;span style=&quot;color: #000000; letter-spacing: 0px;&quot; data-darkreader-inline-color=&quot;&quot;&gt;의 기본이기 때문입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;C++이나 JAVA 등을 하지 않더라도 앞서 말한 설계 원칙들을 모르는 상태에서 코드를 구현한다던가 또는 소프트웨어, 시스템 아키텍처를 한다는 것은&amp;nbsp; 기초 없이 건물 올리는 것과 다르지 않으며 복잡도가 커질수록 문제는 드러날 수밖에 없습니다.&lt;/span&gt;&lt;/p&gt;</description>
      <author>tys</author>
      <guid isPermaLink="true">https://cplusplus.tistory.com/33</guid>
      <comments>https://cplusplus.tistory.com/entry/OOP-%EC%96%B8%EC%96%B4%EA%B0%80-%EC%95%84%EB%8B%88%EB%8B%A4-%E2%89%A0-%EC%84%A4%EA%B3%84-%EC%9B%90%EC%B9%99%EC%9D%84-%EC%95%88-%EC%A7%80%EC%BC%9C%EB%8F%84-%EB%90%9C%EB%8B%A4#entry33comment</comments>
      <pubDate>Sun, 21 Dec 2025 16:26:44 +0900</pubDate>
    </item>
    <item>
      <title>C++17로 진화하기: std::condition_variable의 안전한 랩핑</title>
      <link>https://cplusplus.tistory.com/entry/C17%EB%A1%9C-%EC%A7%84%ED%99%94%ED%95%98%EA%B8%B0-stdconditionvariable-stdthread-%EB%9E%A9%ED%95%91</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;이전에 아래 내용을 C++11로 랩핑 하였는데요,&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;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;a style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-4fb2ff, #51b5ff);&quot; href=&quot;https://cplusplus.tistory.com/entry/stdconditionvariable%EC%9D%98-%EC%95%88%EC%A0%84%ED%95%9C-%EC%8B%9C%EA%B7%B8%EB%84%90%EB%A7%81&quot; data-darkreader-inline-color=&quot;&quot;&gt;std::condition_variable의&amp;nbsp;안전한&amp;nbsp;시그널링&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;앞서 소개한 위 코드를 C++17로 다시 랩핑 하려 합니다.&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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;특히 std::condition_variable에서의 C++11의 복잡했던 템플릿식이 C++ 17의 if constexpr문으로 &lt;span style=&quot;text-align: start; --darkreader-inline-color: var(--darkreader-text-c8c3bc, #c1bcb4);&quot;&gt;훨씬&lt;/span&gt; 간결해지며, 덕분에 이해도 또한 월등히&amp;nbsp;쉬워졌습니다.&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;std::condition_variable(pthread_cond계열 함수도 포함하여)은 OS에 관계없이 시그널 놓침(Lost wakeup)과 가짜 깨움(Spurious wakeup)이라는 동일한 문제를 가지고 있습니다. 이 두 가지를 해결하지 않으면 문제 발생 시 재현이 어렵기 때문에 디버깅도 쉽지 않습니다.&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;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;따라서 std::condition_variable을 사용한다면 이 문제는 꼭 해결하고 넘어가야 하는 문제이며, 해결하려면 mutex 이외에 ready변수를 추가해야 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;std::condition_variable의 오용(missuse)&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;text-align: start; --darkreader-inline-color: var(--darkreader-text-c8c3bc, #c1bcb4);&quot;&gt;std::condition_variable에 대한 실제 업무에서의 실수는 아래처럼 다양합니다. 이는&lt;/span&gt;&amp;nbsp;std::condition_variable에 대한 경험이 부족하거나 &lt;span style=&quot;text-align: start; --darkreader-inline-color: var(--darkreader-text-c8c3bc, #c1bcb4);&quot;&gt;RAII를&amp;nbsp;&lt;/span&gt;모르거나, 혹은 일정에 쫓기다 보면 실무에서 많이 하는 실수들입니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1766294364095&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;std::mutex mutex;

// 송신부 ////////////////////////////////////////////
// RAII 미적용시 unlock 누락
{
  mutex.lock();
  order = {...};
  ready = true; // Lost Wakeup 방지 플래그
  if (...) return ...; // return문에서 unlock 누락
  if (...) return ...; // return문에서 unlock 누락
  if (...) return ...; // return문에서 unlock 누락
  mutex.unlock();
}
cv.notify_one();

// ready 플래그 누락
{
  std::lock_guard&amp;lt;std::mutex&amp;gt; guard(mutex);
  order = {...};
  if (...) return ...; // return문에서 ready누락, 런타임에 영원히 대기.
  ready = true; // Lost Wakeup 방지 플래그
}
cv.notify_one();

// 수신부 ////////////////////////////////////////////
// 조건 함수(predicate) 없이 wait (Spurious Wakeup 취약)
{
  std::unique_lock&amp;lt;std::mutex&amp;gt; lock(mutex);
  cv.wait(lock);  // 가짜 깨움 무방비
}

// lock 안 걸고 wait (undefined behavior)
{
  std::unique_lock&amp;lt;std::mutex&amp;gt; lock(mutex, std::defer_lock);
  cv.wait(lock); // 락이 안 걸린 상태
}

// 엉뚱한 mutex로 wait
{
  std::mutex mutex1, mutex2;
  std::unique_lock&amp;lt;std::mutex&amp;gt; lock(mutex2);  // producer는 mutex1 사용
  cv.wait(lock, []{ return ready; });
}

// while 대신 if (조건 함수(predicate) 없을 때)
{
  std::unique_lock&amp;lt;std::mutex&amp;gt; lock(mutex);
  if (ready == false) // while이어야 함
    cv.wait(lock);
  process(order);     // Spurious wakeup 시 ready=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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;비즈니스 코드가 많아지면 std::condition_variable 처리가 반복될 겁니다. 또 수정이나 추가사항에서도 &lt;span style=&quot;text-align: start;&quot;&gt;std::condition_variable 처리는 반복되고 비즈니스와 &lt;span style=&quot;text-align: start;&quot;&gt;std::condition_variable 처리 모두 집중해서 구현해야 합니다.&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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;OOP는 이러한 실수가 잦은 중복코드를 캡슐화(문제를 해결하고 외부에 노출하지 않음)를 통해 해결하고 추상화(쉽게 사용)하여 직관적으로 사용할 수 있도록 합니다. 결과적으로 복잡하고 반복적인 문제를 치환을 통해 쉽게 해결합니다.&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;color: #000000; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot;&gt;복잡성을 관리하는 방법론인 OOP를 적용하여 관리 가능한, 혹은 관리가 쉬운 수준으로 낮추는 것입니다. 이러한 복잡성 관리는 소프트웨어 아키텍처의 가장 기초적인 이해입니다.&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;color: #000000; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot;&gt;std::condition_variable을 랩핑 하여 비즈니스의 복잡도를 극적으로 낮추어 봅니다.&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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;std::condition_variable 추상화하기&lt;/span&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;추상화하지 않은 코드&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1766297156142&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;std::mutex              mtx;
std::condition_variable cv;
std::string             shared_data;
bool                    ready = false;

void consumer()
{
  std::unique_lock&amp;lt;std::mutex&amp;gt; lock(mtx);
  cv.wait(lock, []{ return ready; });

  std::cout &amp;lt;&amp;lt; &quot;received: &quot; &amp;lt;&amp;lt; shared_data &amp;lt;&amp;lt; std::endl;
}

void producer()
{
  {
    std::lock_guard&amp;lt;std::mutex&amp;gt; lock(mtx);
    shared_data = &quot;hello&quot;;
    ready = true;
  }
  cv.notify_one();
}

int main()
{
  std::thread t1(consumer);
  std::thread t2(producer);

  t1.join();
  t2.join();
  return 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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;추상화된 코드로 작성해 보기&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1766297232911&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;MSignal signal; // std::condition_variable를 추상화한 클래스
std::string  shared_data;

void consumer()
{
  signal.wait();
  std::cout &amp;lt;&amp;lt; &quot;received: &quot; &amp;lt;&amp;lt; shared_data &amp;lt;&amp;lt; std::endl;
}

void producer()
{
  signal.notify_one([&amp;amp;]{ shared_data = &quot;hello&quot;; });
}

int main()
{
  std::thread t1(consumer);
  std::thread t2(producer);

  t1.join();
  t2.join();
  return 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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;std::mutex : std::condition_variable의 요구 변수&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;ready 설정: 가짜 깨움, 시그널 놓침 해결변수&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;lock 범위 관리&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;lock_guard&amp;nbsp;vs&amp;nbsp;unique_lock&amp;nbsp;선택&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;조건함수(predicate)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;데이터 설정과 notify순서&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;이 변수들로 인해 생기는 문제들을 하나의 클래스로 해결하면서 앞서 말한 실수들을 사라지게 합니다.&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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;MSignal 인터페이스&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1766380620495&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class MSignal
{
public:
  // Misuse-Resistant Wrapper, Make interfaces hard to use incorrectly
  struct scoped_lock
  {
    explicit scoped_lock(MSignal &amp;amp;signal);

    void lock      ();
    void unlock    ();
    bool owns_lock () const noexcept;

    void notify_one();

    template&amp;lt;typename F&amp;gt; std::invoke_result_t&amp;lt;F&amp;gt;
         notify_one(F &amp;amp;&amp;amp;func);

    bool wait(uint32_t tmout_msecs = 0);
    bool wait(uint32_t tmout_msecs, const std::function&amp;lt;bool()&amp;gt; &amp;amp;func);
    bool wait(const std::function&amp;lt;bool()&amp;gt; &amp;amp;func);
  };

  void notify_one();

  template&amp;lt;typename F&amp;gt; std::invoke_result_t&amp;lt;F&amp;gt;
       notify_one(F &amp;amp;&amp;amp;func);

  // tmout_msec: 0이면 무한대기
  // false : timeout, true : wakeup
  bool wait(uint32_t tmout_msec = 0);

  // func가 true를 리턴하면 wait가 즉시 깨어남
  // 대기전 락을 걸고 func호출 false이면 대기-&amp;gt;깨어났을때 다시 func호출-&amp;gt;이때 fale이면 다시 대기...
  void wait(const std::function&amp;lt;bool()&amp;gt; &amp;amp;func);

  // false : timeout, true : wakeup
  bool wait(uint32_t tmout_msec, const std::function&amp;lt;bool()&amp;gt; &amp;amp;func);
};&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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;이전 C++11의 소스보다 읽기 쉬워지고 안전하도록 만들어졌습니다.&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;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;복잡한 템플릿이 C++17의 if constexpr을 활용하여 컴파일 타임에 결정하도록 변경하였고, unique_lock을 MSignal::scoped_lock로 랩핑 하여 안전해지도록 변경했습니다.&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;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;함수를 인자로 받는 notify_one&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1766382291882&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;void notify_one();

template&amp;lt;typename F&amp;gt; std::invoke_result_t&amp;lt;F&amp;gt;
   notify_one(F &amp;amp;&amp;amp;func);&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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;리턴타입: std::invoke_result_t&amp;lt;F&amp;gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;함수 객체나 함수 포인터, 멤버 함수 포인터 등의 리턴값을 알아냅니다. 컴파일 타임에 추론되어 자동으로 리턴값이 설정됩니다. 따라서 함수 F가 어떤 타입을 리턴하던 notify_one은 그 타입으로 리턴해 줄 수 있게 됩니다.&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;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;void 타입의 리턴이라면 컴파일 시에 문제가 생길 수 있어 함수구현부에서 if constexpr로 void타입에 대한 코드가 분기처리 시켜야 합니다.&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;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;오용 방지 랩퍼 MSignal::scoped_lock&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;std::condition_variable는 notify_one 또는 wait의 인자로 std::unique_lock을 받도록 되어 있습니다. MSignal의 멤버함수에서 직접 std::unique_lock을 받을 수도 있겠지만 이때 다른 unique_lock을 전달하는 실수를 방지하기 위한 장치로 MSignal::scoped_lock을&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;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;익숙함을 위해 인터페이스는 std::unique_lock과 유사하게 만듭니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1766381930157&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class MSignal
{
public:
  ....
  // Misuse-Resistant Wrapper, Make interfaces hard to use incorrectly
  struct scoped_lock
  {
    explicit scoped_lock(MSignal &amp;amp;signal);

    void lock      ();
    void unlock    ();
    bool owns_lock () const noexcept;

    void notify_one();

    template&amp;lt;typename F&amp;gt; std::invoke_result_t&amp;lt;F&amp;gt;
         notify_one(F &amp;amp;&amp;amp;func);

    bool wait(uint32_t tmout_msecs = 0);
    bool wait(uint32_t tmout_msecs, const std::function&amp;lt;bool()&amp;gt; &amp;amp;func);
    bool wait(const std::function&amp;lt;bool()&amp;gt; &amp;amp;func);
    .....
  };

  ...
};&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;&lt;span style=&quot;color: #000000; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot;&gt;추가적인 인터페이스로 scoped_lock에서 직접 notify_one이나 wait를 호출하도록 합니다. MSignal에서 lock인자를 받아 호출해야 하지만 그런 경우 std::condition_variable과 같은 문제와 더불어, lock인자를 누락하여 호출하는 경우 dead lock이 발생할 수 있습니다. 이런 오용을 최대한 줄이기 위해 lock을 획득한 이후 MSignal::scoped_lock에서 직접 wait나 notify를 처리할 수 있도록 합니다.&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;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;다음과 같이 락을 획득할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1766383524209&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;MSignal signal;

{
  MSignal::scoped_lock slock(signal); // signal의 lock 획득
  ...
  slock.notify_one(); // 시그널 보냄
  // or
  slock.wait();   // 또는 시그널 대기
  
  // signal.wait()       // 데드락
  // signal.notify_one() // 데드락
} // signal의 lock 자동해제&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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;MSignal::scoped_lock 은 MSignal::scoped_lock 클래스의 다음 멤버 함수에서 사용됩니다. 락을 획득하고 wait나 혹은 notify_one을 사용해야 한다면 아래 함수들을 호출할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1766383402452&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  struct scoped_lock
  {
    .....
    void notify_one();
    template&amp;lt;typename F&amp;gt; std::invoke_result_t&amp;lt;F&amp;gt;
         notify_one(F &amp;amp;&amp;amp;func);

    bool wait(uint32_t tmout_msecs = 0);
    bool wait(uint32_t tmout_msecs, const std::function&amp;lt;bool()&amp;gt; &amp;amp;func);
    bool wait(const std::function&amp;lt;bool()&amp;gt; &amp;amp;func);
    .....
  };&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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;std::unique_lock&amp;lt;std::mutex&amp;gt;를 직접 받지 못하도록 하여 오용으로 인한 오류를 최대한으로 줄입니다.&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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;구현&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;이 코드는 1:1 관계에서만 사용하도록 설계되어 있습니다. 따라서 1:N 또는 N:N 이상의 관계에서는 올바르게 동작하지 않습니다.&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;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;1:N이나 N:N 요구를 그대로 얹으면 책임 경계가 흐려지고, 코드 내부에서 상태 관리와 분기 처리가 급격히 복잡해집니다. 따라서 그 복잡도를 하위 객체가 떠안지 않도록 하기 위해서는 상위 레이어에서 조합하여 전달하는 방식을 선택할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;218&quot; data-start=&quot;101&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-is-only-node=&quot;&quot; data-is-last-node=&quot;&quot; data-end=&quot;364&quot; data-start=&quot;220&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;구체적으로는 각 소비자가 MSignal을 소유하고, 그 상위 레이어로 &amp;lsquo;소비자 관리 클래스&amp;rsquo;를 두어 어떤 소비자에게 어떤 방식으로 시그널을 전달할지 결정하도록 합니다. 이 구조는 책임이 명확해지고 동작 예측 가능해지므로, 변경과 디버깅이 한층 수월해집니다&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;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;구현부와 같이 전체 소스는 이렇습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1766382061602&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#pragma once

#include &amp;lt;mutex&amp;gt;
#include &amp;lt;condition_variable&amp;gt;
#include &amp;lt;chrono&amp;gt;
#include &amp;lt;functional&amp;gt;

// 시그널을 놓치는 문제와 가짜깨움이 발생하지 않도록 구현됨.
//
// 이 코드는 1:1 관계에서만 사용하도록 설계되어 있습니다. 따라서 1:N 또는 N:N 이상의
// 관계에서는 올바르게 동작하지 않습니다.
//
// 1:N이나 N:N 요구를 그대로 얹으면 책임 경계가 흐려지고, 코드 내부에서 상태 관리와 분기
// 처리가 급격히 복잡해집니다. 따라서 그 복잡도를 하위 객체가 떠안지 않도록 하기 위해서는
// 상위 레이어에서 조합하여 전달하는 방식을 선택할 수 있습니다.
//
// 1:N 혹은 N:N 사용하려면
// 구체적으로는 각 소비자가 MSignal을 소유하고, 그 상위 레이어로 '소비자 관리 클래스'를
// 두어 어떤 소비자에게 어떤 방식으로 시그널을 전달할지 결정하도록 합니다. 이 구조는 책임
// 이 명확해지고 동작 예측이 가능해지므로, 변경과 디버깅이 한층 수월해집니다
//
// lost Wakeup     - wait가 아닐때 signal을 보내면 wait를 받지 못함.
// spurious Wakeup - POSIX나 모든 OS에서 signal을 줬을때 하나만 깨어나는 것이 아니라
//                   동시에 여러 wait condition이 깨어나는 현상을 뜻합니다.
//                   이는 OS의 성능 이슈이기 때문에 개발자 영역으로 남겨져 있습니다.
//
//  1. 기본 notify_one
//  signal.notify_one() // 대기하는 signal을 깨웁니다.
//
//  2. 기본 람다 notify_one - 리턴값 없음
//  signal.notify_one([&amp;amp;]() { shared_data = &quot;new data&quot;; });
//
//  3. scoped_lock 사용
//  MSignal::scoped_lock slock(signal);
//  ... // 작업 수행
//  slock.notify_one();
//
//  4. scoped_lock과 람다
//  MSignal::scoped_lock slock(signal);
//  slock.notify_one([&amp;amp;]() { shared_data = &quot;new data&quot;; });
//
//  5. scoped_lock과 람다 - 리턴값 있음
//  MSignal::scoped_lock slock(signal);
//  bool success = slock.notify_one([&amp;amp;]() -&amp;gt; bool
//  {
//      shared_data = &quot;new data&quot;;
//      return true;
//  });

class MSignal
{
public:
  MSignal () = default;
  ~MSignal() = default;

  // Misuse-Resistant Wrapper, Make interfaces hard to use incorrectly
  struct scoped_lock
  {
    explicit scoped_lock(MSignal &amp;amp;signal);

    void lock      ();
    void unlock    ();
    bool owns_lock () const noexcept;

    void notify_one();

    template&amp;lt;typename F&amp;gt; std::invoke_result_t&amp;lt;F&amp;gt;
         notify_one(F &amp;amp;&amp;amp;func);

    bool wait(uint32_t tmout_msecs = 0);
    bool wait(uint32_t tmout_msecs, const std::function&amp;lt;bool()&amp;gt; &amp;amp;func);
    bool wait(const std::function&amp;lt;bool()&amp;gt; &amp;amp;func);

    scoped_lock () = delete;
    scoped_lock (const scoped_lock&amp;amp;) = delete;
    scoped_lock (scoped_lock&amp;amp;&amp;amp;     ) = default;
    scoped_lock &amp;amp;operator=(const scoped_lock&amp;amp;) = delete;
    scoped_lock &amp;amp;operator=(scoped_lock&amp;amp;&amp;amp;     ) = delete;
    ~scoped_lock() = default;

  private:
    friend class MSignal;
    MSignal &amp;amp;signal_;
    std::unique_lock&amp;lt;std::mutex&amp;gt; lock_;
  };

  void notify_one();

  template&amp;lt;typename F&amp;gt; std::invoke_result_t&amp;lt;F&amp;gt;
       notify_one(F &amp;amp;&amp;amp;func);

  // tmout_msec: 0이면 무한대기
  // false : timeout, true : wakeup
  bool wait(uint32_t tmout_msec = 0) { return this-&amp;gt;wait(tmout_msec, nullptr); }

  // func가 true를 리턴하면 wait가 즉시 깨어남
  // 대기전 락을 걸고 func호출 false이면 대기-&amp;gt;깨어났을때 다시 func호출-&amp;gt;이때 fale이면 다시 대기...
  void wait(const std::function&amp;lt;bool()&amp;gt; &amp;amp;func) { this-&amp;gt;wait(0, func); }

  // false : timeout, true : wakeup
  bool wait(uint32_t tmout_msec, const std::function&amp;lt;bool()&amp;gt; &amp;amp;func)
  {
    std::unique_lock&amp;lt;std::mutex&amp;gt; guard(lock_);
    return wait_impl(guard, tmout_msec, func);
  }

protected:
  void notify_one_nolock()
  {
    if (notified_ == true)
      return;

    notified_ = true;
    cond_.notify_one();
  }

  bool wait_impl(std::unique_lock&amp;lt;std::mutex&amp;gt; &amp;amp;lock, uint32_t tmout_msec, const std::function&amp;lt;bool()&amp;gt; &amp;amp;func);

private:
  friend struct scoped_lock;
  mutable std::mutex      lock_;
  std::condition_variable cond_;
  bool                    notified_ = false;
};

// ============================================================================
// MSignal 구현부
// ============================================================================

inline void
MSignal::notify_one()
{
  std::lock_guard&amp;lt;std::mutex&amp;gt; guard(lock_);
  notify_one_nolock();
}

template&amp;lt;typename F&amp;gt; std::invoke_result_t&amp;lt;F&amp;gt;
MSignal::notify_one(F &amp;amp;&amp;amp;func)
{
  std::lock_guard&amp;lt;std::mutex&amp;gt; guard(lock_);

  if constexpr (std::is_void_v&amp;lt;std::invoke_result_t&amp;lt;F&amp;gt;&amp;gt;)
  {
    std::forward&amp;lt;F&amp;gt;(func)();
    notify_one_nolock();
  }
  else
  {
    auto result = std::forward&amp;lt;F&amp;gt;(func)();
    notify_one_nolock();
    return result;
  }
}

inline bool
MSignal::wait_impl(std::unique_lock&amp;lt;std::mutex&amp;gt; &amp;amp;lock, uint32_t tmout_msec, const std::function&amp;lt;bool()&amp;gt; &amp;amp;func)
{
  size_t count = 0;
  std::function&amp;lt;bool()&amp;gt; pred = [&amp;amp;]()
  {
    const bool notified = notified_;

    if (notified_ == true)
      notified_ = false;

    if (func != nullptr)
    {
      if (count++  == 0   ) return func();  // 첫 호출 (무조건 한 번은 호출)
      if (notified == true) return func();  // 시그널 받았을 때
    }

    return notified;
  };

  if (tmout_msec == 0)
  {
    cond_.wait(lock, pred);
    return true;
  }

  std::chrono::milliseconds timeout(tmout_msec);

  // 표준 패턴
  // predicate는:
  // 1. wait 진입 시 무조건 한 번 호출 (이미 조건 만족이면 대기 안 함)
  // 2. 깨어났을 때 호출
  return cond_.wait_for(lock, timeout, pred);
}

// ============================================================================
// scoped_lock 구현부
// ============================================================================

inline
MSignal::scoped_lock::scoped_lock(MSignal &amp;amp;signal)
: signal_(signal), lock_(signal.lock_) {}

inline void
MSignal::scoped_lock::lock()
{ lock_.lock(); }

inline void
MSignal::scoped_lock::unlock()
{ lock_.unlock(); }

inline bool
MSignal::scoped_lock::owns_lock() const noexcept
{ return lock_.owns_lock(); }

inline void
MSignal::scoped_lock::notify_one()
{
  if (owns_lock() == false)
    lock();
  signal_.notify_one_nolock();
}

template&amp;lt;typename F&amp;gt; std::invoke_result_t&amp;lt;F&amp;gt;
MSignal::scoped_lock::notify_one(F &amp;amp;&amp;amp;func)
{
  if (owns_lock() == false)
    lock();

  if constexpr (std::is_void_v&amp;lt;std::invoke_result_t&amp;lt;F&amp;gt;&amp;gt;)
  {
    std::forward&amp;lt;F&amp;gt;(func)();
    signal_.notify_one_nolock();
  }
  else
  {
    auto result = std::forward&amp;lt;F&amp;gt;(func)();
    signal_.notify_one_nolock();
    return result;
  }
}

inline bool
MSignal::scoped_lock::wait(uint32_t tmout_msecs)
{
  if (owns_lock() == false)
    lock();
  return signal_.wait_impl(lock_, tmout_msecs, nullptr);
}

inline bool
MSignal::scoped_lock::wait(uint32_t tmout_msecs, const std::function&amp;lt;bool()&amp;gt; &amp;amp;func)
{
  if (owns_lock() == false)
    lock();
  return signal_.wait_impl(lock_, tmout_msecs, func);
}

inline bool
MSignal::scoped_lock::wait(const std::function&amp;lt;bool()&amp;gt; &amp;amp;func)
{
  return wait(0, func);
}&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;&lt;span style=&quot;color: #000000; text-align: start;&quot; data-darkreader-inline-color=&quot;&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;있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <author>tys</author>
      <guid isPermaLink="true">https://cplusplus.tistory.com/32</guid>
      <comments>https://cplusplus.tistory.com/entry/C17%EB%A1%9C-%EC%A7%84%ED%99%94%ED%95%98%EA%B8%B0-stdconditionvariable-stdthread-%EB%9E%A9%ED%95%91#entry32comment</comments>
      <pubDate>Sun, 21 Dec 2025 15:18:47 +0900</pubDate>
    </item>
    <item>
      <title>C++ 간결하고 안전한 숫자변환: to_number: 위험한 atoi 버리기</title>
      <link>https://cplusplus.tistory.com/entry/C-%ED%8E%B8%EB%A6%AC%ED%95%98%EA%B3%A0-%EC%95%88%EC%A0%84%ED%95%9C-%EC%88%AB%EC%9E%90%EB%B3%80%ED%99%98-tonumber-%EB%A7%8C%EB%93%A4%EA%B8%B0-%EC%9C%84%ED%97%98%ED%95%9C-%EB%B2%84%EA%B7%B8-%EC%9C%A0%EB%B0%9C%EC%9E%90-atoi-%EB%B2%84%EB%A6%AC%EA%B8%B0</link>
      <description>&lt;h3 style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-ke-size=&quot;size23&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;여기서 사용된 C++ 기법들&lt;/span&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;템플릿&amp;nbsp;관련&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;-&amp;nbsp;함수&amp;nbsp;템플릿:&amp;nbsp;`template&amp;lt;typename&amp;nbsp;T&amp;gt;`&amp;nbsp;로&amp;nbsp;타입별&amp;nbsp;함수&amp;nbsp;자동&amp;nbsp;생성&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;-&amp;nbsp;템플릿&amp;nbsp;특수화:&amp;nbsp;`template&amp;lt;&amp;gt;&amp;nbsp;struct&amp;nbsp;parser&amp;lt;uint64_t&amp;gt;`&amp;nbsp;처럼&amp;nbsp;특정&amp;nbsp;타입에&amp;nbsp;대한&amp;nbsp;별도&amp;nbsp;구현&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;-&amp;nbsp;비타입&amp;nbsp;템플릿&amp;nbsp;파라미터:&amp;nbsp;`std::ios_base&amp;nbsp;&amp;amp;(*in_type)(std::ios_base&amp;nbsp;&amp;amp;)&amp;nbsp;=&amp;nbsp;std::dec`&amp;nbsp;-&amp;nbsp;함수&amp;nbsp;포인터를&amp;nbsp;템플릿&amp;nbsp;인자로&amp;nbsp;사용&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;컴파일&amp;nbsp;타임&amp;nbsp;기법&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;-&amp;nbsp;`static_assert`:&amp;nbsp;컴파일&amp;nbsp;타임에&amp;nbsp;잘못된&amp;nbsp;타입&amp;nbsp;사용&amp;nbsp;차단&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;-&amp;nbsp;`if&amp;nbsp;constexpr`&amp;nbsp;(C++17):&amp;nbsp;컴파일&amp;nbsp;타임&amp;nbsp;분기,&amp;nbsp;조건에&amp;nbsp;맞지&amp;nbsp;않는&amp;nbsp;코드는&amp;nbsp;아예&amp;nbsp;컴파일&amp;nbsp;안&amp;nbsp;됨&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;-&amp;nbsp;`std::conditional_t`:&amp;nbsp;조건에&amp;nbsp;따라&amp;nbsp;타입&amp;nbsp;선택&amp;nbsp;(메타프로그래밍)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;타입&amp;nbsp;트레이트&amp;nbsp;(Type&amp;nbsp;Traits)&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;-&amp;nbsp;`std::is_arithmetic_v&amp;lt;T&amp;gt;`:&amp;nbsp;숫자&amp;nbsp;타입인지&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;-&amp;nbsp;`std::is_unsigned_v&amp;lt;T&amp;gt;`:&amp;nbsp;unsigned인지&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;-&amp;nbsp;`std::is_integral_v&amp;lt;T&amp;gt;`:&amp;nbsp;정수형인지&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;-&amp;nbsp;`std::is_floating_point_v&amp;lt;T&amp;gt;`:&amp;nbsp;부동소수점인지&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;-&amp;nbsp;`std::numeric_limits&amp;lt;T&amp;gt;`:&amp;nbsp;타입의&amp;nbsp;최소/최대값&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;디자인&amp;nbsp;패턴&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;-&amp;nbsp;Proxy&amp;nbsp;패턴:&amp;nbsp;`to_number_proxy`&amp;nbsp;구조체로&amp;nbsp;타입&amp;nbsp;추론&amp;nbsp;지연&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;-&amp;nbsp;형변환&amp;nbsp;연산자&amp;nbsp;오버로딩:&amp;nbsp;`operator&amp;nbsp;T()`로&amp;nbsp;대입&amp;nbsp;시점에&amp;nbsp;타입&amp;nbsp;결정&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;- detail 네임스페이스: &lt;span style=&quot;background-color: #fafafa; text-align: start; --darkreader-inline-bgcolor: var(--darkreader-background-fafafa, #1b1d1e); --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-bgcolor=&quot;&quot; data-darkreader-inline-color=&quot;&quot;&gt;관심사 분리&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;예외&amp;nbsp;처리&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;-&amp;nbsp;`try/catch`로&amp;nbsp;`std::stoull`&amp;nbsp;등의&amp;nbsp;예외&amp;nbsp;처리&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;-&amp;nbsp;`std::invalid_argument`,&amp;nbsp;`std::out_of_range`&amp;nbsp;활용&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;기타&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;-&amp;nbsp;`using`&amp;nbsp;타입&amp;nbsp;별칭:&amp;nbsp;`using&amp;nbsp;value_type&amp;nbsp;=&amp;nbsp;...`&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;-&amp;nbsp;기본&amp;nbsp;인자값:&amp;nbsp;템플릿&amp;nbsp;파라미터와&amp;nbsp;함수&amp;nbsp;파라미터&amp;nbsp;모두에&amp;nbsp;기본값&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;h3 style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-ke-size=&quot;size23&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;위험한 atoi 함수&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;대부분의 정적분석 툴에서 atoi를 위험한 함수로 사용하지 말 것을 강하게 권고하고 있습니다. atoi() 함수는 유효한 숫자인지 아닌지에 대한 오류 처리 기능이 없고, 오버플로/언더플로를 감지하지 못하며, 입력 유효성 검사가 불가능하여 프로그램의 예측 불가능한 동작(미정의 동작, undefined behavior)이나 보안 취약점을 유발할 수 있기 때문에 위험합니다.&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;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;특히나 설정을 읽고 적용할 때는 그 형식이 맞는지 모두 확인해야 하기 때문에 C 형태로 작업한다는 것은 매우 힘들고 오류 발생 가능성도 높습니다.&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;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;C&amp;nbsp;스타일:&amp;nbsp;atoi,&amp;nbsp;atol,&amp;nbsp;atoll,&amp;nbsp;atof,&amp;nbsp;strtol,&amp;nbsp;strtoll,&amp;nbsp;strtoul,&amp;nbsp;strtoull&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1765873387634&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int n1 = atoi(&quot;123&quot;);      // OK, 123
int n2 = atoi(&quot;123abc&quot;);   // OK, 123 (뒤의 &quot;abc&quot; 무시)
int n3 = atoi(&quot;abc&quot;);      // 0 (에러인지 진짜 0인지 구분 불가, atoi는 변환 실패시 0반환)
int n4 = atoi(&quot;99999999999999999&quot;); // 정의되지 않은 동작(UB)

// 이게 에러인지 실제 0인지 알 수 없음
int value = atoi(user_input.c_str());
if (value == 0)
{
  // 입력이 &quot;0&quot;이었나? &quot;abc&quot;였나? &quot;0abc&quot;였나?
}

char *end;
long n = strtol(&quot;123abc&quot;, &amp;amp;end, 10);
// n = 123, end는 &quot;abc&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt; 여기서는 atoi만 사용했지만 진짜 제대로 하려면 모든 걸 타입에 맞는 함수로 가져다 사용해야 합니다. C++ 표준함수 std::stoi, stol, stoll, stoul, stoull 도 예외처리등 개선되었지만, C함수를 C++ 함수로 바꾼 것에 불과하고 타입에 따라 별도로 분기 처리해야 하기&amp;nbsp; 때문에 큰 체감은 얻을 수 없습니다.&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;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;std::stringstream&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1765873551060&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;std::stringstream ss(&quot;123&quot;);
int n;
ss &amp;gt;&amp;gt; n;
if (ss.fail()) { /* 에러 처리 */ }&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;코드가 길고 복잡해지며 실패 여부를 별도 체크해야 합니다. 성능 오버헤드도 존재합니다. 그 외 C++17부터 사용 가능한 std::from_chars도 있지만 인터페이스가 좀 불편합니다.&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;간결하고 안전한 API 만들어 보기&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;그래서 이런 인터페이스를 구상합니다. 저 개인적으로는 대체로 편의성에 더 높은 비중을 둡니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1765874142546&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 기본 아이디어(10진수 기본) - 느슨한 사용
int32_t  d1 = to_number(&quot;-1234&quot;);
uint32_t d2 = to_number(&quot;1234&quot;);

// 템플릿을 활용한 auto 사용하기
auto d1 = to_number&amp;lt;int32_t&amp;gt;(&quot;-1234&quot;);
auto d2 = to_number&amp;lt;uint32_t&amp;gt;(&quot;1234&quot;);

// 8, 16진수 지원
auto d3 = to_number&amp;lt;int32_t, std::hex&amp;gt;(&quot;0xFF&quot;); // 255
auto d3 = to_number&amp;lt;int32_t, std::oct&amp;gt;(&quot;777&quot;);  // 511

// 진법만 지정, 타입은 추론
uint32_t n = abc::to_number&amp;lt;std::hex&amp;gt;(&quot;FF&quot;);

// 엄격한 사용, 입력값이 오류인 경우 throw를 발생합니다.
try
{
  int32_t d1 = to_number(&quot;-1234a&quot;, true);
}
catch (const std::exception &amp;amp;e)
{
  // 출력 Invalid number format: -1234a
  std::cout &amp;lt;&amp;lt; e.what() &amp;lt;&amp;lt; std::endl;
}&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;이렇게 해서 타입별로 호출 함수가 다름을 템플릿으로 해결하고, 엄격한 모드를 켜면 입력 오류까지 검출하도록 합니다.&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;&lt;span style=&quot;color: #000000; text-align: start; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;함수원형&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;위 사용예제를 사용하는 데에 필요한 함수 원형은 아래와 같습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1765936416034&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// strict가 true인 경우 in이 T, in_type 형식이 아니면 예외를 발생합니다.
// 만약 false인 경우 마치 atoi처럼 동작합니다.

template&amp;lt;typename T, std::ios_base &amp;amp;(*in_type)(std::ios_base &amp;amp;) = std::dec&amp;gt; T
to_number(const std::string &amp;amp;in, bool strict = false);&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;template T&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&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;&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;uint32_t d2 = to_number(&quot;1234&quot;);&lt;br /&gt;&lt;span style=&quot;text-align: start;&quot; data-grammar=&quot;{&amp;quot;input&amp;quot;:&amp;quot;컴파일시에&amp;quot;,&amp;quot;output&amp;quot;:&amp;quot;컴파일 시에&amp;quot;,&amp;quot;etype&amp;quot;:&amp;quot;space&amp;quot;}&quot; data-grammar-id=&quot;grammar1&quot; data-grammar-focus=&quot;false&quot;&gt;&lt;/span&gt;&lt;/span&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;&lt;span style=&quot;color: #000000; text-align: start; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;컴파일 시에 컴파일러가 d2가 uint32_t를 알고 있으므로 &amp;lt;uint32_t&amp;gt;로 만들어 줍니다.&lt;/span&gt;&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;auto d1 = to_number&amp;lt;int32_t&amp;gt;(&quot;-1234&quot;);&lt;br /&gt;&lt;/span&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;&lt;span style=&quot;color: #000000; text-align: start; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;컴파일러는 d1이 뭔지 모르는 추론해야 하는 대상입니다. 따라서 to_number&amp;lt;int32_t&amp;gt;를 명시함으로써 컴파일러가 d1은 int32_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;color: #000000; text-align: start; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;특징은 2가지 방법 모두 한 번만 타입을 지정해 주면 자동으로 그 타입으로 변수와 함수가 컴파일러에 의해 컴파일 타임에 확정된다는 것입니다. 동시에 코드가 곧 문서가 되는 &quot;자기 문서화&quot;가 이루어집니다.&lt;/span&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;C라면 서로 타입이 다를지 아닐지 신경 쓰고 확인해야 했지만, 템플릿으로 그 부분이 사라졌습니다. 구현상의 실수도 사라지고 해당 라인에서는 어떤 타입으로 변환되는지 명확히 알 수 있기 때문에 템플릿을 안다면 가독성이 좋아지고 간결해집니다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;무엇보다도 안전해집니다.&lt;/span&gt;&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;비타입 템플릿 파라미터&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;비타입 템플릿 파라미터란 타입으로는 타입형태뿐 아니라 값 자체를 파라미터 인자로 사용할 때를 말합니다. to_number의 두 번째 템플릿 인자인 in_type이 비타입 템플릿 파라미터입니다.&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;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;여기서는 직접 사용하지 않고 진법구분 용도로만 사용합니다만 내용은 알고 가겠습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1765937289310&quot; class=&quot;cpp&quot; style=&quot;background-color: #f8f8f8; color: #383a42; --darkreader-inline-bgcolor: var(--darkreader-background-f8f8f8, #1c1e1f); --darkreader-inline-color: var(--darkreader-text-383a42, #c1bcb4);&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;cpp&quot; data-darkreader-inline-bgcolor=&quot;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;code&gt;std::ios_base &amp;amp;(*in_type)(std::ios_base &amp;amp;) = std::dec
      │           │              │               └─ 기본값: std::dec
      │           │              └─ 파라미터: std::ios_base&amp;amp; 받음
      │           └─ 함수 포인터(템플릿 파라미터 이름)
      └─ 반환 타입: std::ios_base&amp;amp;&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;in_type은 함수 포인터로 C++ 표준에 정의된 전역함수이지만 단순한 진법 구분 용도로 다음의 3가지를 사용합니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1765937240901&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;std::ios_base&amp;amp; std::dec(std::ios_base&amp;amp; str);
std::ios_base&amp;amp; std::hex(std::ios_base&amp;amp; str);
std::ios_base&amp;amp; std::oct(std::ios_base&amp;amp; str);&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;std::hexfloat도 있으나 실무에서 잘 사용되지 않아 지금은 제외합니다만, 나중에 언제 필요할지는 모릅니다.&amp;nbsp; hexfloat을 사용하지 않는다면 size_t base = 10 이런 식으로 비타입 템플릿 파라미터를 정의해 사용해도 될 겁니다.&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;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;진법 인자로 ios_base 함수 포인터를 사용하지 하지 않아도, 혹은 size_t base로 해도 않을까?&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;size_t base가 직관적이긴 하나 추후 확장할 수 있는 std::hexfloat인 경우 애매해질 수 있기 때문에 저는 ios_base를 사용했습니다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;또 하나 템플릿인 이유는 bool strict과 std::dec를 인자를 나란히 하는 경우 불편할 수 있고, 쉽게 사용토록 구성하려면 상황에 맞는 여러 인터페이스를 만들어야 하기 때문이기도 합니다.&lt;/span&gt;&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;to_number 함수정의&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1765944717917&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;template&amp;lt;typename T, std::ios_base &amp;amp;(*in_type)(std::ios_base &amp;amp;)&amp;gt; T
to_number(const std::string &amp;amp;in, bool strict)
{
  // T형식이 잘못된 사용을 방지하기 위해 컴파일 타임에 체크할 수 있습니다.
  // 아래 static_assert는 복잡한 SFINAE보다 직관적이고 간단할 수 있습니다.
  static_assert(std::is_arithmetic_v&amp;lt;T&amp;gt;, &quot;T must be numeric type&quot;);
  static_assert(in_type == std::dec || in_type == std::hex || in_type == std::oct, &quot;Unsupported number base&quot;);
  static_assert(!(std::is_floating_point_v&amp;lt;T&amp;gt; &amp;amp;&amp;amp; in_type != std::dec), &quot;Floating point only supports decimal&quot;);

  if (in.empty())
  {
    if (strict) throw std::invalid_argument(&quot;Invalid number format&quot;);
    return 0;
  }

  // constexpr을 사용하면 좋겠지만 구형 컴파일러에서 제대로 지원하지 못하는 경우가 있어
  // const로 대채합니다. constexpr을 사용하면 base가 이미 컴파일 타임에 상수화 되기 때문에
  // 성능과 최적화에 도움이 됩니다.
  //  constexpr int base= (in_type == std::hex) ? 16 : (in_type == std::oct) ? 8 : 10;
  const int base= (in_type == std::hex) ? 16 : (in_type == std::oct) ? 8 : 10;
  
  // detail namespace는 아래에 다시 설명합니다.
  using value_type  = detail::value_type&amp;lt;T&amp;gt;;

  // 이 try~catch문은 parse에서 사용할 std::stoull, stoll, stod등의 exception을 처리합니다.
  // catch에서는 strict 변수에 따라 예외를 전파할지 판단합니다.
  // 실패시 기본 반환값은 표준함수군과 마찬가지로 0을 반환시키도록 합니다.
  try
  {
    // C++17의 constexpr입니다. 컴파일타임에 분기되어 별개의 함수처럼 동작하도록 만듭니다.
    // unsigned이면 음수기호가 필요 없으므로 컴파일러는 T가 unsigned가 아니면 
    // 이 코드는 컴파일하지 않습니다.
    // 런타임 분기보다 성능은 챙기면서, 코드는 동일하게 작성할 수 있습니다.
    if constexpr (std::is_unsigned_v&amp;lt;T&amp;gt;)
      if (strict &amp;amp;&amp;amp; in.find_first_of('-') != std::string::npos)
        throw std::invalid_argument(&quot;Invalid number format: &quot; + in);

    size_t     pos   = 0;
    value_type value = 0;
    try { value = detail::parser&amp;lt;value_type&amp;gt;::parse(in, &amp;amp;pos, base); }
    catch (...) { throw std::invalid_argument(&quot;Invalid number format: &quot; + in); }

    if (strict)
    {
      if (pos != in.length())
        if (in.find_first_not_of(&quot; \t\n\r\f\v&quot;, pos) != std::string::npos)
          throw std::invalid_argument(&quot;Invalid number format: &quot; + in);

      if (detail::in_range&amp;lt;T&amp;gt;(value) == false)
        throw std::out_of_range(&quot;Value exceeds target type range: &quot; + in);
    }

    return static_cast&amp;lt;T&amp;gt;(value); // 자동 wraparound/클램핑
  }
  catch (...)
  {
    if (strict) throw; // 예외전파.
  }

  return 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;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;namespace detail 에서 파싱 하기&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1765945247146&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;namespace detail
{
  // 빈 parser를 선언합니다.
  template&amp;lt;typename T&amp;gt; struct parser;

  // 각 parser들을 to_number의 T인 type에 따라 특수화 합니다.
  template&amp;lt;&amp;gt; struct parser&amp;lt;uint64_t&amp;gt;
  {
    static uint64_t parse(const std::string &amp;amp;s, size_t *pos, int base) { return std::stoull(s, pos, base); }
  };

  template&amp;lt;&amp;gt; struct parser&amp;lt;int64_t&amp;gt;
  {
    static int64_t  parse(const std::string &amp;amp;s, size_t *pos, int base) { return std::stoll(s, pos, base); }
  };

  template&amp;lt;&amp;gt; struct parser&amp;lt;double&amp;gt;
  {
    static double   parse(const std::string &amp;amp;s, size_t *pos, int) { return std::stod(s, pos); }
  };

  // value 타입을 편하게 사용하기 위해 조건부 타입으로 컴파일러가 선택하도록 합니다.
  template&amp;lt;typename T&amp;gt;
  using value_type = std::conditional_t&amp;lt;std::is_unsigned_v&amp;lt;T&amp;gt;, uint64_t,
                     std::conditional_t&amp;lt;std::is_integral_v&amp;lt;T&amp;gt;, int64_t, double&amp;gt;&amp;gt;;

  // 올바른 범위인지 검사하는 템플릿 함수 입니다.
  // v가 T범위에 있는지 확인합니다.
  template&amp;lt;typename T, typename V&amp;gt;
  bool in_range(V v)
  {
    if constexpr (std::is_floating_point_v&amp;lt;T&amp;gt;)
      return (std::numeric_limits&amp;lt;T&amp;gt;::lowest() &amp;lt;= v &amp;amp;&amp;amp; v &amp;lt;= std::numeric_limits&amp;lt;T&amp;gt;::max())
             || std::isinf(v) || std::isnan(v);
    else
      return static_cast&amp;lt;V&amp;gt;(std::numeric_limits&amp;lt;T&amp;gt;::min()) &amp;lt;= v &amp;amp;&amp;amp; v &amp;lt;= static_cast&amp;lt;V&amp;gt;(std::numeric_limits&amp;lt;T&amp;gt;::max());
  }
}&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;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;자동 추론을 위한 Proxy 패턴&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1765945623320&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int32_t a = to_number(&quot;1234&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;아래 Proxy 패턴 적용은 위와 같이 사용하도록 하기 위한 핵심입니다. 이때 컴파일러가 to_number&amp;lt;int32_t&amp;gt;를 호출하게 만드는 과정입니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1765945573874&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;template&amp;lt;std::ios_base &amp;amp;(*in_type)(std::ios_base&amp;amp;) = std::dec&amp;gt;
struct to_number_proxy
{
  std::string str;
  bool strict = false;
  // 자동 추론의 핵심
  // operator T()는 형변환 연산자로써 보통 (float)k; 이런식으로 사용합니다.
  // 이때 (float)부분을 k의 아래와 같은 멤버함수가 있다면 호출하여 줍니다.
  template&amp;lt;typename T&amp;gt; operator T() { return to_number&amp;lt;T, in_type&amp;gt;(str, strict); }
};

// to_number_proxy의 operator T() 기능을 직접 사용할 수도 있겠지만 이러면 코드가 많이 번거로우니
// 원래 의도한 대로 to_number함수가 to_number_proxy를 한번 감싸서 인터페이스를 간결하게 만듭니다.
template&amp;lt;std::ios_base &amp;amp;(*in_type)(std::ios_base&amp;amp;) = std::dec&amp;gt;
to_number_proxy&amp;lt;in_type&amp;gt;
to_number(const std::string &amp;amp;in, bool strict = false)
{
  return {in, strict};
}&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;to_number(&quot;1234&quot;) 는 to_number_proxy의 str에 in을 strict에 strict를 전달합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;to_number_proxy라는 임시 객체가 만들어지고 반환됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;'int32_t a = ' 의 과정에서 to_number_proxy::operator T()가 호출되는데 이때 T = int32_t가 됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;그럼 상단에 만들어 두었던 to_number&amp;lt;int32_t, in_type&amp;gt;(...) 함수가 실행되고 결과값을 반환합니다.&lt;/span&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;부가 편의 함수를 만들 수 있습니다.&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1765945482325&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;template&amp;lt;std::ios_base&amp;amp; (*in_type)(std::ios_base&amp;amp;) = std::dec&amp;gt;
int16_t  to_int16 (const std::string &amp;amp;in, bool strict = false) { return to_number&amp;lt;int16_t,  in_type&amp;gt;(in, strict); }

template&amp;lt;std::ios_base&amp;amp; (*in_type)(std::ios_base&amp;amp;) = std::dec&amp;gt;
uint16_t to_uint16(const std::string &amp;amp;in, bool strict = false) { return to_number&amp;lt;uint16_t, in_type&amp;gt;(in, strict); }

template&amp;lt;std::ios_base&amp;amp; (*in_type)(std::ios_base&amp;amp;) = std::dec&amp;gt;
int32_t  to_int32 (const std::string &amp;amp;in, bool strict = false) { return to_number&amp;lt;int32_t,  in_type&amp;gt;(in, strict); }

template&amp;lt;std::ios_base&amp;amp; (*in_type)(std::ios_base&amp;amp;) = std::dec&amp;gt;
uint32_t to_uint32(const std::string &amp;amp;in, bool strict = false) { return to_number&amp;lt;uint32_t, in_type&amp;gt;(in, strict); }

template&amp;lt;std::ios_base&amp;amp; (*in_type)(std::ios_base&amp;amp;) = std::dec&amp;gt;
int64_t  to_int64 (const std::string &amp;amp;in, bool strict = false) { return to_number&amp;lt;int64_t,  in_type&amp;gt;(in, strict); }

template&amp;lt;std::ios_base&amp;amp; (*in_type)(std::ios_base&amp;amp;) = std::dec&amp;gt;
uint64_t to_uint64(const std::string &amp;amp;in, bool strict = false) { return to_number&amp;lt;uint64_t, in_type&amp;gt;(in, strict); }&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;이제 진짜로 이렇게 사용할 수 있게 되는 겁니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1765946113221&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 기본 아이디어(10진수 기본) - 느슨한 사용
int32_t  d1 = to_number(&quot;-1234&quot;);
uint32_t d2 = to_number(&quot;1234&quot;);

// 템플릿을 활용한 auto 사용하기
auto d1 = to_number&amp;lt;int32_t&amp;gt;(&quot;-1234&quot;);
auto d2 = to_number&amp;lt;uint32_t&amp;gt;(&quot;1234&quot;);

// 8, 16진수 지원
auto d3 = to_number&amp;lt;int32_t, std::hex&amp;gt;(&quot;0xFF&quot;); // 255
auto d3 = to_number&amp;lt;int32_t, std::oct&amp;gt;(&quot;777&quot;);  // 511

// 진법만 지정, 타입은 추론
uint32_t n = abc::to_number&amp;lt;std::hex&amp;gt;(&quot;FF&quot;);

// 엄격한 사용, 입력값이 오류인 경우 throw를 발생합니다.
try
{
  int32_t d1 = to_number(&quot;-1234a&quot;, true);
}
catch (const std::exception &amp;amp;e)
{
  // 출력 Invalid number format: -1234a
  std::cout &amp;lt;&amp;lt; e.what() &amp;lt;&amp;lt; std::endl;
}&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;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;하찮아 보이던 문자열-&amp;gt;숫자 변환&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;막상 구현하고 보면 코드가 단순하지 않습니다. 코드도 짧지 않고 길며 모든 부분에 예외를 처리해야 하기 때문입니다. C를 사용한다면 굉장히 번거롭고 예외발생이 되지 않기 때문에 모든 라인을 검사해야 했을 겁니다.&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;&lt;span style=&quot;color: #000000;&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;있습니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>atoi</category>
      <category>C++</category>
      <category>stoi</category>
      <category>stoll</category>
      <category>template</category>
      <author>tys</author>
      <guid isPermaLink="true">https://cplusplus.tistory.com/31</guid>
      <comments>https://cplusplus.tistory.com/entry/C-%ED%8E%B8%EB%A6%AC%ED%95%98%EA%B3%A0-%EC%95%88%EC%A0%84%ED%95%9C-%EC%88%AB%EC%9E%90%EB%B3%80%ED%99%98-tonumber-%EB%A7%8C%EB%93%A4%EA%B8%B0-%EC%9C%84%ED%97%98%ED%95%9C-%EB%B2%84%EA%B7%B8-%EC%9C%A0%EB%B0%9C%EC%9E%90-atoi-%EB%B2%84%EB%A6%AC%EA%B8%B0#entry31comment</comments>
      <pubDate>Tue, 16 Dec 2025 17:19:53 +0900</pubDate>
    </item>
    <item>
      <title>C++ std::mutex보다 빠른 RCU(Lock-free) 라이브러리: cppurcu</title>
      <link>https://cplusplus.tistory.com/entry/C17-Lock-Free-RCU-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC-cppurcu</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;C++&amp;nbsp;Lock-free&amp;nbsp;library&amp;nbsp;faster&amp;nbsp;than&amp;nbsp;std::mutex:&amp;nbsp;cppurcu&lt;/span&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;읽기 작업이 많은 프로세스&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;std::mutex&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;멀티스레드 환경에서 특정 자료는 거의 읽기가 대부분 마스터 데이터와 같은 자료를 읽으려 할 때마다 단순하게는 std::mutex 또는 pthread mutex를 사용하여 다루는 데이터의 오염이나 크래시를 피해야 합니다.&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;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;간혹 생기는 업데이트 때문에 자료 접근 시의 모든 경우 lock을 사용해야 하는 것은 자원 낭비일 수 있습니다.&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;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;thread_local&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;이를 &lt;span style=&quot;text-align: start;&quot;&gt;간단히 &lt;/span&gt;해결할 수 있는 방법 중에는 thread-local-storage를 이용해 또 다른 복사본을 만들어 놓는 방법이 있습니다. 이&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;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;std::shared_ptr&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;또 이 문제를 해결하기 위해 thread_local과 shared_ptr를 같이 사용하기도 합니다. 하지만 shared_ptr의 참조 카운팅은 상당한 오버헤드 때문에 데이터가 아주 크지 않으면 mutex와의 성능 차이가 그리 크지 않습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #e8e6e3; --darkreader-inline-color: var(--darkreader-text-e8e6e3, #d8d4cf);&quot; data-ke-size=&quot;size26&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;RCU(Read Copy Update) 란?&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;그래서 읽기 작업이 압도적으로 많은 경우 RCU 패턴을 사용할 수 있습니다. RCU 패턴은 &lt;span style=&quot;text-align: start;&quot;&gt;mutex를 사용하는 것보다 4x ~ 20x 정도 빠르게 처리되어&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt; 체감할 정도의 성능 향상을 기대해 볼 수 있으며, 락프리 이므로 스레드가 늘어나더라도 mutex처럼 전체 퍼포먼스에 영향을 주지 않습니다.&lt;/span&gt;&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;읽기는 락 없이 - 락프리로 읽는 쪽은 거의 비용이 들지 않습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;쓰기는 복사 후 교체 - 새 데이터를 만들어서 포인터만 바꾸어 줍니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;안전한 메모리 회수 - 모든 읽기가 끝난 후에 옛날 데이터를 정리합니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;전통적인 뮤텍스는 읽기와 쓰기가 서로를 막지만, RCU는 새 데이터로 교체하더라도 읽기 작업을 최대한 방해받지 않도록 하는 것이 목적입니다.&lt;/span&gt;&lt;/p&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;h3 style=&quot;color: #e8e6e3; text-align: start; --darkreader-inline-color: var(--darkreader-text-e8e6e3, #d8d4cf);&quot; data-ke-size=&quot;size23&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;RCU 라이브러리&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #c8c3bc; text-align: start; --darkreader-inline-color: var(--darkreader-text-c8c3bc, #c1bcb4);&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;RCU는 커널에서만 사용되는 기술로 USER API는 따로 없습니다. 따라서 사용자 공간에서 사용할 수 있는 아래와 같은 라이브러리들이 있습니다. 또 C++ 26의 표준 라이브러리에 포함될 예정입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #c8c3bc; text-align: start; --darkreader-inline-color: var(--darkreader-text-c8c3bc, #c1bcb4);&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #c8c3bc; text-align: start; --darkreader-inline-color: var(--darkreader-text-c8c3bc, #c1bcb4);&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;그러나 우리나라는 아직도 C++17 조차 사용하지 않는 곳도 많으니 C++26은 진짜 딴 나라 이야기 일 수도 있겠죠.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #c8c3bc; text-align: start; --darkreader-inline-color: var(--darkreader-text-c8c3bc, #c1bcb4);&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; font-size: 1.25em; letter-spacing: -1px; caret-color: auto; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #c8c3bc; text-align: start; --darkreader-inline-color: var(--darkreader-text-c8c3bc, #c1bcb4);&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; font-size: 1.25em; letter-spacing: -1px; caret-color: auto; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;liburcu&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;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;전통적인 C라이브러리 입니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;신뢰성이 높습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;러닝커브가 높고 사용방법이 까다로워 실수하지 않도록 주의해야 합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;LGPL 라이센스 입니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;a style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; href=&quot;https://liburcu.org/&quot; data-darkreader-inline-color=&quot;&quot;&gt;공식 사이트&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-size: 1.25em; letter-spacing: -1px; caret-color: auto; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;folly::rcu&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;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;전통적인 RCU의 C++ 버전입니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Meta(페이스북)에서 만들고 사용하고 있으며 신뢰성이 높습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;러닝커브가 높은 편이며 제약사항은 liburcu와 비슷합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;folly전체를 빌드해야하며 boost, glog등의 의존성이 많습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Apache-2.0 라이센스 입니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;a style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; href=&quot;https://github.com/facebook/folly&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-darkreader-inline-color=&quot;&quot;&gt;github 링크&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #e8e6e3; text-align: start; --darkreader-inline-color: var(--darkreader-text-e8e6e3, #d8d4cf);&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;cppurcu&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;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;C++17 기반 header-only 라이브러리 입니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;사용법이 쉽습니다. 제약조건을 숙지해야 합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;다른 라이브러리는 필요 없습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;MIT 라이센스 입니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;a style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; href=&quot;https://github.com/ty7swkr/cppurcu&quot; data-darkreader-inline-color=&quot;&quot;&gt;github 링크&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #c8c3bc; text-align: start; --darkreader-inline-color: var(--darkreader-text-c8c3bc, #c1bcb4);&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #e8e6e3; text-align: start; --darkreader-inline-color: var(--darkreader-text-e8e6e3, #d8d4cf);&quot; data-ke-size=&quot;size23&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;RCU 메커니즘&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #c8c3bc; text-align: start; --darkreader-inline-color: var(--darkreader-text-c8c3bc, #c1bcb4);&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;읽기 작업은 락 없이 수행하고, 쓰기는 특별한 방식으로 처리합니다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start; --darkreader-inline-color: var(--darkreader-text-e8e6e3, #d8d4cf);&quot; data-ke-size=&quot;size20&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Read (읽기)&lt;/span&gt;&lt;/h4&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;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;읽기 작업은 락을 획득하지 않습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;이때 오버헤드가 거의 없습니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start; --darkreader-inline-color: var(--darkreader-text-e8e6e3, #d8d4cf);&quot; data-ke-size=&quot;size20&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Copy (복사)&lt;/span&gt;&lt;/h4&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;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;기존 데이터를 복사해 업데이트 하거나 새로운 데이터 셋을 적용합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;이때 원본은 오래된 데이터와 새 데이터 이렇게 최소 2본이 메모리에 생성 됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start; --darkreader-inline-color: var(--darkreader-text-e8e6e3, #d8d4cf);&quot; data-ke-size=&quot;size20&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Update (갱신)&lt;/span&gt;&lt;/h4&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;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;포인터를 원자적으로 바꿔서 새 데이터를 가리키게 합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;이후의 읽기 작업은 새 데이터를 보게 됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;기존에 읽기 중이던 작업들은 여전히 이전 데이터를 봅니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;여전히 이전 데이터를 볼 수 있으므로 언제 삭제해야하는지가 중요하며 아래와 같이 유예기간을 두기도 합니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start; --darkreader-inline-color: var(--darkreader-text-e8e6e3, #d8d4cf);&quot; data-ke-size=&quot;size20&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;이전 데이터 삭제&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #c8c3bc; text-align: start; --darkreader-inline-color: var(--darkreader-text-c8c3bc, #c1bcb4);&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Grace Period (유예기간): &lt;span style=&quot;text-align: left;&quot;&gt;이전 데이터를 참조하던 모든 읽기 작업이 끝날 때까지&amp;nbsp;기다린 후 삭제합니다.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; text-align: left; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Epoch-Based Reclamation (EBR): 모든 스레드가 다음 epoch로 넘어가면 이전 epoch의 데이터를 회수합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;text-align: left;&quot;&gt;Hazard Pointers: Lock-free 큐에서도 종종 사용되는 방법으로 각 쓰레드가 내가 사용하는 포인터를 기록하므로 아무도 사용하지 않는 것이 확인되면 메모리를 해제합니다.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start; --darkreader-inline-color: var(--darkreader-text-e8e6e3, #d8d4cf);&quot; data-ke-size=&quot;size20&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;사용 사례&lt;/span&gt;&lt;/h4&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;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;리눅스 커널의 네트워크 라우팅 테이블&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;파일 시스템의 디렉토리 캐시등&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;업계 표준 RCU 라이브러리: liburcu&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;리눅스 커널에 적용된 RCU를 일반 어플리케이션에서 사용할 수 있도록 개발된 라이브러리입니다. 동일한 개발자들이 만든 라이브러리로 RCU라이브러리 중에서는 가장 널리 쓰이는 RCU라이브러리이지만 학습곡선이 높고 사용하기가 까다롭습니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;이러한 난이도 때문에 잘 사용되지 못해 오히려 잘 알려지지 못한 패턴이기도 합니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;br /&gt;liburcu 예제&lt;/span&gt;&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;pre id=&quot;code_1774325467859&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;urcu.h&amp;gt;          // flavor에 따라 urcu-qsbr.h, urcu-bp.h 등
#include &amp;lt;urcu/rculist.h&amp;gt;
#include &amp;lt;map&amp;gt;
#include &amp;lt;string&amp;gt;

// RCU로 보호할 데이터 - rcu_head 필수
struct MapData {
    std::map&amp;lt;std::string, std::string&amp;gt; data;
    struct rcu_head rcu_head;
};

// 전역 RCU 포인터
static MapData *g_data;

// ── 초기화 ──
void init() {
    // 스레드마다 호출 필요,
    // ⚠️ 빼먹으면 정상 동작 보장 X
    // ⚠️ 다른 콜백 체인에서 중복 호출시 정상 동작 보장 X
    rcu_register_thread();

    auto *initial = new MapData();
    rcu_assign_pointer(g_data, initial);
}

// ── 읽기 (lock-free) ──
void read_example() {
    // ⚠️ 중복 호출이더라도 rcu_read_unlock과 짝을 맞추어 호출해야함
    rcu_read_lock();

    MapData *d = rcu_dereference(g_data);
    if (d-&amp;gt;data.count(&quot;key&quot;) &amp;gt; 0) {
        // 여기서 사용
        // ⚠️ rcu_read_unlock() 넘어서 포인터 들고 있으면 안 됨
    }

    // ⚠️ 중복 호출이더라도 rcu_read_lock과 짝을 맞추어 호출해야함
    rcu_read_unlock();
}

// ── 쓰기 (업데이트) ──
static void free_cb(struct rcu_head *head) {
    MapData *old = caa_container_of(head, MapData, rcu_head);
    delete old;
}

void update_example() {
    auto *new_data = new MapData();
    (*new_data).data[&quot;key&quot;] = &quot;value&quot;;

    MapData *old = g_data;
    rcu_assign_pointer(g_data, new_data);

    // 방법 1: 동기식 - 모든 reader 끝날 때까지 블록
    // ⚠️ 사용하는 스레드에서 업데이트시 데드락
    // synchronize_rcu();
    // delete old;

    // 방법 2: 비동기식 - 나중에 콜백으로 해제
    call_rcu(&amp;amp;old-&amp;gt;rcu_head, free_cb);
}

// ── 정리 ──
void cleanup() {
    // ⚠️ 다른 콜백 체인에서 중복 호출시 비정상 동작
    rcu_unregister_thread();
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;위 코드는&amp;nbsp; '⚠️' 과 같은 문제가 사용을 어렵게 만듭니다. 이러한 문제를 cppurcu는 구조적으로 발생할 수 없도록 만들어졌습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;cppurcu 라이브러리&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;liburcu가 업계 표준이지만 학습 난이도가 매우 높고 사용자 실수가 빈번할 수 인터페이스 구조이기 때문에 만들어진 라이브러리입니다. &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot;&gt;RCU 의 성능은 유지하되 사용은 쉽고 오용(misuse)은 방지하도록 설계되었습니다.&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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;cppurcu 예제&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1774324530552&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;cppurcu/cppurcu.h&amp;gt;
#include &amp;lt;memory&amp;gt;
#include &amp;lt;string&amp;gt;
#include &amp;lt;map&amp;gt;

// Create a storage with initial data
auto storage = cppurcu::create(std::make_shared&amp;lt;std::map&amp;lt;std::string, std::string&amp;gt;&amp;gt;());

// Read (lock-free) - returns a guard object
{
  auto data = storage.load(); // auto:cppurcu::guard&amp;lt;T&amp;gt;, version 1
  if (data-&amp;gt;count(&quot;key&quot;) &amp;gt; 0)
  {
    // Use the data
    auto data2 = storage.load(); // version 1
  }
}

auto new_data = std::make_shared&amp;lt;std::map&amp;lt;std::string, std::string&amp;gt;&amp;gt;();
(*new_data)[&quot;key&quot;] = &quot;value&quot;;

// Update (immediate, delegates reclamation to std::shared_ptr)
storage = new_data; // or storage.update(new_data);&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;liburcu vs cppurcu 성능비교&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://github.com/ty7swkr/cppurcu/blob/main/docs/PERFORMANCE.md&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot; data-darkreader-inline-color=&quot;&quot;&gt;https://github.com/ty7swkr/cppurcu/blob/main/docs/PERFORMANCE.md&lt;/a&gt;&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;구성요소&lt;/span&gt;&lt;/h3&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;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;storage&amp;lt;T&amp;gt;: source와 local을 가지고 있는 사용자 인터페이스&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;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;내부 클래스들&amp;nbsp;&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;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;source&amp;lt;T&amp;gt;: 사용자 데이터 원본 T를 가지고 있는 클래스&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;local&amp;lt;T&amp;gt;: 사용자 데이터의 버전과, 사본을 shared_ptr로 가지며 raw포인터를 thread-local에 캐싱&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;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;guard&amp;lt;T&amp;gt;: thread-local 캐싱 데이터를 참조로 가지고 있는 객체로 스냅샷 격리를 제공하는 클래스&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;reclaimer_thread: 원본이 아닌 이전 데이터의 사용해제만을 담당하는 스레드 클래스&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;color: #000000; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot;&gt;모든 호출은 thread-safe입니다.&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;cppurcu 메커니즘&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;원본 데이터를 보관하는 source&amp;lt;T&amp;gt; 와 thread-local caching을 담당하는 local&amp;lt;T&amp;gt; 의 두 구성으로 동작하며 이 두 클래스는 인터페이스 클래스인 storage에 의해 호출됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start; --darkreader-inline-color: var(--darkreader-text-e8e6e3, #d8d4cf);&quot; data-ke-size=&quot;size20&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Reader thread: 데이터 로드&lt;/span&gt;&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;현재 데이터: Fast path (Lock-free)&lt;/span&gt;&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;source&amp;lt;T&amp;gt; 의 원본 데이터를 참조한 데이터들은 local&amp;lt;T&amp;gt; 에서 관리합니다. local&amp;lt;T&amp;gt; 의 역할은 참조된 데이터들을 thread-local 캐시에 저장해 두고 사용자가 storage&amp;lt;T&amp;gt;::load() 로 요청할 때 저장된 원본 데이터의 guard&amp;lt;T&amp;gt; 를 반환합니다. &lt;br /&gt;&lt;br /&gt;반한 된 guard&amp;lt;T&amp;gt; 는 local&amp;lt;T&amp;gt; 의 thread-local 데이터를 참조하고 있는 객체이며 이 객체는 사용자 데이터의 원시포인터를 반환하여 줍니다. Fast path의 모든 과정은 Lock 없이 동작합니다.&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;새로운 데이터: Slow path&lt;/span&gt;&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;[ reclaimer_thread를 사용하지 않은 경우 ]&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;thread-local 캐시에 새로운 원본 데이터의 버전, shared_ptr, raw 포인터를 저장하고 raw 포인터를 반환합니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;이때 이전 데이터 모든 참조가 0이 되면 해당 스레드에서 메모리 해제가 발생합니다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;[ reclaimer_thread를 사용한 경우 ]&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;reclaimer_thread를 사용하지 않은 경우와 동일하지만 이전 원본의 shared_ptr을 업데이트 시 미리 reclaimer_thread로 보냈기 때문에 리더 스레드에서 메모리가 해제되지 않고 reclaimer_thread에서 해제되도록 합니다.&lt;/span&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;&lt;span style=&quot;color: #000000; text-align: start; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;이전 데이터에 대한 회수전략을 다른 RCU라이브러리 들과 다르게 C++의 shared_ptr에 위임하여 사용합니다. 이 과정 덕분에 퍼포먼스는 충분히 유지하면서도 구현과 인터페이스가 매우 간단해졌습니다.&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;color: #000000; text-align: start; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Writer thread: &lt;span style=&quot;text-align: start;&quot;&gt;데이터 업데이트&lt;/span&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;source&amp;lt;T&amp;gt; 클래스는 사용자 데이터 T를 shared_ptr형태로 갖게 되며 local&amp;lt;T&amp;gt; 는 이를 참조합니다. source&amp;lt;T&amp;gt; 는 데이터 먼저 업데이트한 후 버전을 업데이트합니다. &lt;/span&gt;&lt;span style=&quot;color: #000000; letter-spacing: 0px; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;local&amp;lt;T&amp;gt;(읽기작업)는 반대로 version을 먼저 확인하도록 합니다. 이 과정은 메모리 순서 보장(메모리 배리어(memory barrier) 또는 메모리 펜스(memory fence))을 통해 수행됩니다.&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;사용하기&lt;/span&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;기본 사용법&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;reclaimer_thread를 사용하지 않는 아주 기본적인 사용법입니다 Modern C++으로 구현되어 있어 사용법이 liburcu뿐 아니라 std::mutex 보다 더 단순합니다.&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_1761640233215&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;cppurcu/cppurcu.h&amp;gt;
......

// Main Thread
auto init_config = std::make_shared&amp;lt;Config&amp;gt;();

// rcu 저장소 생성시에는 초기값(필수)을 넣어줍니다.
auto config_storage = cppurcu::create(init_config);


// Writer Thread: 저장소 업데이트
std::shared_ptr&amp;lt;Config&amp;gt; config = read(....);
// 저장소에 새 원본 데이터를 업데이트 합니다.
config_storage = config; // or config_storage.update(config);


// Reader Thread: 저장소에서 값을 가져옵니다.
auto config = config_storage.load(); // auto는 cppurcu::guard&amp;lt;Config&amp;gt; 타입이 됩니다.
// config를 사용합니다.
std::cout &amp;lt;&amp;lt; config-&amp;gt;date &amp;lt;&amp;lt; &quot;:&quot; &amp;lt;&amp;lt; config-&amp;gt;version &amp;lt;&amp;lt; &quot;:&quot; &amp;lt;&amp;lt; config-&amp;gt;period &amp;lt;&amp;lt; std::endl;&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;별도로 reclaimer_thread&lt;span style=&quot;text-align: start;&quot;&gt;를 사용하지 않기 때문에 Reader 스레드에서 T 객체는 소멸될 수 있습니다. T 객체의 소멸이 Reader 스레드에서는 부담이 될 수 있기 때문에 reclaimer_thread&lt;span style=&quot;text-align: start;&quot;&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;h4 style=&quot;color: #e8e6e3; --darkreader-inline-color: var(--darkreader-text-e8e6e3, #d8d4cf);&quot; data-ke-size=&quot;size20&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Snapshot Isolation: 일관된 읽기 guard&amp;lt;T&amp;gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;스냅샷 격리는 cppurcu의 핵심 기능 중 하나로, 동일한 스레드 내 동일한 스코프에서 여러 번의 storage&amp;lt;T&amp;gt;::load() 호출이 복잡한 호출 체인에 걸쳐 발생하거나, 심지어 읽기 중에 다른 스레드에서 storage의 데이터를 업데이트를 하더라도 모든 읽기 작업이 같은 버전의 데이터를 보도록 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1762757880828&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  auto data = storage.load();    // Snapshot version 1
  {
    auto data1 = storage.load(); // Snapshot version 1
  } // data1 Guard destroyed
} // data Guard destroyed

storage.update(new_data);      // Update to version 2

{
  auto data = storage.load();  // Loads version 2
}&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;guard&amp;lt;T&amp;gt; 는 -&amp;gt;연산자로 사용자 데이터에 접근할 수 있으며 항상 읽기(&lt;span style=&quot;text-align: start;&quot;&gt;const)&lt;/span&gt;&amp;nbsp;전용입니다.&amp;nbsp;guard&amp;lt;T&amp;gt; 객체는 복사/이동이 모두 불가하므로 스레드 밖으로 나갈 수도 없고, 컨테이너에 넣을 수도 없습니다. storage::load&amp;lt;T&amp;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;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;이는 동일한 스레드 내 스코프 안에서(RAII) 동일한 데이터가 관찰되도록 보장하는 안전장치입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #e8e6e3; --darkreader-inline-color: var(--darkreader-text-e8e6e3, #d8d4cf);&quot; data-ke-size=&quot;size20&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Multiple Storages Snapshot: 여러 스토리지를 한 번에 load 하는 guard_pack&amp;lt;...T&amp;gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;앞서 이야기한 guard&amp;lt;T&amp;gt;를 이용한 Snapshot Isolation은 1번에 1개의 guard만을 만들어 냅니다. 그런데 여러 스토리지를 생성하고 load 해야 한다면 실수가 잦을 수 있고 가독성도 떨어집니다. 그래서 제공되는 유틸리티 클래스입니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1763813393402&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;cppurcu/cppurcu.h&amp;gt;

// Multiple data storages
auto storageA = cppurcu::create(...);
auto storageB = cppurcu::create(...);
auto storageC = cppurcu::create(...);

// Load all at once - ensures same snapshot point
const auto &amp;amp;[a, b, c] = cppurcu::load(storageA, storageB, storageC);

// All reads within this scope see consistent data
// even if updates occur from other threads
a-&amp;gt;lookup(...);
b-&amp;gt;query(...);
c-&amp;gt;find(...);&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;이렇게 구조화된 바인딩(Structured binding)으로 값을 받을 수 있습니다.&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;특정 스코프 안에서는 일관된 스냅샷을 위해 다음과 같이 사용할 수도 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1763813728028&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  auto pack = cppurcu::make_guard_pack(storageA.load(), storageB.load(), storageC.load());
  // or
  auto pack = cppurcu::load(storageA, storageB, storageC); // a=v1, b=v2, c=v1
  .....
  // 이 안에서 storageA와 storageC를 사용하더라도 pack이 storageA,B,C의 일관성을 보장합니다.
  my_class.calculate();
  .....
}

my_class::calculate()
{
  // 여전히 a=v1, b=v2, c=v1
  auto a = storageA.load(); // a=v1
  auto b = storageB.load(); // b=v2
  auto c = storageC.load(); // c=v1
}&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;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;guard_pack 각 저장소에서 스냅샷을 로드하고 동일한 범위 내에서 동일한 버전을 보도록 '스냅샷 격리' 상태를 유지하지만 여러 저장소에 걸쳐 글로벌 트랜잭션 원자성을 제공하는 기능은 아닙니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;각 저장소는 여전히 독립적으로 버전이 관리됩니다. guard_pack은 편리한 다중 저장소 스냅샷 로딩을 위한 RAII 도우미이지만, 저장소 간 트랜잭션 메커니즘은 아닙니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;guard_pack 역시 guard&amp;lt;T&amp;gt;와 마찬가지로&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&amp;nbsp;복사/이동이 모두 불가하며 동일하게 스레드 밖으로 나갈 수도 없고, 컨테이너에 넣을 수도 없습니다.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;color: #e8e6e3; --darkreader-inline-color: var(--darkreader-text-e8e6e3, #d8d4cf);&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;reclaimer_thread를 이용한 회수&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;reclaimer_thread는 Reader thread가 새로운 데이터를 가져올 때 이전 데이터를 reclaimer_thread&lt;span style=&quot;text-align: start;&quot;&gt;로 전달하기 때문에 이 과정에 약간의 오버헤드가 추가됩니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1761645230658&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;cppurcu/cppurcu.h&amp;gt;

// reclaimer_thread 쓰레드를 생성합니다.
auto reclaimer = std::make_shared&amp;lt;cppurcu::reclaimer_thread&amp;gt;();

// storage 객체 생성시 두번째 인자로 reclaimer를 전달합니다.
auto storage1 = cppurcu::create(
                    std::make_shared&amp;lt;std::set&amp;lt;std::string&amp;gt;&amp;gt;(),
                    reclaimer);


// storage&amp;lt;T&amp;gt;의 T가 다르더라도 동일한 reclaimer_thread 객체를 전달할 수 있습니다.
auto storage2 = cppurcu::create(
                    std::make_shared&amp;lt;std::vector&amp;lt;int&amp;gt;&amp;gt;(),
                    reclaimer);


// Write thread: Somewhere in the source...(update)
storage1 = new_data;

// Reader thread
auto data = storage1.load();&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;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;storage&amp;lt;T&amp;gt; 인스턴스의 수명 관리&lt;/span&gt;&lt;/h3&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;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;인스턴스 storage&amp;lt;T&amp;gt;는 그것을 사용하는 모든 스레드보다 오래 지속되어야 합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;storage스레드가 여전히 액세스 하는 동안 삭제하면 정의되지 않은 동작이 발생합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;일반적으로 storage전역, 정적 또는 장기 멤버 변수로 선언합니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;사용자 책임&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;C++17 표준 라이브러리로 개발된, 의존성 없는, 작은 header-only 라이브러리로서 성능과 C++사용성은 직관적이나, 아직 새로운 라이브러리이므로 널리 사용되지 않은 점은 사전에 충분히 고려하셔야 합니다. 다만 의존성이 없고 소스가 작아 디버깅이 쉽다는 점은 이점입니다.&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;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;참고: 이 라이브러리는 현업에서 적용하여 사용 중인 라이브러리입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1761808741348&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 - ty7swkr/cppurcu: C++17 user-space RCU with versioned thread-local caching&quot; data-og-description=&quot;C++17 user-space RCU with versioned thread-local caching - ty7swkr/cppurcu&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/ty7swkr/cppurcu&quot; data-og-url=&quot;https://github.com/ty7swkr/cppurcu&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bCt80T/hyZMUSqytO/FHIQHnXxyPnVgNQU6RR0mk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/P3JKd/hyZMJXFDUw/9jM4H5GPKUdTQENuKbKTAk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/ty7swkr/cppurcu&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/ty7swkr/cppurcu&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bCt80T/hyZMUSqytO/FHIQHnXxyPnVgNQU6RR0mk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/P3JKd/hyZMJXFDUw/9jM4H5GPKUdTQENuKbKTAk/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 - ty7swkr/cppurcu: C++17 user-space RCU with versioned thread-local caching&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;C++17 user-space RCU with versioned thread-local caching - ty7swkr/cppurcu&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;</description>
      <author>tys</author>
      <guid isPermaLink="true">https://cplusplus.tistory.com/29</guid>
      <comments>https://cplusplus.tistory.com/entry/C17-Lock-Free-RCU-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC-cppurcu#entry29comment</comments>
      <pubDate>Tue, 28 Oct 2025 19:17:23 +0900</pubDate>
    </item>
    <item>
      <title>C++ 소프트웨어 아키텍쳐와 모델링</title>
      <link>https://cplusplus.tistory.com/entry/CPP-%EC%86%8C%ED%94%84%ED%8A%B8%EC%9B%A8%EC%96%B4-%EC%95%84%ED%82%A4%ED%85%8D%EC%B3%90%EC%99%80-%EB%AA%A8%EB%8D%B8%EB%A7%81</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;잘못된 소프트웨어 아키텍처에서의 모델링이 미치는 영향은 큽니다.&amp;nbsp; 현실세계의 모델과, 소프트웨어 아키텍처가 가지는 모델의 괴리가 발생할 때, 요구사항&amp;middot;장애 분석&amp;middot;운영&lt;span style=&quot;text-align: start;&quot;&gt;&amp;middot;&lt;/span&gt;대화에서 동일한 용어에 서로 다른 논리를 가리키는 문제가 반복됩니다. 데이터 정합성 검증과 기능 수정, 해석에서도 각자의 모델에 대한 불필요한 대화와 이해가 수반되어 작업의 지연과 장애가 발생하게 됩니다. 이런 불필요한 과정들은 이해 관계자 모두를 지치게 만드는 요인들입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;

            &lt;figure class=&quot;unsupported component-kakaotv&quot; contenteditable=&quot;false&quot; style=&quot;background:#000;margin:16px 0;min-height:72px;padding:10px 16px;display:flex;align-items:center;justify-content:center;text-align:center;box-sizing:border-box;width:100%;max-width:100%;&quot;&gt;
                &lt;p contenteditable=&quot;false&quot; style=&quot;margin:0;color:#8a8a8a;font-size:13px;line-height:1.6;user-select:none;pointer-events:none;&quot;&gt;동영상 서비스가 종료되어 해당 콘텐츠를 재생할 수 없습니다.&lt;/p&gt;
            &lt;/figure&gt;
        
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;데이터베이스와 모델링&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;개념적 모델링 -&amp;gt; 논리적 모델링 -&amp;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;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;이때, 현실세계의 비즈니스 규칙이나 제약조건들도 완전히 반영되어야 합니다. 현실의 규칙이 데이터베이스에 제대로 반영되지 않으면 데이터 불일치나 비즈니스 로직 오류가 발생할 수 있기 때문에, 개념적 모델링부터 물리적 구현까지 일관성 있게 유지하는 것이 매우 중요합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;소프트웨어 아키텍처와 모델링&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;프로그래밍에서의 설계도 데이터베이스의 모델링과 마찬가지입니다.&lt;/span&gt; 현실세계의 정보를 모델링하여 사용하려고 노력하는 이유는 매우 직관적이고 효율적이기 때문입니다. &quot;&lt;/span&gt;&lt;span style=&quot;text-align: start; --darkreader-inline-bgcolor: var(--darkreader-background-17191a, #121415);&quot;&gt;같은 단어로 같은 것을 상상&quot; 하는 &lt;/span&gt;&lt;span style=&quot;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;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;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;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;프로젝트에서의 이런&amp;nbsp;&lt;span style=&quot;text-align: start; --darkreader-inline-bgcolor: var(--darkreader-background-1d1f21, #17191a);&quot;&gt;모델과 현실이 불일치는 각자 &quot;같은 단어로 다른 것을 상상&quot; 하게 만들며. &lt;/span&gt;이런 오해와 혼선이&lt;span style=&quot;text-align: start;&quot;&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;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;현실적인 고려사항&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&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 style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;다만, 이 글에서 이야기하고자 하는 것은&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;기본적인 소프트웨어 아키텍처와 모델링 관계에 대한 이해 없이&lt;br /&gt;Usecase 구현에만 집중하는 것은 프로젝트 참여자 모두를 힘들게 만들기 때문입니다.&lt;/span&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;a style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; href=&quot;https://product.kyobobook.co.kr/detail/S000001033082&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot; data-darkreader-inline-color=&quot;&quot;&gt;https://product.kyobobook.co.kr/detail/S000001033082&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1758678027282&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;클린 아키텍처: 소프트웨어 구조와 설계의 원칙 | 로버트 C. 마틴 - 교보문고&quot; data-og-description=&quot;클린 아키텍처: 소프트웨어 구조와 설계의 원칙 | 살아있는 전설이 들려주는 실용적인 소프트웨어 아키텍처 원칙 소프트웨어 아키텍처의 보편 원칙을 적용하면 소프트웨어 수명 전반에서 개발&quot; data-og-host=&quot;product.kyobobook.co.kr&quot; data-og-source-url=&quot;https://product.kyobobook.co.kr/detail/S000001033082&quot; data-og-url=&quot;https://product.kyobobook.co.kr/detail/S000001033082&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/gAJ8E/hyZJt3cWFV/iStAdHEcGHMCnUcTzPO6kk/img.jpg?width=458&amp;amp;height=600&amp;amp;face=0_0_458_600,https://scrap.kakaocdn.net/dn/C706A/hyZJtWpSuq/wAm88m1g9oZpHTV6Ra4O21/img.jpg?width=458&amp;amp;height=600&amp;amp;face=0_0_458_600&quot;&gt;&lt;a href=&quot;https://product.kyobobook.co.kr/detail/S000001033082&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://product.kyobobook.co.kr/detail/S000001033082&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/gAJ8E/hyZJt3cWFV/iStAdHEcGHMCnUcTzPO6kk/img.jpg?width=458&amp;amp;height=600&amp;amp;face=0_0_458_600,https://scrap.kakaocdn.net/dn/C706A/hyZJtWpSuq/wAm88m1g9oZpHTV6Ra4O21/img.jpg?width=458&amp;amp;height=600&amp;amp;face=0_0_458_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;클린 아키텍처: 소프트웨어 구조와 설계의 원칙 | 로버트 C. 마틴 - 교보문고&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;클린 아키텍처: 소프트웨어 구조와 설계의 원칙 | 살아있는 전설이 들려주는 실용적인 소프트웨어 아키텍처 원칙 소프트웨어 아키텍처의 보편 원칙을 적용하면 소프트웨어 수명 전반에서 개발&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;product.kyobobook.co.kr&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;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;a style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; href=&quot;https://martinfowler.com/tags/domain%20driven%20design.html&quot; data-darkreader-inline-color=&quot;&quot;&gt;https://martinfowler.com/tags/domain%20driven%20design.html&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1758678236700&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;domain driven design&quot; data-og-description=&quot;When programming, I often find it's useful to represent things as a compound. A 2D coordinate consists of an x value and y value. An amount of money consists of a number and a currency. A date range consists of start and end dates, which themselves can be &quot; data-og-host=&quot;martinfowler.com&quot; data-og-source-url=&quot;https://martinfowler.com/tags/domain%20driven%20design.html&quot; data-og-url=&quot;https://martinfowler.com/tags/domain%20driven%20design.html&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dc1Wu5/hyZJxYR2n1/KDBP4MN45dveXo2nlVENd1/img.png?width=772&amp;amp;height=478&amp;amp;face=0_0_772_478,https://scrap.kakaocdn.net/dn/gdTy2/hyZJEJI1xq/v4XSmyXphnR5QLLz0IaC10/img.png?width=637&amp;amp;height=464&amp;amp;face=0_0_637_464,https://scrap.kakaocdn.net/dn/bpKfOa/hyZJtB4HN4/jKQUNkNok4XIWPtm6lweIk/img.png?width=560&amp;amp;height=300&amp;amp;face=0_0_560_300&quot;&gt;&lt;a href=&quot;https://martinfowler.com/tags/domain%20driven%20design.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://martinfowler.com/tags/domain%20driven%20design.html&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dc1Wu5/hyZJxYR2n1/KDBP4MN45dveXo2nlVENd1/img.png?width=772&amp;amp;height=478&amp;amp;face=0_0_772_478,https://scrap.kakaocdn.net/dn/gdTy2/hyZJEJI1xq/v4XSmyXphnR5QLLz0IaC10/img.png?width=637&amp;amp;height=464&amp;amp;face=0_0_637_464,https://scrap.kakaocdn.net/dn/bpKfOa/hyZJtB4HN4/jKQUNkNok4XIWPtm6lweIk/img.png?width=560&amp;amp;height=300&amp;amp;face=0_0_560_300');&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;domain driven design&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;When programming, I often find it's useful to represent things as a compound. A 2D coordinate consists of an x value and y value. An amount of money consists of a number and a currency. A date range consists of start and end dates, which themselves can be&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;martinfowler.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;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: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;다음에는 간략한 모델링 예제를 통해 C++로 어떻게 자료구조를 모델링하고 동시에 동시성을 어떻게 해결해 가는지 작성해볼 생각입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>소프트웨어</category>
      <category>아키텍처</category>
      <author>tys</author>
      <guid isPermaLink="true">https://cplusplus.tistory.com/27</guid>
      <comments>https://cplusplus.tistory.com/entry/CPP-%EC%86%8C%ED%94%84%ED%8A%B8%EC%9B%A8%EC%96%B4-%EC%95%84%ED%82%A4%ED%85%8D%EC%B3%90%EC%99%80-%EB%AA%A8%EB%8D%B8%EB%A7%81#entry27comment</comments>
      <pubDate>Tue, 23 Sep 2025 20:56:57 +0900</pubDate>
    </item>
    <item>
      <title>libcurl을 이용한 간단한 C++ SFTP Client</title>
      <link>https://cplusplus.tistory.com/entry/C-libcurl%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EA%B0%84%EB%8B%A8%ED%95%9C-SFTP-%ED%81%B4%EB%9D%BC%EC%9D%B4%EC%96%B8%ED%8A%B8</link>
      <description>&lt;div&gt;
&lt;div&gt;
&lt;div data-message-model-slug=&quot;gpt-5&quot; data-message-id=&quot;e5d51a42-2118-4fe2-b942-bb678b7477d9&quot; data-message-author-role=&quot;assistant&quot;&gt;
&lt;h2 style=&quot;color: #000000; text-align: start; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-ke-size=&quot;size26&quot; data-start=&quot;48&quot; data-end=&quot;71&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;클래싱 포인트&lt;/span&gt;&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot; data-start=&quot;72&quot; data-end=&quot;457&quot; data-is-last-node=&quot;&quot; data-is-only-node=&quot;&quot;&gt;
&lt;li data-start=&quot;72&quot; data-end=&quot;147&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;SFTPCurl / SFTPCurlTemplate&amp;nbsp;클래스를 정의해 libcurl 옵션 설정과 에러 처리 캡슐화&lt;/span&gt;&lt;/li&gt;
&lt;li data-start=&quot;72&quot; data-end=&quot;147&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;CRTP(Curiously Recurring Template Pattern)을 사용해 템플릿으로 추상화, 파생 클래스를 정적으로 확장&lt;/span&gt;&lt;/li&gt;
&lt;li data-start=&quot;148&quot; data-end=&quot;245&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Fluent Interface (메서드 체이닝)&amp;nbsp;패턴을 사용하여 user(...).password(...).url(...) 와 같은 인터페이스 설계&lt;/span&gt;&lt;/li&gt;
&lt;li data-start=&quot;246&quot; data-end=&quot;314&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;단일 책임 원칙(SRP)와 인터페이스 분리 원칙(ISP)을 적용하여 업로드, 다운로드 기능 클래스 분리&lt;/span&gt;&lt;/li&gt;
&lt;li data-start=&quot;315&quot; data-end=&quot;388&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;예외 클래스(SFTPCurl::exception) 로 CURL 오류를 객체 지향적으로 처리&lt;/span&gt;&lt;/li&gt;
&lt;li data-start=&quot;315&quot; data-end=&quot;388&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;프록시 패턴을 이용한 실행(perform())&lt;/span&gt;&lt;/li&gt;
&lt;li data-start=&quot;389&quot; data-end=&quot;457&quot; data-is-last-node=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;RAII를 활용한 스마트 포인터(unique_ptr)와 ScopeExit를 사용해 curl 핸들 및 파일 리소스를 안전하게 관리&lt;/span&gt;&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;시작&lt;/span&gt;&lt;/h2&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-size: 16px; letter-spacing: 0px; color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;libcurl은 인기 있는 오픈소스 중 하나입니다. ftp나 http를 사용하는 대부분의 프로젝트에서는 빠지지 않고 사용되는 오픈소스이기도 합니다. 그래서 libcurl 중 sftp부분만을 보다 쉽게 사용할 수 있도록 랩핑해 보려고 합니다.&lt;/span&gt;&lt;/h3&gt;
&lt;/div&gt;

            &lt;figure class=&quot;unsupported component-kakaotv&quot; contenteditable=&quot;false&quot; style=&quot;background:#000;margin:16px 0;min-height:72px;padding:10px 16px;display:flex;align-items:center;justify-content:center;text-align:center;box-sizing:border-box;width:100%;max-width:100%;&quot;&gt;
                &lt;p contenteditable=&quot;false&quot; style=&quot;margin:0;color:#8a8a8a;font-size:13px;line-height:1.6;user-select:none;pointer-events:none;&quot;&gt;동영상 서비스가 종료되어 해당 콘텐츠를 재생할 수 없습니다.&lt;/p&gt;
            &lt;/figure&gt;
        
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-ke-size=&quot;size26&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;C 스타일 사용예제&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;위 예제를 C의 콜백(스트림) 형태로 사용한다면 아래와 같이 작성할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #fafafa; color: #333333; --darkreader-inline-bgcolor: var(--darkreader-background-fafafa, #1b1d1e); --darkreader-inline-color: var(--darkreader-text-333333, #c8c3bc);&quot; data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot; data-darkreader-inline-bgcolor=&quot;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;&lt;br /&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1766640096349&quot; class=&quot;reasonml&quot; style=&quot;background-color: #f8f8f8; color: #383a42; --darkreader-inline-bgcolor: var(--darkreader-background-f8f8f8, #1c1e1f); --darkreader-inline-color: var(--darkreader-text-383a42, #c1bcb4);&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;cpp&quot; data-darkreader-inline-bgcolor=&quot;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;sys/stat.h&amp;gt;
#include &amp;lt;curl/curl.h&amp;gt;

static size_t read_callback(void *ptr, size_t size, size_t nmemb, void *stream)
{
  FILE *file = (FILE *)stream;
  size_t retcode = fread(ptr, size, nmemb, file);
  return retcode;
}

int upload_file_with_filepointer(FILE *file, const char *remote_url, const char *auth)
{
  CURL *curl;
  CURLcode res;
  long file_size;
  
  // 파일 크기 구하기
  fseek(file, 0L, SEEK_END);
  file_size = ftell(file);
  fseek(file, 0L, SEEK_SET);
  
  curl = curl_easy_init();
  if (curl != NULL) 
  {
    // SFTP URL 설정
    res = curl_easy_setopt(curl, CURLOPT_URL, remote_url);
    if (res != CURLE_OK) 
    {
      fprintf(stderr, &quot;URL 설정 실패: %s\n&quot;, curl_easy_strerror(res));
      curl_easy_cleanup(curl);
      return -1;
    }
    
    // 인증 정보
    res = curl_easy_setopt(curl, CURLOPT_USERPWD, auth);
    if (res != CURLE_OK) 
    {
      fprintf(stderr, &quot;인증 정보 설정 실패: %s\n&quot;, curl_easy_strerror(res));
      curl_easy_cleanup(curl);
      return -1;
    }
    
    // 업로드 모드
    res = curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
    if (res != CURLE_OK) 
    {
      fprintf(stderr, &quot;업로드 모드 설정 실패: %s\n&quot;, curl_easy_strerror(res));
      curl_easy_cleanup(curl);
      return -1;
    }
    
    // 파일 객체 직접 전달
    res = curl_easy_setopt(curl, CURLOPT_READDATA, file);
    if (res != CURLE_OK) 
    {
      fprintf(stderr, &quot;READDATA 설정 실패: %s\n&quot;, curl_easy_strerror(res));
      curl_easy_cleanup(curl);
      return -1;
    }
    
    res = curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback);
    if (res != CURLE_OK) 
    {
      fprintf(stderr, &quot;READFUNCTION 설정 실패: %s\n&quot;, curl_easy_strerror(res));
      curl_easy_cleanup(curl);
      return -1;
    }
    
    // 파일 크기 설정
    res = curl_easy_setopt(curl, CURLOPT_INFILESIZE, file_size);
    if (res != CURLE_OK) 
    {
      fprintf(stderr, &quot;파일 크기 설정 실패: %s\n&quot;, curl_easy_strerror(res));
      curl_easy_cleanup(curl);
      return -1;
    }
    
    // SSH 호스트 키 검증 비활성화
    res = curl_easy_setopt(curl, CURLOPT_SSH_KNOWNHOSTS, NULL);
    if (res != CURLE_OK) 
    {
      fprintf(stderr, &quot;SSH_KNOWNHOSTS 설정 실패: %s\n&quot;, curl_easy_strerror(res));
      curl_easy_cleanup(curl);
      return -1;
    }
    
    // 업로드 실행
    res = curl_easy_perform(curl);
    
    if(res != CURLE_OK) 
    {
      fprintf(stderr, &quot;업로드 실패: %s\n&quot;, curl_easy_strerror(res));
      curl_easy_cleanup(curl);
      return -1;
    }
    
    curl_easy_cleanup(curl);
    return 0;
  }
  
  return -1;
}

int main(void)
{
  FILE *fp;
  
  curl_global_init(CURL_GLOBAL_DEFAULT);
  
  // 파일 열기
  fp = fopen(&quot;localfile.txt&quot;, &quot;rb&quot;);
  if(!fp) 
  {
    printf(&quot;파일을 열 수 없어요\n&quot;);
    return 1;
  }
  
  // 파일 객체로 업로드
  if(upload_file_with_filepointer(fp, &quot;sftp://example.com/upload/remotefile.txt&quot;, &quot;username:password&quot;) == 0) 
  {
    printf(&quot;파일 업로드 완료!\n&quot;);
  }
  else 
  {
    printf(&quot;파일 업로드 실패!\n&quot;);
  }
  
  fclose(fp);
  curl_global_cleanup();
  return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;코드를 보면 사용자가 이것저것 챙겨야 할 옵션이 많습니다. 옵션도 if문으로 모두 검사해야 합니다.&amp;nbsp;이렇게 코드가 길고 반복되다 보면 실수할 가능성이 높아지고 가독성도 떨어집니다. 결국 &lt;span style=&quot;text-align: start; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&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;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;반면 C++에서는 직관적으로 접근하도록 하여 비즈니스 로직에만 집중하도록 만들 수 있습니다. 예외처리는 클래스 내에서 자동으로 처리되도록 하고, 예외시 try-catch 구문으로 아래에서 사용자가 알 수 있도록 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #e8e6e3; --darkreader-inline-color: var(--darkreader-text-e8e6e3, #d8d4cf);&quot; data-ke-size=&quot;size26&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;C++ 인터페이스로 구상해 보기&lt;/span&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1766635168525&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  try
  {
    SFTPUploader sftp;
    //////////////////////////////////////////////////////////////
    // 기본 설정
    if (sftp.verify_host(false)
            .username(&quot;user&quot;)
            .password(&quot;pass&quot;)
            .connect_timeout(30)    // 연결 타임아웃 30초
            .url     (&quot;sftp://server.com/backup/file.zip&quot;)
            .exists  () == true)
    {
      std::cout &amp;lt;&amp;lt; &quot;파일이 이미 존재해요&quot; &amp;lt;&amp;lt; std::endl;
      return;
    }

    ///////////////////////////////////////////////////////////////
    // 로컬파일 업로드.
    sftp.upload(&quot;local_backup.zip&quot;).perform();
    std::cout &amp;lt;&amp;lt; &quot;새 파일 업로드 완료&quot; &amp;lt;&amp;lt; std::endl;

    ///////////////////////////////////////////////////////////////
    // 람다함수를 이용한 콜백으로 업로드.
    std::string data = &quot;Hello, SFTP World!&quot;;
    size_t      pos = 0;
    sftp.upload([&amp;amp;](void *target, size_t target_size) -&amp;gt; size_t
                {
                   size_t remaining = data.size() - pos;
                   size_t to_copy   = std::min(remaining, target_size);

                   if (to_copy == 0) return to_copy;

                   memcpy(target, data.data() + pos, to_copy);
                   pos += to_copy;

                   return to_copy;
               })
        .perform(); // 실행
  }
  catch (const SFTPCurl::exception &amp;amp;e)
  {
    std::cerr &amp;lt;&amp;lt; &quot;작업 실패: &quot; &amp;lt;&amp;lt; e.code() &amp;lt;&amp;lt; &quot; &quot; &amp;lt;&amp;lt; e.what() &amp;lt;&amp;lt; std::endl;
  }&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;메서드 체이닝을 이용해 Fluent Interface를 구현합니다. 각 설정이나 옵션들이 자연스럽게 설정될 수 있도록 합니다.&amp;nbsp;&lt;span style=&quot;text-align: start;&quot;&gt;메서드 체이닝을 사용하면&lt;/span&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&amp;nbsp;if문으로 할 수 없을뿐더러&amp;nbsp;&lt;/span&gt;부득이 exception을 사용할 수밖에 없기도 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;--darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot;&gt;C++ 인터페이스 정의&lt;/span&gt;&lt;span style=&quot;--darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot;&gt;&lt;/span&gt;&lt;span style=&quot;--darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;SFTP Curl의 예외처리&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;std::exception을 상속받아 예외처리를 구현합니다. std::exception을 상속받게 되면 std::exception으로도 &lt;span style=&quot;text-align: start;&quot;&gt;예외를 &lt;/span&gt;받을 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1755504888842&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class SFTPCurl
{
public:
  class exception : public std::exception
  {
  public:
    exception(CURLcode code, const std::string &amp;amp;message)
    : code_(code), message_(message) {}

    exception(CURLcode code, std::string &amp;amp;&amp;amp;message)
    : code_(code), message_(std::move(message)) {}

    const CURLcode &amp;amp;code() const noexcept { return code_; }
    const char *    what() const noexcept override { return message_.c_str(); }

  private:
    CURLcode    code_;
    std::string message_;
  };
};&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;&lt;span style=&quot;color: #000000; text-align: start; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;SFTPCurl::exception을 구현하였는데요. 이는 libcurl의 code값을 받기 위해서입니다. 사용자가 code값을 통해 다른 처리를 할 수 있도록 돕기 위함입니다.&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;color: #000000; text-align: start; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;인터페이스 분리&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;저는 Upload 기능과 Download 기능을 분리하여 구현합니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&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;&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;span style=&quot;color: #000000; text-align: start; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;libcurl은 C 기반의 단일화된 인터페이스를 제공하지만, 업로드와 다운로드는 설정해야 하는 옵션이 서로 다르면서도 일부는 중첩됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; text-align: start; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;하나의 인터페이스 안에서 두 기능을 동시에 처리할 경우, 의도하지 않은 동작이 발생할 가능성이 있으며 이를 검증하기 위해서는 많은 테스트가 필요합니다. 또 디버깅을 매우 어렵게 만듭니다.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; text-align: start; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;단일책임원칙(SRP)과&lt;span style=&quot;text-align: start;&quot;&gt; 인터페이스 분리(ISP)&lt;/span&gt;를 준수하면서도, 유지보수가 용이합니다.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;color: #000000; text-align: start; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-ke-size=&quot;size20&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;br /&gt;공통기능 SFTPCurlTemplate 클래스&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start; --darkreader-inline-color: var(--darkreader-text-333333, #c8c3bc);&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;Uploader와 Downloader의 공통 기능&lt;/span&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;초기화: reset(핸들 재생성), reset_options(옵션 초기화)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;연결 설정: url(주소), port(포트), connect_timeout(연결 시간 제한), timeout(수행 시간 제한)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;인증: userpass/username/password(계정 정보), private_key/public_key(키 파일)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;보안: verify_host(호스트 검증), known_hosts(호스트 파일 지정), verify_peer(피어 검증)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;디버깅/기타: verbose(로그 출력), set_option(직접 옵션 설정)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;파일 작업: list(파일 목록 가져오기), exists(파일 존재 확인)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start; --darkreader-inline-color: var(--darkreader-text-333333, #c8c3bc);&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Uploader 기능&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;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;업로드 관련 속성 reset, reset_options&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;파일로 업로드&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;파일 스트름으로 업로드&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;br /&gt;❗업데이트: &lt;span style=&quot;text-align: left;&quot;&gt;Proxy 패턴을 이용한 perform() 추가&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #333333; text-align: start; --darkreader-inline-color: var(--darkreader-text-333333, #c8c3bc);&quot; data-ke-size=&quot;size20&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;메서드 체이닝 구현 : CRTP&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start; --darkreader-inline-color: var(--darkreader-text-333333, #c8c3bc);&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;이제 Uploader과 Downloader의 공통된 동작을 묶은 클래스는 템플릿으로 작성하려고 합니다. &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;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;일반적인 상속 구조에서는 부모 클래스가 자식 클래스의 멤버를 모르기 때문에 아래와 같은 메서드 체이닝을 구현할 수 없습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1766636133308&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SFTPUploader sftp;
sftp.username(&quot;user&quot;) &amp;lt;- 공통클래스(부모클래스)의 멤버
    .upload  (&quot;sftp://server.com/backup/file.zip&quot;) &amp;lt;- 자식클래스의 멤버
    .perform();&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start; --darkreader-inline-color: var(--darkreader-text-333333, #c8c3bc);&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start; --darkreader-inline-color: var(--darkreader-text-333333, #c8c3bc);&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;위 내용을 풀어쓰면 이렇습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1766637214602&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SFTPUploader sftp;

// username은 부모 클래스의 멤버이기 때문에 부모클래스를 리턴합니다.
SFTPCurlTemplate &amp;amp;parent = sftp.username(&quot;user&quot;)

// 그런데 부모클래스에서 이어서 upload를 호출하면 부모클래스에는 upload가 없는데???
parent.upload(&quot;file.zip&quot;).perform();&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start; --darkreader-inline-color: var(--darkreader-text-333333, #c8c3bc);&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start; --darkreader-inline-color: var(--darkreader-text-333333, #c8c3bc);&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; letter-spacing: 0px; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;이러면 메서드 체이닝을 구현할 수 없겠죠.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start; --darkreader-inline-color: var(--darkreader-text-333333, #c8c3bc);&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start; --darkreader-inline-color: var(--darkreader-text-333333, #c8c3bc);&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;이 문제를 해결하기 위해 &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;CRTP (&lt;/span&gt;&lt;span style=&quot;text-align: start;&quot;&gt;Curiously Recurring Template Pattern)를 사용합니다. CRTP를 사용하면 부모클래스가 자식 클래스를 알 수 있게 됩니다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1766636435162&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;template&amp;lt;typename T&amp;gt;
class SFTPCurlTemplate : protected SFTPCurl
{
public:
  .....
  T &amp;amp;username(const std::string  &amp;amp;username)
  {
    // 컴파일 타임에 T로 형변환이 가능한지 체크하고 형변환
    return static_cast&amp;lt;T&amp;amp;&amp;gt;(*this);
  }
  ...
};

// 여기서 SFTPCurlTemplate의 T가 SFTPUploader이 됩니다.
class SFTPUploader : public SFTPCurlTemplate&amp;lt;SFTPUploader&amp;gt;
{&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start; --darkreader-inline-color: var(--darkreader-text-333333, #c8c3bc);&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start; --darkreader-inline-color: var(--darkreader-text-333333, #c8c3bc);&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;이 모든 사항들이 컴파일 타임에 결정되어 오브젝트 파일이 생성되므로 런타임 오버헤드가 없습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start; --darkreader-inline-color: var(--darkreader-text-333333, #c8c3bc);&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/p&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: #000000; text-align: start; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;CRTP (&lt;/span&gt;Curiously Recurring Template Pattern)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;런타임 오버헤드 없는 다형성(&lt;/span&gt;&lt;span style=&quot;text-align: left;&quot;&gt;정적 다형성)을 사용하기 위해&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;부모 클래스의 메서드들을 자식 클래스에서 메서드 체이닝으로 사용하기 위해&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Mixin방식으로&amp;nbsp;조각된&amp;nbsp;기능을&amp;nbsp;쉽게&amp;nbsp;추가하기&amp;nbsp;위해&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote style=&quot;color: #666666; text-align: left; --darkreader-inline-color: var(--darkreader-text-666666, #a8a095);&quot; data-ke-style=&quot;style2&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;정적 다형성이란 런타임에서는 다형성이 동작하지 않습니다만 코드 단위에서는 다형성이 동작하는 것과 같습니다. 이러한 이유는 자식 클래스가 템플릿 클래스를 상속받았으므로 컴파일 타임에 독립적인 타입이 결정됩니다.&lt;/span&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot;&gt;❗업데이트: &lt;/span&gt;Proxy 패턴을 이용한 프록시 객체 : Performer&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: left;&quot; data-darkreader-inline-color=&quot;&quot;&gt;이전에는 perform이 없어서 어디가 실행인지 인터페이스로는 불명확한 부분이 존재했습니다. 명시적인 exists(파일 존재여부 체크)와 list(파일 목록)을 제외하고는 SFTPUploader, SFTPDownloader 에서 사용하려고 합니다.&lt;br /&gt;&lt;br /&gt;그러기 위해서는 Performer와 같은 중간객체가 필요합니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1770172349325&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;template&amp;lt;typename T&amp;gt;
class SFTPCurlTemplate : protected SFTPCurl
{
public:
  .....
  T &amp;amp;username(const std::string  &amp;amp;username)
  .......
  
  // 실행해주는 프락시패턴 객체
  struct Performer
  {
    Performer(T &amp;amp;sftp_) noexcept : sftp_(sftp_) {}
    void perform() { sftp_.perform(); }

  private:
    T &amp;amp;sftp_;
  };
};

// 여기서 SFTPCurlTemplate의 T가 SFTPUploader이 됩니다.
class SFTPUploader : public SFTPCurlTemplate&amp;lt;SFTPUploader&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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;실제 객체 T = SFTPUploader 또는 SFTPDownloader를 실행해주는 중간객체로 임시객체로써 동작하게 됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;즉 마지막에 실행이 필요한 메서드의 리턴값으로 Performer를 반환하면 perform()을 호출해 주므로써 명시적으로 SFTP*의 어떤 메서드가 실행이 필요한지 인터페이스를 보고 알 수 있으며, SFTPxxxxx클래스에 어떤 메서드들이 perform이 필요한지 의도적으로 작성할 수 있습니다.&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;pre id=&quot;code_1770171825262&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class SFTPUploader : public SFTPCurlTemplate&amp;lt;SFTPUploader&amp;gt;
{
public:
  SFTPUploader &amp;amp;reset        ();
  SFTPUploader &amp;amp;reset_options();
  
  Performer upload(const std::string &amp;amp;filename);
  Performer upload(const std::function&amp;lt;size_t(void *target, size_t target_size)&amp;gt; &amp;amp;stream_func);&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;&lt;span style=&quot;color: #000000; text-align: start; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;공통옵션의 대상으로는 Performer를 포함해 연결/응답 타임아웃, 인증, 보안/검증, 로깅, 경로/URL구성등이 됩니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1755506313984&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;template&amp;lt;typename T&amp;gt;
class SFTPCurlTemplate : protected SFTPCurl
{
public:
  SFTPCurlTemplate(bool verify_host = false) : curl_(curl_easy_init(), curl_easy_cleanup)
  {
    if (curl_ == nullptr)
      throw_ec(CURLE_FAILED_INIT, curl_easy_strerror(CURLE_FAILED_INIT));

    this-&amp;gt;verify_host(verify_host);
  }
  virtual ~SFTPCurlTemplate() {}

  const std::string &amp;amp;url     () const { return url_; }
  const std::string &amp;amp;username() const { return username_; }
  const std::string &amp;amp;password() const { return password_; }

  // curl핸들을 새로 생성합니다. 기존의 curl핸들은 해제됩니다.
  T &amp;amp;reset          () { curl_.reset(curl_easy_init()); return static_cast&amp;lt;T&amp;amp;&amp;gt;(*this); }

  // curl핸들은 유지하지만 연결은 끊깁니다.
  T &amp;amp;reset_options  () { curl_easy_reset(curl_.get()); url_.clear(); username_.clear(); password_.clear(); port_ = 22; return static_cast&amp;lt;T&amp;amp;&amp;gt;(*this); }

  // 실행중 상세로그를 표준출력으로 출력합니다.
  T &amp;amp;verbose        (bool enable);

  // 이 함수의 타임아웃은 연결을 시도하는 시간입니다.
  T &amp;amp;connect_timeout(const long long    &amp;amp;seconds);

  // 이 함수의 타임아웃은 실제 수행시간을 의미합니다. 수행시간이 seconds를 넘으면 exception이 발생합니다.
  T &amp;amp;timeout        (const long long    &amp;amp;seconds);
  // SFTP 서버의 URL을 설정합니다. sftp://로 시작해야 합니다.
  T &amp;amp;url            (const std::string  &amp;amp;url);
  // SFTP 서버의 포트를 설정합니다. 기본값은 22입니다.
  T &amp;amp;port           (uint16_t port);

  // 사용자 인증 정보 설정
  T &amp;amp;userpass       (const std::string  &amp;amp;username, const std::string &amp;amp;password);
  T &amp;amp;username       (const std::string  &amp;amp;username);
  T &amp;amp;password       (const std::string  &amp;amp;password);

  // 잘 사용 안하는 함수들.
  T &amp;amp;private_key    (const std::string  &amp;amp;keyfile);
  T &amp;amp;public_key     (const std::string  &amp;amp;keyfile);
  T &amp;amp;verify_peer    (bool enable);

  // 기본값 false
  // false인 경우 ~/.ssh/known_host에 host키값이 등록되어 있지 않더라도 접근이 가능합니다.
  // true인 경우 ~/.ssh/known_host에 host키값이 등록되어 있지 않으면 접근 오류 발생.
  T &amp;amp;verify_host    (bool enable);
  T &amp;amp;known_hosts    (const std::string  &amp;amp;hostfile);

  // 옵션 설정을 위한 메서드.
  template&amp;lt;typename P&amp;gt;
  T &amp;amp;set_option(CURLoption option, P parameter);

  // SFTP 서버에 있는 파일 목록을 가져옵니다.
  virtual std::set&amp;lt;std::string&amp;gt; list();

  // SFTP 서버에 있는 파일이 존재하는지 확인합니다.
  // 파일이 존재하면 true, 존재하지 않으면 false를 반환합니다.
  virtual bool exists();

  // 실행해주는 프락시패턴 객체
  struct Performer
  {
    Performer(T &amp;amp;sftp_) noexcept : sftp_(sftp_) {}
    void perform() { sftp_.perform(); }

  private:
    T &amp;amp;sftp_;
  };
  ......
private:
  // RAII를 이용한 crul_ 자동소멸 등록
  std::unique_ptr&amp;lt;CURL, decltype(&amp;amp;curl_easy_cleanup)&amp;gt; curl_;
};&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;decltype&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;std::unique_ptr&amp;lt;CURL, decltype(&amp;amp;curl_easy_cleanup)&amp;gt; curl_; 에서 사용한 delctype는 C++11 표준에서 도입된 키워드로&amp;nbsp; decltype(expr)을 통해 임의의 표현식 expr의 타입을 정확히 얻을 수 있습니다. auto 키워드와 함께 제네릭 프로그래밍에 자주 사용됩니다.&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #c8c3bc; text-align: start; --darkreader-inline-color: var(--darkreader-text-c8c3bc, #c1bcb4);&quot; data-ke-size=&quot;size20&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;curl_의 RAII (Resource acquisition is initialization)&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1766637707926&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;std::unique_ptr&amp;lt;CURL, decltype(&amp;amp;curl_easy_cleanup)&amp;gt; curl_;&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #c8c3bc; text-align: start; --darkreader-inline-color: var(--darkreader-text-c8c3bc, #c1bcb4);&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #c8c3bc; text-align: start; --darkreader-inline-color: var(--darkreader-text-c8c3bc, #c1bcb4);&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;CURL의 자원관리를 unique_ptr에 위임시켜 자동으로 관리하도록 합니다. unique_ptr에 소멸자도 같이 등록합니다.&amp;nbsp; SFTPCurlTemplate가 소멸될 때 같이 소멸되므로 별도의 소멸코드를 작성하지 않아도 됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #c8c3bc; text-align: start; --darkreader-inline-color: var(--darkreader-text-c8c3bc, #c1bcb4);&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #c8c3bc; text-align: start; --darkreader-inline-color: var(--darkreader-text-c8c3bc, #c1bcb4);&quot; data-ke-size=&quot;size23&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;SFTPUploader&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1755520447810&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class SFTPUploader : public SFTPCurlTemplate&amp;lt;SFTPUploader&amp;gt;
{
public:
  SFTPUploader() : SFTPCurlTemplate&amp;lt;SFTPUploader&amp;gt;() { curl_setopt(CURLOPT_UPLOAD, 1L); }

  // curl핸들을 새로 생성합니다. 기존의 curl핸들은 해제됩니다.
  // reset동작은 공통 reset을 더해 UPLOAD 속성을 추가로 reset하기 때문에 추가합니다.
  SFTPUploader &amp;amp;reset        ();
  SFTPUploader &amp;amp;reset_options();

  // 아래 두개의 upload 함수는 별도 스레드 없이 perform() 호출 스레드에서 실행됩니다.
  Performer upload(const std::string &amp;amp;filename);
  // std::function&amp;lt;size_t(void *target, size_t target_size)&amp;gt;
  // target: curl이 제공하는 버퍼 (여기에 데이터를 채워야 함)
  // target_size: 버퍼 크기 (최대로 채울 수 있는 양)
  // 반환값: 실제로 채운 바이트 수(0 반환하면 전송 완료 (EOF))
  Performer upload(const std::function&amp;lt;size_t(void *target, size_t target_size)&amp;gt; &amp;amp;stream_func);

protected:
  static size_t read_callback(void *ptr, size_t size, size_t nmemb, void *userdata);

protected:
  std::function&amp;lt;size_t(void *target, size_t target_size)&amp;gt; stream_func_ = nullptr;
};&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;필요한 공통 옵션들은 SFTPCurlTemplate에 구현되어 있으므로 SFTPUploader는 오롯이 Upload 구현에만 집중할 수 있습니다.&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;&lt;s&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;text-align: left;&quot;&gt;※ &lt;/span&gt;SFTPDownloader는 별도로 게시하지 않았습니다.&lt;/span&gt;&lt;/s&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot;&gt;❗업데이트: &lt;/span&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot;&gt;SFTPDownloader을 추가하였습니다!&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;구현&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/ty7swkr/sftpcurl-cpp&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/ty7swkr/sftpcurl-cpp&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1773463941287&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 - ty7swkr/sftpcurl-cpp: A lightweight, header-only C++ SFTP upload/download library built on libcurl&quot; data-og-description=&quot;A lightweight, header-only C++ SFTP upload/download library built on libcurl - ty7swkr/sftpcurl-cpp&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/ty7swkr/sftpcurl-cpp&quot; data-og-url=&quot;https://github.com/ty7swkr/sftpcurl-cpp&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/b6UDrk/dJMb89ycagK/ZYEwNFgiWuC1ED6iBFPXck/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/8fchp/dJMb89ycagJ/5mzHzPuT1CugJef9k9c7B1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/ty7swkr/sftpcurl-cpp&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/ty7swkr/sftpcurl-cpp&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/b6UDrk/dJMb89ycagK/ZYEwNFgiWuC1ED6iBFPXck/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/8fchp/dJMb89ycagJ/5mzHzPuT1CugJef9k9c7B1/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 - ty7swkr/sftpcurl-cpp: A lightweight, header-only C++ SFTP upload/download library built on libcurl&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;A lightweight, header-only C++ SFTP upload/download library built on libcurl - ty7swkr/sftpcurl-cpp&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;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&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;있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>C++</category>
      <category>crpt</category>
      <category>libcurl</category>
      <category>SFTP</category>
      <category>sftp client</category>
      <category>sftpclient</category>
      <author>tys</author>
      <guid isPermaLink="true">https://cplusplus.tistory.com/25</guid>
      <comments>https://cplusplus.tistory.com/entry/C-libcurl%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EA%B0%84%EB%8B%A8%ED%95%9C-SFTP-%ED%81%B4%EB%9D%BC%EC%9D%B4%EC%96%B8%ED%8A%B8#entry25comment</comments>
      <pubDate>Mon, 18 Aug 2025 17:49:50 +0900</pubDate>
    </item>
    <item>
      <title>C++ UDP 수신 최적화: recvmmsg 캡슐화</title>
      <link>https://cplusplus.tistory.com/entry/C-UDP-%EC%88%98%EC%8B%A0-%EC%B5%9C%EC%A0%81%ED%99%94-recvmmsg-%EC%BA%A1%EC%8A%90%ED%99%94</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;UDP는 송수신 시 데이터를 패킷(덩어리) 단위로 주고받습니다. 따라서 UDP를 사용할 때는 여러 가지 위험 요소를 고려하여, 데이터 크기가 MTU(Maximum Transmission Unit)에서 IP 및 UDP 헤더 크기를 뺀 값을 초과하지 않도록 설계합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;

            &lt;figure class=&quot;unsupported component-kakaotv&quot; contenteditable=&quot;false&quot; style=&quot;background:#000;margin:16px 0;min-height:72px;padding:10px 16px;display:flex;align-items:center;justify-content:center;text-align:center;box-sizing:border-box;width:100%;max-width:100%;&quot;&gt;
                &lt;p contenteditable=&quot;false&quot; style=&quot;margin:0;color:#8a8a8a;font-size:13px;line-height:1.6;user-select:none;pointer-events:none;&quot;&gt;동영상 서비스가 종료되어 해당 콘텐츠를 재생할 수 없습니다.&lt;/p&gt;
            &lt;/figure&gt;
        
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;263&quot; data-start=&quot;204&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;UDP 소켓을 통해 데이터를 수신할 때는 일반적으로 다음과 같은 recvfrom 함수를 사용합니다:&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1752908701990&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ssize_t recvfrom(int sockfd,
                 void buf[restrict .len], size_t len,
                 int flags,
                 struct sockaddr *_Nullable restrict src_addr,
                 socklen_t *_Nullable restrict addrlen)&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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;하지만 recvfrom은 한 번에 하나의 패킷만 처리할 수 있기 때문에, 예를 들어 100개의 패킷이 도착하더라도 시스템 콜을 100번 호출해야 모두 처리할 수 있습니다. 이때 애플리케이션이 충분히 빠르게 커널 소켓 버퍼의 데이터를 소비하지 못하면, 커널 소켓 버퍼가 가득 찬 이후 도착하는 패킷은 손실될 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;844&quot; data-start=&quot;659&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;이러한 문제를 해결하기 위해, 로그 수집과 같은 고가용성 서버 애플리케이션에서는 리눅스에서 제공하는 전용 함수인 recvmmsg를 사용합니다. recvmmsg는 한 번의 시스템 콜로 여러 개의 패킷을 한 번에 가져올 수 있기 때문입니다.&lt;/span&gt;&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; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;시스템 콜 함수: recvmmsg, recvfrom&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px; color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;recvfrom이나 recvmmsg함수는 시스템 콜 함수입니다. recvfrom이나 recvmmsg는 시스템 콜이며, 호출 시 유저 모드에서 커널 모드로의 모드 전환이 발생합니다. 그리고 유저-커널 모드 전환 및 시스템 콜 처리 비용은 유저 함수 호출보다 큽니다.&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;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;예를 들어, 커널 소켓 버퍼에 1000개의 데이터가 이미 도착해 있다면, recvfrom을 1000번 호출해야 하며, 그만큼 커널 모드로의 진입과 유저 모드로의 복귀가 각각 발생하므로 총 2000회의 모드 전환이 일어나게 됩니다.&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;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;하지만 recvmmsg를 사용해 100개씩 한 번에 수신한다고 가정하면, 20번의 시스템 콜로 같은 작업을 처리할 수 있어 모드 전환 횟수도 20회로 줄어듭니다.&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; color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;아래의 recvmmsg함수는 1번의 호출로 여러 개의 버퍼를 가져올 수 있도록 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1752909442389&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;recvmmsg - receive multiple messages on a socket

int recvmmsg(int sockfd,
             struct mmsghdr *msgvec,
             unsigned int vlen,
             int flags, 
             struct timespec *timeout);&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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;recvmmsg 함수를 사용한 예제&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1752910293237&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;constexpr int VLEN = 10;
constexpr int BUFSIZE = 2048;

int main()
{
  int fd = socket(AF_INET, SOCK_DGRAM, 0);
  ..................

  struct mmsghdr msgs[VLEN];
  struct iovec iovecs[VLEN];
  char bufs[VLEN][BUFSIZE];
  sockaddr_in src_addrs[VLEN];

  for (int i = 0; i &amp;lt; VLEN; ++i)
  {
    iovecs[i].iov_base = bufs[i];
    iovecs[i].iov_len = BUFSIZE;

    msgs[i].msg_hdr.msg_name = &amp;amp;src_addrs[i];
    msgs[i].msg_hdr.msg_namelen = sizeof(sockaddr_in);
    msgs[i].msg_hdr.msg_iov = &amp;amp;iovecs[i];
    msgs[i].msg_hdr.msg_iovlen = 1;
    msgs[i].msg_hdr.msg_control = nullptr;
    msgs[i].msg_hdr.msg_controllen = 0;
    msgs[i].msg_hdr.msg_flags = 0;
  }

  while (true)
  {
    int ret = recvmmsg(fd, msgs, VLEN, MSG_DONTWAIT, nullptr);
    if (ret &amp;lt;= 0)
    {
      usleep(1000);
      continue;
    }

    for (int i = 0; i &amp;lt; ret; ++i)
    {
      char ip[INET_ADDRSTRLEN];
      inet_ntop(AF_INET, &amp;amp;src_addrs[i].sin_addr, ip, sizeof(ip));
      printf(&quot;recvmmsg: [%s:%d] %.*s\n&quot;,
             ip, ntohs(src_addrs[i].sin_port),
             msgs[i].msg_len, bufs[i]);
    }
  }

  close(fd);
  return 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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;위 예제는 현재로서는 비교적 간단해 보입니다. 하지만 아직 비즈니스 로직이 포함되어 있지 않기 때문입니다. 실제 서비스 코드에서는 위 예제에 다양한 처리 로직이 함께 들어가게 되므로&amp;nbsp;&lt;b&gt;&quot;복잡도와 결합도가 증가&quot;&lt;/b&gt; 하게 됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;복잡도와 결합도가 증가하게 되면 디버깅은 까다로워지고 가독성이 떨어지며, 유지보수는 어려워지는(수정이 어려운) 구조로 만들어집니다.&lt;/span&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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;이러한 문제를 방지하기 위해서는 recvmmsg 처리는 캡슐화하고, 비즈니스 로직에서는 필요한 메시지만 받아 처리할 수 있도록 인터페이스를 단순화합니다. 따라서 여기서는 recvmmsg를 클래스로 캡슐화하여 구조적으로, 또 물리적으로도 코드를 분리합니다.&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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;사용성 디자인 하기&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;아래는 만들기 전에 어떻게 사용할지 만들어 본 예제입니다. 사용성을 구상하지 않는 경우 자주&amp;nbsp;&lt;span style=&quot;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;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;패킷&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;크기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;어디로부터 왔는가&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;오류&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;입니다. 그리고 그 패킷은 어디든 이동이 자유로울 수 있어야 합니다. 포인터를 사용한다는 의미가 아닙니다. UDPMmsg에 의존적이지 않은 상태를 말합니다. &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;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;Buffer를 복사 없는&lt;/span&gt; raw pointer형태로 제공하는 방법은 의도적으로 배제했습니다. 내가 있는 업무 환경에서는 숙련된 동시성 처리 개발자 혹은 숙련된 C++개발자가 흔하지 않기 때문입니다.&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_1752910338237&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &quot;UDPMmsg.h&quot;

int main() 
{
  int fd = socket(AF_INET, SOCK_DGRAM, 0);
  ........................

  // 한번에 받을 개수와 각 버퍼별 크기
  UDPMmsg udp_mmsg(128, 1472);

  while (true) 
  {
    int count = udp_mmsg.recv(fd, MSG_DONTWAIT,
              [](int msg_flags, UdpMmsg::Buffer buffer, const UdpMmsg::SrcAddr&amp;amp; addr)
              {
              	std::cerr &amp;lt;&amp;lt; &quot;msg_flags &quot; &amp;lt;&amp;lt; msg_flags &amp;lt;&amp;lt; std::endl;

                char ip[INET_ADDRSTRLEN];
                inet_ntop(AF_INET, &amp;amp;addr.sin_addr, ip, sizeof(ip));
                std::cout &amp;lt;&amp;lt; &quot;udpmmsg: &quot; &amp;lt;&amp;lt; ip &amp;lt;&amp;lt; &quot; &quot; &amp;lt;&amp;lt; ntohs(addr.sin_port) &amp;lt;&amp;lt; &quot; &quot; &amp;lt;&amp;lt; buffer.size());
              });

    if (count &amp;lt;= 0)
      usleep(1000);
  }

  close(fd);
  return 0;
}&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;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;인터페이스 정의&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1752914556207&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class UDPMmsg
{
public:
  using Buffer  = std::vector&amp;lt;uint8_t&amp;gt;;
  using SrcAddr = struct sockaddr_in;

  UDPMmsg(size_t batch_size = 128, size_t buffer_size = 1400);

  /**
  - 멤버변수 Buffer들은 recvmmsg함수 호출시 마다 발생하는 mmsghdr 초기화 오버헤드를
    없애기 위해 재사용되도록 만들어 졌습니다.
  */

  /**
   * @brief UDP 메시지를 수신합니다. 타임아웃을 설정할 수 있습니다.
   * @param fd 소켓 파일 디스크립터
   * @param flags 수신 플래그 (MSG_DONTWAIT 등)
   * @param func 수신된 메시지를 처리할 콜백 함수
   * @param func(msg_flags, buffer, addr) msg_flags: 메시지 플래그, buffer: 수신된 데이터, addr: 송신자 주소
   * @note  msg_flags의 값은 다음과 같습니다:
   *        0x00            정상수신
   *        0x01(MSG_OOB)   OOB (Out-of-band) 데이터가 수신됨
   *        0x20(MSG_TRUNC) 데이터가 버퍼보다 커서 잘림
   * @see sys/socket.h 참조
   * @return 수신된 메시지의 개수, 오류 발생 시 -1 반환
   */
  int recv(int fd, int flags, const std::function&amp;lt;void(int, Buffer, const SrcAddr &amp;amp;)&amp;gt; &amp;amp;func);

  /**
   * @brief UDP 메시지를 수신합니다. 타임아웃을 설정할 수 있습니다.
   * @param fd 소켓 파일 디스크립터
   * @param flags 수신 플래그 (MSG_DONTWAIT 등)
   * @param tmout_msecs 타임아웃 시간 (밀리초 단위)
   * @param func 수신된 메시지를 처리할 콜백 함수
   * @param func(msg_flags, buffer, addr) msg_flags: 메시지 플래그, buffer: 수신된 데이터, addr: 송신자 주소
   * @note  msg_flags의 값은 다음과 같습니다:
   *        0x00            정상수신
   *        0x01(MSG_OOB)   OOB (Out-of-band) 데이터가 수신됨
   *        0x20(MSG_TRUNC) 데이터가 버퍼보다 커서 잘림
   * @see sys/socket.h 참조
   * @return 수신된 메시지의 개수, 오류 발생 시 -1 반환
   */
  int recv(int fd, int flags, uint32_t tmout_msecs, const std::function&amp;lt;void(int, Buffer, const SrcAddr &amp;amp;)&amp;gt; &amp;amp;func);

protected:
  struct timespec
  msecs_to_timespec(uint32_t milliseconds)
  {
    struct timespec ts;
    ts.tv_sec  = milliseconds / 1000;
    ts.tv_nsec = (milliseconds % 1000) * 1000000;
    return ts;
  }

protected:
  std::vector&amp;lt;struct mmsghdr&amp;gt; msgs_;
  std::vector&amp;lt;struct iovec&amp;gt;   iovecs_;
  std::vector&amp;lt;Buffer&amp;gt;         buffers_;
  std::vector&amp;lt;SrcAddr&amp;gt;        addrs_;
};&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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;RAII를 충분히 활용하고 있기 때문에&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&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;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;UDPMmsg&lt;/span&gt;는 사용한 메모리를 &lt;span style=&quot;text-align: start;&quot;&gt;UDPMmsg&lt;/span&gt;소멸 시 자동으로 반환합니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;std::vector로 데이터를 보관하고 있으므로 &lt;span style=&quot;text-align: start;&quot;&gt;UDPMmsg&lt;/span&gt; 소멸 시 자동해제되기 때문입니다.&lt;/span&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;&lt;span style=&quot;color: #000000; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot;&gt;람다 함수의 인자로 받은 Buffer 또한 명시적 메모리 해제가 필요 없습니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;Buffer(using Buffer = std::vector&amp;lt;uint8_t&amp;gt;)이기 때문입니다. 이렇게 받은 Buffer는 자동 소멸은 물론 std::move등을 통해 &lt;span style=&quot;text-align: left;&quot;&gt;복사 없이 옮길&lt;/span&gt; 수도 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&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;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;전체 코드&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1752917117251&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#pragma once

#include &amp;lt;cstdint&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;cerrno&amp;gt;
#include &amp;lt;functional&amp;gt;

#include &amp;lt;sys/socket.h&amp;gt;
#include &amp;lt;netinet/in.h&amp;gt;

/***
 * @brief UDP 멀티 메시지 수신 클래스
 *        recvmmsg() 시스템 콜을 사용하여 UDP 메시지를 배치로 수신합니다.
 *        멀티 메시지 수신을 위한 인터페이스를 제공합니다.
 *        주소 정보가 필요한 경우 sockaddr_in 구조체를 사용합니다
 *        C++17 표준을 사용합니다.
 *
 * 예제
    UDPMmsg udp_mmsg;

    // 메시지 수신
    auto result = udp_mmsg.recv(fd, MSG_DONTWAIT, [](int msg_flags, Buffer buffer)
    {
      std::cout &amp;lt;&amp;lt; int msg_flags &amp;lt;&amp;lt; &quot; 받은 데이터: &quot; &amp;lt;&amp;lt; buffer.size() &amp;lt;&amp;lt; &quot; bytes\n&quot;;
    });

    if (result &amp;gt; 0)
      std::cout &amp;lt;&amp;lt; &quot;총 &quot; &amp;lt;&amp;lt; result &amp;lt;&amp;lt; &quot;개 메시지 받음\n&quot;;
    else if (result == 0)
      std::cout &amp;lt;&amp;lt; &quot;타임아웃 또는 데이터 없음\n&quot;;
    else
      std::cout &amp;lt;&amp;lt; &quot;오류: &quot; &amp;lt;&amp;lt; strerror(errno) &amp;lt;&amp;lt; &quot;\n&quot;;
 *
 * @note
 *       flags
 *       MSG_DONTWAIT : 주로 이 flag를 기본으로 사용합니다. 논블로킹 모드로 동작시킵니다. 이 경우 데이터가 없으면 즉시 반환됩니다.
 *       MSG_PEEK     : 데이터를 읽지만 큐에서 제거하지 않습니다.
 *       MSG_WAITALL  : UDP에서는 의미 없음. 요청한 모든 데이터를 받을 때까지 대기합니다.
 *       MSG_TRUNC    : 메시지가 버퍼보다 클 경우 잘린 부분 정보를 제공합니다.
 *       MSG_CTRUNC   : 제어 데이터가 잘린 경우 알림을 받습니다.
 *       MSG_OOB      : 대역 외 데이터를 수신합니다.
 *       MSG_ERRQUEUE : 에러 큐에서 메시지를 수신합니다.
 * @see sys/socket.h 참조
 */
class UDPMmsg
{
public:
  using Buffer  = std::vector&amp;lt;uint8_t&amp;gt;;
  using SrcAddr = struct sockaddr_in;

  UDPMmsg(size_t batch_size = 128, size_t buffer_size = 1400);

  /**
  - 멤버변수 Buffer들은 recvmmsg함수 호출시 마다 발생하는 mmsghdr 초기화 오버헤드를
    없애기 위해 재사용되도록 만들어 졌습니다.
  */

  /**
   * @brief UDP 메시지를 수신합니다. 타임아웃을 설정할 수 있습니다.
   * @param fd 소켓 파일 디스크립터
   * @param flags 수신 플래그 (MSG_DONTWAIT 등)
   * @param func 수신된 메시지를 처리할 콜백 함수
   * @param func(msg_flags, buffer, addr) msg_flags: 메시지 플래그, buffer: 수신된 데이터, addr: 송신자 주소
   * @note  msg_flags의 값은 다음과 같습니다:
   *        0x00            정상수신
   *        0x01(MSG_OOB)   OOB (Out-of-band) 데이터가 수신됨
   *        0x20(MSG_TRUNC) 데이터가 버퍼보다 커서 잘림
   * @see sys/socket.h 참조
   * @return 수신된 메시지의 개수, 오류 발생 시 -1 반환
   */
  int recv(int fd, int flags, const std::function&amp;lt;void(int, Buffer, const SrcAddr &amp;amp;)&amp;gt; &amp;amp;func);

  /**
   * @brief UDP 메시지를 수신합니다. 타임아웃을 설정할 수 있습니다.
   * @param fd 소켓 파일 디스크립터
   * @param flags 수신 플래그 (MSG_DONTWAIT 등)
   * @param tmout_msecs 타임아웃 시간 (밀리초 단위)
   * @param func 수신된 메시지를 처리할 콜백 함수
   * @param func(msg_flags, buffer, addr) msg_flags: 메시지 플래그, buffer: 수신된 데이터, addr: 송신자 주소
   * @note  msg_flags의 값은 다음과 같습니다:
   *        0x00            정상수신
   *        0x01(MSG_OOB)   OOB (Out-of-band) 데이터가 수신됨
   *        0x20(MSG_TRUNC) 데이터가 버퍼보다 커서 잘림
   * @see sys/socket.h 참조
   * @return 수신된 메시지의 개수, 오류 발생 시 -1 반환
   */
  int recv(int fd, int flags, uint32_t tmout_msecs, const std::function&amp;lt;void(int, Buffer, const SrcAddr &amp;amp;)&amp;gt; &amp;amp;func);

protected:
  struct timespec
  msecs_to_timespec(uint32_t milliseconds)
  {
    struct timespec ts;
    ts.tv_sec  = milliseconds / 1000;
    ts.tv_nsec = (milliseconds % 1000) * 1000000;
    return ts;
  }

protected:
  std::vector&amp;lt;struct mmsghdr&amp;gt; msgs_;
  std::vector&amp;lt;struct iovec&amp;gt;   iovecs_;
  std::vector&amp;lt;Buffer&amp;gt;         buffers_;
  std::vector&amp;lt;SrcAddr&amp;gt;        addrs_;
};

inline
UDPMmsg::UDPMmsg(size_t batch_size, size_t buffer_size)
{
  buffers_ .resize(batch_size);
  msgs_    .resize(batch_size);
  iovecs_  .resize(batch_size);
  addrs_   .resize(batch_size);
  for (size_t index = 0; index &amp;lt; batch_size; ++index)
  {
    buffers_[index].resize(buffer_size);
    iovecs_ [index].iov_base               = buffers_[index].data();
    iovecs_ [index].iov_len                = buffer_size;
    msgs_   [index].msg_hdr.msg_iov        = &amp;amp;iovecs_[index];
    msgs_   [index].msg_hdr.msg_iovlen     = 1;
    msgs_   [index].msg_hdr.msg_name       = &amp;amp;addrs_[index];
    msgs_   [index].msg_hdr.msg_namelen    = sizeof(sockaddr_in);
    msgs_   [index].msg_hdr.msg_control    = nullptr;
    msgs_   [index].msg_hdr.msg_controllen = 0;
    msgs_   [index].msg_hdr.msg_flags      = 0;
  }
}

inline int
UDPMmsg::recv(int fd, int flags,
              const std::function&amp;lt;void(int msg_flags, UDPMmsg::Buffer buffer,
                                       const UDPMmsg::SrcAddr &amp;amp;addr)&amp;gt; &amp;amp;func)
{
  return this-&amp;gt;recv(fd, flags, 0, func);
}

inline int
UDPMmsg::recv(int fd, int flags, uint32_t tmout_msecs,
              const std::function&amp;lt;void(int msg_flags, UDPMmsg::Buffer buffer,
                                       const UDPMmsg::SrcAddr &amp;amp;addr)&amp;gt; &amp;amp;func)
{
  struct timespec *timeout_ptr = nullptr;
  struct timespec  timeout;
  if (tmout_msecs &amp;gt; 0)
  {
    timeout     = msecs_to_timespec(tmout_msecs);
    timeout_ptr = &amp;amp;timeout;
  }

  int count = recvmmsg(fd, msgs_.data(), msgs_.size(), flags, timeout_ptr);
  if (count &amp;lt;= 0)
    return count;

  for (size_t index = 0; index &amp;lt; (size_t)count; ++index)
    func(msgs_[index].msg_hdr.msg_flags,
         Buffer(buffers_[index].begin(),
                buffers_[index].begin() + msgs_[index].msg_len),
         addrs_[index]);

  return count;
}&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;&lt;span style=&quot;color: #000000; text-align: start;&quot; data-darkreader-inline-color=&quot;&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;있습니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>C++</category>
      <category>C++17</category>
      <category>recvfrom</category>
      <category>recvmmsg</category>
      <category>UDP</category>
      <category>캡슐화</category>
      <author>tys</author>
      <guid isPermaLink="true">https://cplusplus.tistory.com/24</guid>
      <comments>https://cplusplus.tistory.com/entry/C-UDP-%EC%88%98%EC%8B%A0-%EC%B5%9C%EC%A0%81%ED%99%94-recvmmsg-%EC%BA%A1%EC%8A%90%ED%99%94#entry24comment</comments>
      <pubDate>Sat, 19 Jul 2025 16:34:44 +0900</pubDate>
    </item>
    <item>
      <title>C++ 멀티쓰레드: Read-Only 데이터셋의 업데이트와 락프리 접근</title>
      <link>https://cplusplus.tistory.com/entry/C-%EB%A9%80%ED%8B%B0%EC%93%B0%EB%A0%88%EB%93%9C-Read-Only-%EB%8D%B0%EC%9D%B4%ED%84%B0%EC%85%8B%EC%9D%98-%EC%97%85%EB%8D%B0%EC%9D%B4%ED%8A%B8%EC%99%80-%EB%9D%BD%ED%94%84%EB%A6%AC-%EC%A0%91%EA%B7%BC-RCU</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;어플리케이션에서 흔히 볼 수 있는 코드 중 하나가 마스터 정보를 메모리에 두고 다수의 쓰레드가 검색하는 것입니다. 이런 Read-Only 데이터셋은 굳이 Lock을 만들 필요가 없습니다. 하지만 이 마스터 정보가 간헐적으로 업데이트된다면 하는 수 없이 Lock을 사용해야 하는데요,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;간헐적으로 일어나는 업데이트 때문에 다수의 쓰레드도 데이터셋에 접근시마다 Lock을 사용할 수밖에 없습니다. 이렇게 되면 싱글쓰레드에서 검색하는 게 오히려 더 빠릅니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1763822223132&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;C++ std::mutex보다 빠른 RCU(Lock-free) 라이브러리: cppurcu&quot; data-og-description=&quot;C++ Lock-free library faster than std::mutex: cppurcu 읽기 작업이 많은 프로세스멀티스레드 환경에서 특정 자료는 거의 읽기가 대부분 환경에서 해당 자료를 읽으려 할 때마다 단순하게는 std::mutex 또는 pthre&quot; data-og-host=&quot;cplusplus.tistory.com&quot; data-og-source-url=&quot;https://cplusplus.tistory.com/entry/C17-Lock-Free-RCU-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC-cppurcu&quot; data-og-url=&quot;https://cplusplus.tistory.com/entry/C17-Lock-Free-RCU-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC-cppurcu&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/fqzVG/hyZN5ggDzX/izx0WBgTNbh001vYEXu3S1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/bXjdqb/hyZOczF9Yb/S2cx35CKy5TikeKo2lHAf0/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800&quot;&gt;&lt;a href=&quot;https://cplusplus.tistory.com/entry/C17-Lock-Free-RCU-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC-cppurcu&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://cplusplus.tistory.com/entry/C17-Lock-Free-RCU-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC-cppurcu&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/fqzVG/hyZN5ggDzX/izx0WBgTNbh001vYEXu3S1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/bXjdqb/hyZOczF9Yb/S2cx35CKy5TikeKo2lHAf0/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800');&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;C++ std::mutex보다 빠른 RCU(Lock-free) 라이브러리: cppurcu&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;C++ Lock-free library faster than std::mutex: cppurcu 읽기 작업이 많은 프로세스멀티스레드 환경에서 특정 자료는 거의 읽기가 대부분 환경에서 해당 자료를 읽으려 할 때마다 단순하게는 std::mutex 또는 pthre&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;cplusplus.tistory.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;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;여기서는 데이터셋의 업데이트와 검색을 std::atomic과 std::shared_ptr을 이용하여 Lock없이 사용하는 방법입니다. &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;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;std::shared_ptr은 단순히 복사 가능한(trivially copyable) 타입이 아니기에 std::atomic_load, std::atomic_store 같은 free function들을 써야 합니다. &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;그런데 std::atomic처럼 사용하고 싶네요. 그래서 사용하기 쉽게 아래와 같은 클래스를 만듭니다. Satomic이라 해보았습니다. std::atomic과 비슷하게 해 줍니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1753847384847&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;template&amp;lt;typename T&amp;gt;
class Satomic
{
public:
  Satomic() noexcept = default;

  explicit Satomic(std::shared_ptr&amp;lt;T&amp;gt; ptr) noexcept
  {
    store(std::move(ptr));
  }

  void store(std::shared_ptr&amp;lt;T&amp;gt; new_ptr) noexcept
  {
    std::atomic_store(&amp;amp;ptr_, std::move(new_ptr));
  }

  Satomic &amp;amp;operator=(std::shared_ptr&amp;lt;T&amp;gt; new_ptr) noexcept
  {
    this-&amp;gt;store(new_ptr);
    return *this;
  }

  std::shared_ptr&amp;lt;T&amp;gt; load() const noexcept
  {
    return std::atomic_load(&amp;amp;ptr_);
  }

private:
  std::shared_ptr&amp;lt;T&amp;gt; ptr_;
};&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;이제 위의 Satomic 클래스를 이용해 쉽게 사용하도록 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1752981921741&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class IPContainer
{
public:
  bool contains(const std::string &amp;amp;ip);
  void update();
  
private:
  // 여기 const가 있습니다.
  Satomic&amp;lt;const std::unordered_set&amp;lt;std::string&amp;gt;&amp;gt; ips_;
}

void
IPContainer::update()
{
  auto ips = std::make_shared&amp;lt;std::unordered_set&amp;lt;std::string&amp;gt;&amp;gt;();
  ............
  ips_.store(ips);
}

bool
IPContainer::contains(const std::string &amp;amp;ip)
{
  // std::shared_ptr&amp;lt;std::unordered_set&amp;lt;std::string&amp;gt;
  auto ips = ips_.load();
  return ips.count(ip) &amp;gt; 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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;1: contains() 함수 시작&lt;/b&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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;ips_.load()로 현재 shared_ptr을 원자적으로 가져옵니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;shared_ptr 복사가 일어나지만 실제 unordered_set 객체는 복사되지 않고 참조 카운트만 증가합니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;2: 검색 수행&lt;/b&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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;복사된 shared_ptr을 통해 unordered_set에서 ip를 검색합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;이때 const로 보호되어 수정할 수 없습니다.&lt;br /&gt;std::atomic&amp;lt;std::shared_ptr&amp;lt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot; data-darkreader-inline-color=&quot;&quot;&gt;const&lt;/span&gt;&lt;/b&gt;&amp;nbsp;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot; data-darkreader-inline-color=&quot;&quot;&gt;std::unordered_set&amp;lt;std::string&amp;gt;&lt;/span&gt;&amp;gt;&lt;/b&gt;&amp;gt;&amp;nbsp;ips_;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;3: 동시에 다른 스레드에서 update() 호출&lt;/b&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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; text-align: left;&quot; data-darkreader-inline-color=&quot;&quot;&gt;shared_ptr로&amp;nbsp;&lt;/span&gt;새로운 unordered_set 객체를 생성합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;ips_.store()로 새로운 shared_ptr을 원자적으로 저장합니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;4: 기존 객체 보호&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;&lt;/b&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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;contains()에서 사용 중인 기존 객체는 여전히 참조되고 있어서 소멸되지 않습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;새로운 객체가 ips_에 저장되어도 기존 작업에 영향을 주지 않습니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;5: contains() 함수 완료&lt;/b&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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;검색 결과를 반환하고 함수가 끝납니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;지역 shared_ptr이 소멸되면서 참조 카운트가 감소합니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;6: 자동 메모리 관리&lt;/b&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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;더 이상 참조되지 않는 기존 객체라면 자동으로 소멸됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;이후 새로운 요청들은 업데이트된 데이터셋을 사용합니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;br /&gt;RCU(Read-Copy-Update)!&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;&lt;span style=&quot;color: #000000; text-align: start;&quot; data-darkreader-inline-color=&quot;&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;있습니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>Atomic</category>
      <category>C++</category>
      <category>cpp</category>
      <category>rcu</category>
      <category>shared_ptr</category>
      <author>tys</author>
      <guid isPermaLink="true">https://cplusplus.tistory.com/23</guid>
      <comments>https://cplusplus.tistory.com/entry/C-%EB%A9%80%ED%8B%B0%EC%93%B0%EB%A0%88%EB%93%9C-Read-Only-%EB%8D%B0%EC%9D%B4%ED%84%B0%EC%85%8B%EC%9D%98-%EC%97%85%EB%8D%B0%EC%9D%B4%ED%8A%B8%EC%99%80-%EB%9D%BD%ED%94%84%EB%A6%AC-%EC%A0%91%EA%B7%BC-RCU#entry23comment</comments>
      <pubDate>Fri, 18 Jul 2025 08:54:50 +0900</pubDate>
    </item>
    <item>
      <title>이 블로그는 왜 시작되었을까요?</title>
      <link>https://cplusplus.tistory.com/notice/22</link>
      <description>&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;533&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/br9kyG/btsPk4sKYoS/ytrKvtBKy3uOp8KLk1rLq1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/br9kyG/btsPk4sKYoS/ytrKvtBKy3uOp8KLk1rLq1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/br9kyG/btsPk4sKYoS/ytrKvtBKy3uOp8KLk1rLq1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbr9kyG%2FbtsPk4sKYoS%2FytrKvtBKy3uOp8KLk1rLq1%2Fimg.jpg&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;477&quot; height=&quot;318&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;533&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;color: #000000; font-family: GungSeo, serif;&quot;&gt;어떤 코드가 더 많은 개발자에게 더 나은 결과를 제공하는가?&lt;/span&gt;&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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;모던 C++ 실무 교육을 위한 블로그&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;이 블로그는 실무에서 모던 C++을 더 &lt;span style=&quot;text-align: start;&quot; data-darkreader-inline-color=&quot;&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;왜 시작했나요?&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;국내 C++&lt;span style=&quot;text-align: start;&quot; data-darkreader-inline-color=&quot;&quot;&gt;개발&amp;nbsp;&lt;/span&gt;환경은 C++을 단순히 'C의 확장' 정도로만 인식하는 경우가 많습니다. 모던 C++이 제공하는 강력한 기능들이 있음에도 불구하고, 여전히 C 스타일의 위험한 코드가 실무에서 자주 사용되고 있는 게 현실입니다.&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;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;특히 C++을 사용하면서도 C++98, C++03 수준에 머물러 있는 현실과 더불어 템플릿, RAII, 스마트 포인터와 C++11 이후에 제공되는 모던 C++의 강력한 기능들이 제대로 활용되지 못하고 있는 점은 매우 아쉬운 부분입니다.&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;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;실무에 어떻게 OOP를 적용하고 여기에 모던 C++을 어떻게 사용하는지 이론보다는 실무에 필요한 패턴들과 현대적인 클래스를 만드는 과정등을 코드와 예제를 중심으로&amp;nbsp;&lt;span style=&quot;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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;무엇을 배울 수 있나요?&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;OOP의 이론과 원칙들이 어떻게 지켜지는지 자연스럽게 습득할 수 있도록 합니다.&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;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;실무에서 바로 사용할 수 있는 모던 C++ 기법들&lt;/b&gt;: 안전한 배열 복사, 멀티쓰레드 프로그래밍, 자동 자원 관리 등&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;성능과 안전성을 동시에 확보하는 방법&lt;/b&gt;: BlockingQueue, 샤딩, TLS 활용법 등&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;C 스타일 코드의 문제점과 모던 C++로의 개선 방법&lt;/b&gt;: 실제 코드 예제를 통한 before/after&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;실전 프로젝트에서 활용 가능한 패턴들&lt;/b&gt;: 쓰레드 랩핑, 시그널 핸들링, 템플릿 활용법 등&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&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;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;이 블로그는&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;C++17부터의 모던 기능들을 중심으로, 실무에서 겪을 수 있는 구체적인 문제들을 해결하는 방법들을 안내합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;단순한 문법 설명보다는 실무에서 사용할 수 있는 실용적인 해결책들을 제시합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;모던 C++의 가치를 이해하고, 더 안전하고 효율적인 코드를 작성하는 것이 이 블로그의 목적입니다.&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <author>tys</author>
      <guid isPermaLink="true">https://cplusplus.tistory.com/notice/22</guid>
      <pubDate>Wed, 16 Jul 2025 22:24:51 +0900</pubDate>
    </item>
    <item>
      <title>C++17 템플릿 추론기능을 이용한 안전한 배열 복사</title>
      <link>https://cplusplus.tistory.com/entry/CPP17-%EC%B6%94%EB%A1%A0%EA%B8%B0%EB%8A%A5%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EC%95%88%EC%A0%84%ED%95%9C-%EB%B0%B0%EC%97%B4-%EB%B3%B5%EC%82%AC</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;들어가며&lt;/span&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;모던 C++을 사용하지 않은 코드에서는 std::string이나 std::array보다는 문자 기반의 배열을 자주 사용합니다. 이러한 방식은 코딩 실수가 발생하기 쉬우며, 그로 인해 의도치 않은 동작이나 비정상적인 종료로 이어질 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start; --darkreader-inline-color: var(--darkreader-text-333333, #c8c3bc);&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start; --darkreader-inline-color: var(--darkreader-text-333333, #c8c3bc);&quot; data-start=&quot;187&quot; data-end=&quot;322&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;특히 구조체에 이런 배열을 십수 개 이상 선언하는 관습에서는, 배열 복사 과정에서 발생하는 오류를 찾기가 쉽지 않습니다. 테스트를 진행하더라도 오류가 발생한 지점에서 즉시 종료되지 않는 경우가 많기 때문에, 문제를 더욱 찾기 어렵게 만듭니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start; --darkreader-inline-color: var(--darkreader-text-333333, #c8c3bc);&quot; data-start=&quot;187&quot; data-end=&quot;322&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start; --darkreader-inline-color: var(--darkreader-text-333333, #c8c3bc);&quot; data-start=&quot;324&quot; data-end=&quot;450&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;이러한 배열 복사는 오늘날 실무에서도 여전히 자주 사용되고 있습니다. 때문에 모던 C++을 잘 모르는 많은 개발자들은 아직도 배열을 하나하나 확인하는 고통을 받고 있습니다. :(&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start; --darkreader-inline-color: var(--darkreader-text-333333, #c8c3bc);&quot; data-start=&quot;324&quot; data-end=&quot;450&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start; --darkreader-inline-color: var(--darkreader-text-333333, #c8c3bc);&quot; data-start=&quot;324&quot; data-end=&quot;450&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt; 여기서는 모던 C++을 이용하여 자동으로 배열 계산해 주고 복사해 주는 보다 편리하고 안전하게 다룰 수 있는 방법을 소개합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;

            &lt;figure class=&quot;unsupported component-kakaotv&quot; contenteditable=&quot;false&quot; style=&quot;background:#000;margin:16px 0;min-height:72px;padding:10px 16px;display:flex;align-items:center;justify-content:center;text-align:center;box-sizing:border-box;width:100%;max-width:100%;&quot;&gt;
                &lt;p contenteditable=&quot;false&quot; style=&quot;margin:0;color:#8a8a8a;font-size:13px;line-height:1.6;user-select:none;pointer-events:none;&quot;&gt;동영상 서비스가 종료되어 해당 콘텐츠를 재생할 수 없습니다.&lt;/p&gt;
            &lt;/figure&gt;
        
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;당신이 흔히 볼 수 있는 코드&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;모던 C++ 이 적용되지 않은 흔히 볼 수 있는 코드들은 이렇습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1752127602078&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;char ip    [32];
char name  [16]; 
char buffer[64];

// A. 일반적으로 strcpy를 사용했을때,
strcpy(ip,     &quot;192.168.1.1&quot;);
strcpy(name,   &quot;홍길동&quot;); 
strcpy(buffer, &quot;test data&quot;);

// B. 혹은 strncpy를 사용해서
strncpy(ip, &quot;192.168.1.1&quot;, sizeof(ip) - 1);
ip[sizeof(ip) - 1] = '\0';

strncpy(name, &quot;홍길동&quot;, sizeof(name) - 1);
name[sizeof(name) - 1] = '\0';

strncpy(buffer, &quot;test data&quot;, sizeof(buffer));

// C. 혹은 길이검사를 해서
{
  const char *src = &quot;192.168.1.1&quot;;
  size_t src_len = strlen(src);
  size_t copy_len = src_len &amp;lt; sizeof(ip)-1 ? src_len : sizeof(ip)-1;
  memcpy(ip, src, copy_len);
  ip[copy_len] = '\0';
}
{
  const char *src = &quot;홍길동&quot;;
  size_t src_len = strlen(src);
  size_t copy_len = src_len &amp;lt; sizeof(name)-1 ? src_len : sizeof(name)-1;
  memcpy(name, src, copy_len);
  name[copy_len] = '\0';
}
{
  const char *src = &quot;test data&quot;;
  size_t src_len = strlen(src);
  size_t copy_len = src_len &amp;lt; sizeof(buffer)-1 ? src_len : sizeof(buffer)-1;
  memcpy(buffer, src, copy_len);
}

// D. 혹은 매크로를 이용해서
CHAR_ARRAY_COPY_NULL(ip, &quot;192.168.1.1&quot;);      // null terminator 추가
CHAR_ARRAY_COPY_NULL(name, &quot;홍길동&quot;);          // null terminator 추가  
CHAR_ARRAY_COPY_FIXED(buffer, &quot;test data&quot;);   // 고정 길이, null terminator 없음&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;A. strcpy:&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;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;버퍼 오버플로우가 발생할 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;소스 문자열의 길이를 검사하지 않기 때문입니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;B. strncpy:&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;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;strncpy(buffer, &quot;test data&quot;, sizeof(buffer))에서 null terminator 설정이 누락될 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;strncpy는 버퍼가 가득 찰 경우 자동으로 null terminator를 추가하지 않습니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;C. 길이검사 후 memcpy:&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;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;null terminator 설정이 누락될 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;코드가 반복적이고 길어지며, 정해 놓은 버퍼 형식에 따라 실수할 가능성이 높습니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;D. 매크로 사용의 문제점:&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;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;매크로 구현에 따라 다른 문제점들이 나타날 가능성이 있으며&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;모던 C++로 브릿징 하기&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;이전 코드들을 모두 std::string이나 std::array로 변경하기에는 부담이 큽니다. &lt;span style=&quot;text-align: start;&quot;&gt;이 상태에서 변경사항이나 수정사항이 생길 때마다 모든 배열을 확인해 가며 작업해야만 합니다. 확인한다 해도 코드상에 실수를 0으로 만들기는 어렵습니다.&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;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;그래서 여기서는 C++ 17의 추론기능을 활용하여 컴파일러에게 배열 확인작업을 맡겨 배열 확인에 대한 실수를 없애버리고 주요 로직에 집중하도록 만드는 것이 핵심입니다.&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;span style=&quot;color: #000000; text-align: start; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;인터페이스&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1752146615848&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt; char ip  [32];
 char name[16];
 
 // 일반 문자열을 char배열에 복사
 chars_term(ip)   = &quot;192.168.1.1&quot;;  // N=32 추론
 chars_term(name) = &quot;홍길동&quot;;        // N=16 추론
 
 // std::string 문자열을 char배열에 복사
 std::string city_str = &quot;서울&quot;;
 char city[10];
 chars_term(city) = str;
 
 std::cout &amp;lt;&amp;lt; ip   &amp;lt;&amp;lt; std::endl;     // 192.168.1.1
 std::cout &amp;lt;&amp;lt; name &amp;lt;&amp;lt; std::endl;     // 홍길동
 std::cout &amp;lt;&amp;lt; city &amp;lt;&amp;lt; std::endl;     // 서울

 char buffer[64];
 chars_term(buffer) = &quot;test data&quot;;    // N=64 추론

 // 배열끼리 복사
 char backup[32];
 chars_byte(backup) = ip;    // M=32, N=32
 
 std::cout &amp;lt;&amp;lt; backup &amp;lt;&amp;lt; std::endl; // 192.168.1.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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;개인적으로 함수를 사용하는 것 보다는, operator를 함수로 사용하는 것을 좋아합니다. 조금 더 직관적으로 읽히는 경우가 더 많기 때문입니다. 그래서 저는 위와 같이 구상하고 아래와 같은 요구 조건을 설정하였습니다.&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;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;chars_term은 인자로 들어온 값의 끝에 NULL을 붙이도록 합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;chars_byte는 인자로 들어온 값에 NULL 없이 끝까지 복사하도록 합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;[문자열 리터럴], [std::string형태의 문자열], [또 다른 배열]을 배열에 복사할 수 있도록 합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;클래스 구현&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;자동으로 배열의 개수를 알아야 하므로 C++ 비-타입 템플릿 매개변수(Non-Type Template Parameter) 기능을 이용해 컴파일 타임에 복사 대상이 되는 배열의 개수를 알아 옵니다. 이러한 템플릿 추론 기능은 컴파일 타임에 사용되므로 실행시간의 오버헤드가 없는 것도 장점입니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1752129462072&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_view&amp;gt;
#include &amp;lt;string&amp;gt;
#include &amp;lt;cstring&amp;gt;
#include &amp;lt;algorithm&amp;gt;

template&amp;lt;std::size_t N&amp;gt;
class chars_helper
{
public:
  chars_helper(char (&amp;amp;arr_ref)[N], bool null_term)
  : value_(arr_ref), null_term_(null_term)
  {
    std::memset(value_, 0, N * sizeof(char));
  }

  chars_helper &amp;amp;operator=(const std::string &amp;amp;str)
  {
    std::memcpy(value_, str.c_str(), std::min(str.length(), null_term_ ? N - 1 : N));
    if (null_term_ == true) value_[N-1] = '\0';

    return *this;
  }

  chars_helper &amp;amp;operator=(const std::string_view &amp;amp;str)
  {
    std::memcpy(value_, str.data(), std::min(str.size(), null_term_ ? N - 1 : N));
    if (null_term_ == true) value_[N-1] = '\0';
    return *this;
  }

  chars_helper &amp;amp;operator=(const char *str)
  {
    std::memcpy(value_, str, std::min(std::strlen(str), null_term_ ? N - 1 : N));
    if (null_term_ == true) value_[N-1] = '\0';

    return *this;
  }

  template&amp;lt;std::size_t M&amp;gt;
  chars_helper &amp;amp;operator=(const char (&amp;amp;source)[M])
  {
    if (null_term_ == false)
    {
      std::memcpy(value_, source, std::min(N, M));
      return *this;
    }

    for (std::string::size_type index = 0; index &amp;lt; std::min(N-1, M); ++index)
      if (value_[index] = source[index]; source[index] == '\0')
        break;

    return *this;
  }

private:
  char  (&amp;amp;value_)[N];
  bool  null_term_;
};


// 템플릿 추론을 위한 함수
template&amp;lt;std::size_t N&amp;gt; chars_helper&amp;lt;N&amp;gt;
chars_term(char (&amp;amp;arr)[N]) { return chars_helper&amp;lt;N&amp;gt;(arr, true); }

// 템플릿 추론을 위한 함수
template&amp;lt;std::size_t N&amp;gt; chars_helper&amp;lt;N&amp;gt;
chars_byte(char (&amp;amp;arr)[N]) { return chars_helper&amp;lt;N&amp;gt;(arr, 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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;chars_term과 chars_byte는 널 이외의 동작은 일치합니다. 그래서 chars_helper클래스로 두 동작에 만족하는 클래스를 구성합니다.&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;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;생성자의 인자로 복사대상의 변수를 받습니다. 이때 마지막 배열에 NULL이 필요한지 아닌지 두 번째 인자로 받도록 합니다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;operator= 연산자를 이용하여 std::string, 문자열 리터럴 및 배열을 복사할 수 있도록 합니다.&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;--darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot;&gt;그 외에 다른 타입에 대한 복사가 필요한 경우 더 추가하기가 쉽습니다. 심지어 int형도 할 수 있습니다.&lt;/span&gt;&lt;span style=&quot;--darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot;&gt;&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;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;--darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot;&gt;C++14까지는 템플릿 추론 기능을 클래스에서 지원하지 않습니다. C++17은 Deduction Guide 기능을 통해 사용할 수도 있으나 위와 같은 조건은 그러한 기능을 사용하기가 까다롭습니다.&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;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;--darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot;&gt;&lt;span style=&quot;text-align: start; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot;&gt;때문에, 함수 수준에서 손쉽게 사용 가능한 템플릿 추론기능을 사용하여 chars_term과 chars_byte를 만들어 임시객체인 chars_helper을 반환하여 배열에 복사되도록 합니다.&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;--darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot;&gt;&lt;span style=&quot;text-align: start; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot;&gt;C++14와 C++17의 템플릿 추론&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&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;color: #c8c3bc; text-align: start; --darkreader-inline-color: var(--darkreader-text-c8c3bc, #c1bcb4);&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;C++14까지는&amp;nbsp;템플릿 함수의 경우, 함수에 전달된 인자의&amp;nbsp;타입을 보고&amp;nbsp;컴파일러가 템플릿의 타입을&amp;nbsp;자동으로 추론해 줍니다. 그래서 따로 명시적으로 타입을 적지 않아도 컴파일러가 알아서 타입을 찾아줍니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #c8c3bc; text-align: start; --darkreader-inline-color: var(--darkreader-text-c8c3bc, #c1bcb4);&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #c8c3bc; text-align: start; --darkreader-inline-color: var(--darkreader-text-c8c3bc, #c1bcb4);&quot; data-start=&quot;163&quot; data-end=&quot;293&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;하지만&amp;nbsp;템플릿 클래스의 경우는 다릅니다. 생성자에 값을 전달하더라도, 컴파일러는 그 값을 보고 클래스 템플릿의 타입을&amp;nbsp;자동으로 추론하지 못합니다.&lt;/span&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1752966894142&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;template&amp;lt;typename T&amp;gt;
void print(T value)
{
  std::cout &amp;lt;&amp;lt; value &amp;lt;&amp;lt; std::endl;
}

print(10); // 컴파일러가 T를 int로 자동 추론합니다.


template&amp;lt;typename T&amp;gt;
class Box
{
public:
  Box(T value) {}
};

Box&amp;lt;int&amp;gt; b(10); // T를 int로 명시해야 합니다.&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;함수 템플릿은 값을 보면 타입을 추론하지만, 클래스 템플릿은 값을 봐도 타입을 추론하지 못합니다.&lt;/p&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++17 부터는 클래스도 &lt;span style=&quot;background-color: #fafafa; color: #333333; text-align: start;&quot; data-darkreader-inline-bgcolor=&quot;&quot; data-darkreader-inline-color=&quot;&quot;&gt;컴파일러가 T를 int로 자동 추론합니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1752967369473&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Box b(10); // T를 int로 자동 추론&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;더 복잡한 템플릿의 경우 추론 가이드(deduction guide)를 직접 정의해주어야 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1752967121488&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;template&amp;lt;typename T1, typename T2&amp;gt;
class Pair 
{
  ....
};

// Deduction guide를 명시적으로 추가
template&amp;lt;typename T1, typename T2&amp;gt;
Pair(T1, T2) -&amp;gt; Pair&amp;lt;T1, T2&amp;gt;;

Pair p(42, std::string(&quot;Hello&quot;));  // Pair&amp;lt;int, std::string&amp;gt;로 추론됨&lt;/code&gt;&lt;/pre&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;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;자동화된 최적화&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;--darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot;&gt;&lt;span style=&quot;--darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot;&gt;C++17부터는 chars_term이나 chars_byte처럼 임시객체 반환의 경우 복사 생략(copy elision)이 의무화되었기 때문에 복사비용이 들지 않습니다. 하지만 &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot;&gt;&lt;span style=&quot;--darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot;&gt;C++11은 컴파일러의 선택적 사항이며 최적화 옵션인 -O2의 경우 대부분 RVO가 적용될 수 있다고 알려져 있습니다.&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;--darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot;&gt;끝맺음&lt;/span&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;이 클래스는 컴파일 시 배열의 크기를 자동으로 추론하여 안전하게 문자열을 복사하고, 널 종단 문자 처리 여부를 유연하게 선택할 수 있도록 설계되었습니다. 결과적으로 반복적인 코드와 실수 가능성을 줄여주며 기존 C++ 코드베이스를 현대적인 방식으로 안전하게 브릿징 할 수 있습니다.&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;color: #000000; text-align: start; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&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;있습니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>C++</category>
      <category>C++17</category>
      <category>copy</category>
      <category>cpp</category>
      <category>template</category>
      <category>배열</category>
      <author>tys</author>
      <guid isPermaLink="true">https://cplusplus.tistory.com/21</guid>
      <comments>https://cplusplus.tistory.com/entry/CPP17-%EC%B6%94%EB%A1%A0%EA%B8%B0%EB%8A%A5%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EC%95%88%EC%A0%84%ED%95%9C-%EB%B0%B0%EC%97%B4-%EB%B3%B5%EC%82%AC#entry21comment</comments>
      <pubDate>Thu, 10 Jul 2025 14:50:19 +0900</pubDate>
    </item>
    <item>
      <title>C++ std::list와 빠른 검색을 동시에: FindableList</title>
      <link>https://cplusplus.tistory.com/entry/C-stdlist%EC%99%80-%EB%B9%A0%EB%A5%B8-%EA%B2%80%EC%83%89%EC%9D%84-%EB%8F%99%EC%8B%9C%EC%97%90-FindableList</link>
      <description>&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;&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;h3 data-ke-size=&quot;size23&quot;&gt;인터페이스&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 어떻게 사용할지 만들어 봅니다.&lt;/p&gt;
&lt;pre id=&quot;code_1752059809064&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;

....

int main(void)
{
  // 원소 타입을 받을 수 있도록 합니다.
  FindableList&amp;lt;std::string&amp;gt; flist;
  .......

  // 원소를 앞,뒤로 추가할 수 있도록 합니다.
  flist.push_back(&quot;one&quot;);
  flist.push_back(&quot;two&quot;);
  flist.push_back(&quot;three&quot;);
  flist.push_front(&quot;zero&quot;);

  // 원소에 접근할 수 있도록 front와 back 메서드를 제공합니다.
  std::optional&amp;lt;std::string&amp;gt; front_val = flist.front();
  std::optional&amp;lt;std::string&amp;gt; back_val  = flist.back();

  // 원소에서 값을 추출할 수 있도록 합니다.
  // 최근의 C++ 표준에서는 STL에 extract 메서드가 추가 되어 있는데요,
  // 여기서도 extract 메서드를 사용하여 원소를 추출할 수 있도록 하겠습니다.
  std::optional&amp;lt;std::string&amp;gt; ext_val1 = flist.extract_front();
  std::optional&amp;lt;std::string&amp;gt; ext_val2 = flist.extract_back();

  // 원소 찾기
  if (flist.find(&quot;one&quot;) == true)
    std::cout &amp;lt;&amp;lt; &quot;one이 있습니다.&quot; &amp;lt;&amp;lt; std::endl;
    
  flist.erase(&quot;one&quot;);
  flist.erase_if(&quot;two&quot;, [&amp;amp;](const std::string &amp;amp;v) { return true; });
  
  // 상태 확인
  std::cout &amp;lt;&amp;lt; flist.size()  &amp;lt;&amp;lt; std::endl;
  std::cout &amp;lt;&amp;lt; flist.empty() &amp;lt;&amp;lt; std::endl; // 0 (false)

  // 원소 전체를 입력된 순으로 가져오기
  std::list&amp;lt;std::string&amp;gt; container = flist.container();
  for (const auto&amp;amp; item : container)
    std::cout &amp;lt;&amp;lt; item.first &amp;lt;&amp;lt; &quot;: &quot; &amp;lt;&amp;lt; item.second &amp;lt;&amp;lt; std::endl;

  flist.clear();
  ...
}&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;std::list는 중간 삽입(insert)이 있습니다만, 특정 값을 삭제할 수 있는 기능은 그대로 유지하고, 삽입 순서가 지켜저야 하므로 중간 insert는 제외시켰습니다. 중간 삽입이 있는 경우 이 클래스의 성격이 뭔가 이상해지기도 합니다.&lt;/p&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을 사용하기 위해서는 iterator를 사용하기 어려우므로 순회기능인 for_each함수도 추가합니다.&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;인터페이스는 위의 구성으로 할 텐데요, 데이터를 순서대로 담을 수 있도록 하고, 또 검색도 돼야 하므로 list와 map을 사용해야 합니다. 그리고 많은 데이터의 검색이 용이하도록 unordered_map을 사용합니다. std::unordered_map은 대용량에서 std::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;여기서는 list의 insert기능을 추가하지 않았으므로 unordered_map이 가능합니다. 하지만 insert기능을 추가하려면 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;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;unordered_map의 값으로는 std::set&amp;lt;std::list::iterator&amp;gt;으로 합니다. 중복된 값이 있을 수 있기 때문입니다. T라는 값이 std::list에 어느 위치에 있는지를 담은 정보입니다. 이것도 검색과 삭제가 쉽도록 std::set으로 구성합니다.&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_1752062113113&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;template&amp;lt;typename T,
         typename HASH = std::hash&amp;lt;T&amp;gt;,
         typename PRED = std::equal_to&amp;lt;T&amp;gt;&amp;gt;
class FindableKeyList
{
public:
  using container_type = std::list&amp;lt;T&amp;gt;;
  ...........

private:
  // T를 담을 컨테이너
  container_type list_;

private:
  // 값을 검색하는데 있어 중복 저장과 검색이 가능하도록 unordered_map에 set을 값으로 사용하며 
  // list::iterator를 담도록 합니다.
  // set은 담는 대상이 비교연산이 가능해야 하므로 아래와 같이 값의 주소값을 사용하는
  // 별도의 비교연산 함수를 만듭니다.
  struct it_comparator
  {
    bool operator()(const typename container_type::iterator &amp;amp;a,
                    const typename container_type::iterator &amp;amp;b) const
    { return &amp;amp;(*a) &amp;lt; &amp;amp;(*b); }
  };

  // 위에서 작성한 it_comparator로 비교연산 함수를 set에 추가합니다.
  std::unordered_map&amp;lt;T, std::set&amp;lt;typename container_type::iterator,
                                 it_comparator&amp;gt;, HASH, PRED&amp;gt; mapk_;

private:
  bool              use_lock_ = false;
  mutable SpinLock  lock_;
};&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;이 클래스는 쓰레드에 안전하도록 lock을 사용할 텐데요, 이전 포스팅에서 만들어 두었던 SpinLock을 사용할 겁니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1752063033370&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;C++ 유틸리티: TPS 측정과 SpinLock&quot; data-og-description=&quot;프로젝트에 참여해서 일을 하다 보면 TPS를 측정해야 할 일이 종종 있습니다. 이미 프로젝트에 사용되던 TPS소스를 사용하려 했지만 사용이 쉽지 않았습니다. TPS측정 소스를 보면 시간별 버킷을 &quot; data-og-host=&quot;cplusplus.tistory.com&quot; data-og-source-url=&quot;https://cplusplus.tistory.com/entry/C-%EC%9C%A0%ED%8B%B8%EB%A6%AC%ED%8B%B0-TPS-%EC%B8%A1%EC%A0%95%EA%B3%BC-SpinLock&quot; data-og-url=&quot;https://cplusplus.tistory.com/entry/C-%EC%9C%A0%ED%8B%B8%EB%A6%AC%ED%8B%B0-TPS-%EC%B8%A1%EC%A0%95%EA%B3%BC-SpinLock&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/eP6t6/hyZjAGTZ0l/kZBUBHZLejKkVMU1PPOrak/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/fMeIU/hyZf9KZLJc/tKjSkaHTImVxKRNgP1frr1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800&quot;&gt;&lt;a href=&quot;https://cplusplus.tistory.com/entry/C-%EC%9C%A0%ED%8B%B8%EB%A6%AC%ED%8B%B0-TPS-%EC%B8%A1%EC%A0%95%EA%B3%BC-SpinLock&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://cplusplus.tistory.com/entry/C-%EC%9C%A0%ED%8B%B8%EB%A6%AC%ED%8B%B0-TPS-%EC%B8%A1%EC%A0%95%EA%B3%BC-SpinLock&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/eP6t6/hyZjAGTZ0l/kZBUBHZLejKkVMU1PPOrak/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/fMeIU/hyZf9KZLJc/tKjSkaHTImVxKRNgP1frr1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800');&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;C++ 유틸리티: TPS 측정과 SpinLock&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;프로젝트에 참여해서 일을 하다 보면 TPS를 측정해야 할 일이 종종 있습니다. 이미 프로젝트에 사용되던 TPS소스를 사용하려 했지만 사용이 쉽지 않았습니다. TPS측정 소스를 보면 시간별 버킷을&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;cplusplus.tistory.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;p data-ke-size=&quot;size16&quot;&gt;선언부&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;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1752108471458&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;template&amp;lt;typename T,
         typename HASH = std::hash&amp;lt;T&amp;gt;,
         typename PRED = std::equal_to&amp;lt;T&amp;gt;&amp;gt;
class FindableList
{
public:
  // 타입 별명
  using container_type = std::list&amp;lt;T&amp;gt;;

  template&amp;lt;typename R&amp;gt;
  using optional_ref = std::optional&amp;lt;std::reference_wrapper&amp;lt;R&amp;gt;&amp;gt;;

  // 생성자
  FindableList(bool use_lock = false);

  // 컨테이너 접근
  container_type        container    () const;
  const container_type &amp;amp;container_ref() const;

  // 저장
  void push_front(const T &amp;amp;item);
  void push_back (const T &amp;amp;item);

  // 검색
  bool find(const T &amp;amp;item) const;
  std::optional&amp;lt;T&amp;gt; front();
  std::optional&amp;lt;T&amp;gt; back ();

  // 추출
  std::optional&amp;lt;T&amp;gt; extract_front();
  std::optional&amp;lt;T&amp;gt; extract_back ();

  // 삭제 연산
  void pop_back ();
  void pop_front();
  bool erase    (const T &amp;amp;item);
  
  // 이 컨테이너는 중복된 요소를 허용하므로, 해당 item이 여러 번 존재할 수 있습니다.
  // 따라서, erase_if는 해당 item이 존재하는 모든 요소를 삭제합니다.
  // locked_func는 item을 기준으로 true인 경우에만 삭제합니다.
  void erase_if (const T &amp;amp;item, std::function&amp;lt;bool(const T &amp;amp;)&amp;gt; locked_func);

  // 순회 연산
  void for_each(std::function&amp;lt;void(const T &amp;amp;)&amp;gt; locked_func) const;
  bool for_each(std::function&amp;lt;bool(const T &amp;amp;)&amp;gt; locked_func) const;

  // 크기 및 상태
  size_t size () const;
  bool   empty() const;
  void   clear();

private:
  // 멤버 변수
  container_type list_;

private:
  struct it_comparator
  {
    bool operator()(const typename container_type::iterator &amp;amp;a,
                    const typename container_type::iterator &amp;amp;b) const
    { return &amp;amp;(*a) &amp;lt; &amp;amp;(*b); }
  };

  std::unordered_map&amp;lt;T, std::set&amp;lt;typename container_type::iterator,
                                 it_comparator&amp;gt;, HASH, PRED&amp;gt; mapk_;

private:
  bool              use_lock_ = false;
  mutable SpinLock  lock_;
};&lt;/code&gt;&lt;/pre&gt;
&lt;/div&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;구현부&lt;/p&gt;
&lt;pre id=&quot;code_1752200054490&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;template&amp;lt;typename T, typename HASH, typename PRED&amp;gt;
FindableList&amp;lt;T, HASH, PRED&amp;gt;::FindableList(bool use_lock)
: use_lock_(use_lock)
{
}

template&amp;lt;typename T, typename HASH, typename PRED&amp;gt;
typename FindableList&amp;lt;T, HASH, PRED&amp;gt;::container_type
FindableList&amp;lt;T, HASH, PRED&amp;gt;::container() const
{
  SpinLockGuard guard(lock_, use_lock_);
  return list_;
}

template&amp;lt;typename T, typename HASH, typename PRED&amp;gt;
const typename FindableList&amp;lt;T, HASH, PRED&amp;gt;::container_type &amp;amp;
FindableList&amp;lt;T, HASH, PRED&amp;gt;::container_ref() const
{
  return list_;
}

template&amp;lt;typename T, typename HASH, typename PRED&amp;gt;
void
FindableList&amp;lt;T, HASH, PRED&amp;gt;::push_front(const T &amp;amp;item)
{
  SpinLockGuard guard(lock_, use_lock_);
  list_.push_front(item);
  mapk_[item].insert(list_.begin());
}

template&amp;lt;typename T, typename HASH, typename PRED&amp;gt;
void
FindableList&amp;lt;T, HASH, PRED&amp;gt;::push_back(const T &amp;amp;item)
{
  SpinLockGuard guard(lock_, use_lock_);
  list_.push_back (item);
  mapk_[item].insert(std::prev(list_.end()));
}

template&amp;lt;typename T, typename HASH, typename PRED&amp;gt;
bool
FindableList&amp;lt;T, HASH, PRED&amp;gt;::find(const T &amp;amp;item) const
{
  SpinLockGuard guard(lock_, use_lock_);
  return mapk_.count(item) &amp;gt; 0;
}

template&amp;lt;typename T, typename HASH, typename PRED&amp;gt;
std::optional&amp;lt;T&amp;gt;
FindableList&amp;lt;T, HASH, PRED&amp;gt;::front()
{
  SpinLockGuard guard(lock_, use_lock_);
  if (list_.empty() == true)
    return std::nullopt;

  return list_.front();
}

template&amp;lt;typename T, typename HASH, typename PRED&amp;gt;
std::optional&amp;lt;T&amp;gt;
FindableList&amp;lt;T, HASH, PRED&amp;gt;::back()
{
  SpinLockGuard guard(lock_, use_lock_);
  if (list_.empty() == true)
    return std::nullopt;

  return list_.back();
}

template&amp;lt;typename T, typename HASH, typename PRED&amp;gt;
std::optional&amp;lt;T&amp;gt;
FindableList&amp;lt;T, HASH, PRED&amp;gt;::extract_front()
{
  SpinLockGuard guard(lock_, use_lock_);
  if (list_.empty() == true)
    return std::nullopt;

  auto item = list_.front();

  mapk_[item].erase(list_.begin());
  if (mapk_[item].empty() == true)
    mapk_.erase(item);

  list_.pop_front();
  return item;
}

template&amp;lt;typename T, typename HASH, typename PRED&amp;gt;
std::optional&amp;lt;T&amp;gt;
FindableList&amp;lt;T, HASH, PRED&amp;gt;::extract_back()
{
  SpinLockGuard guard(lock_, use_lock_);
  if (list_.empty() == true)
    return std::nullopt;

  auto item = list_.back();

  mapk_[item].erase(std::prev(list_.end()));
  if (mapk_[item].empty() == true)
    mapk_.erase(item);

  list_.pop_back();
  return item;
}

template&amp;lt;typename T, typename HASH, typename PRED&amp;gt;
void
FindableList&amp;lt;T, HASH, PRED&amp;gt;::pop_front()
{
  this-&amp;gt;extract_front();
}

template&amp;lt;typename T, typename HASH, typename PRED&amp;gt;
void
FindableList&amp;lt;T, HASH, PRED&amp;gt;::pop_back()
{
  return this-&amp;gt;extract_back();
}

template&amp;lt;typename T, typename HASH, typename PRED&amp;gt;
bool
FindableList&amp;lt;T, HASH, PRED&amp;gt;::erase(const T &amp;amp;item)
{
  SpinLockGuard guard(lock_, use_lock_);
  auto it = mapk_.find(item);
  if (it == mapk_.end())
    return false;

  for (auto &amp;amp;list_it : it-&amp;gt;second)
    list_.erase(list_it);

  mapk_.erase(it);
  return true;
}

template&amp;lt;typename T, typename HASH, typename PRED&amp;gt;
void
FindableList&amp;lt;T, HASH, PRED&amp;gt;::erase_if(const T &amp;amp;item, std::function&amp;lt;bool(const T &amp;amp;)&amp;gt; locked_func)
{
  SpinLockGuard guard(lock_, use_lock_);
  auto it = mapk_.find(item);
  if (it == mapk_.end())
    return;

  auto &amp;amp;mapk_set = it-&amp;gt;second;
  for (auto set_it = mapk_set.begin(); set_it != mapk_set.end();)
  {
    auto list_it = *set_it;
    if (locked_func(*list_it) == false)
    {
      ++set_it;
      continue;
    }

    list_.erase(list_it);
    set_it = mapk_set.erase(set_it);
  }

  if (mapk_set.empty() == true)
    mapk_.erase(it);
}

template&amp;lt;typename T, typename HASH, typename PRED&amp;gt;
void
FindableList&amp;lt;T, HASH, PRED&amp;gt;::for_each(std::function&amp;lt;void(const T &amp;amp;)&amp;gt; locked_func) const
{
  SpinLockGuard guard(lock_, use_lock_);
  for (const auto &amp;amp;item : list_)
    locked_func(item);
}

template&amp;lt;typename T, typename HASH, typename PRED&amp;gt;
bool
FindableList&amp;lt;T, HASH, PRED&amp;gt;::for_each(std::function&amp;lt;bool(const T &amp;amp;)&amp;gt; locked_func) const
{
  SpinLockGuard guard(lock_, use_lock_);
  for (const auto &amp;amp;item : list_)
    if (locked_func(item) == false)
      return false;

  return true;
}

template&amp;lt;typename T, typename HASH, typename PRED&amp;gt;
size_t
FindableList&amp;lt;T, HASH, PRED&amp;gt;::size() const
{
  SpinLockGuard guard(lock_, use_lock_);
  return list_.size();
}

template&amp;lt;typename T, typename HASH, typename PRED&amp;gt;
bool
FindableList&amp;lt;T, HASH, PRED&amp;gt;::empty() const
{
  SpinLockGuard guard(lock_, use_lock_);
  return list_.empty();
}

template&amp;lt;typename T, typename HASH, typename PRED&amp;gt;
void
FindableList&amp;lt;T, HASH, PRED&amp;gt;::clear()
{
  SpinLockGuard guard(lock_, use_lock_);
  list_.clear();
  mapk_.clear();
}&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;&lt;span style=&quot;color: #000000; text-align: start;&quot; data-darkreader-inline-color=&quot;&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;있습니다.&lt;/span&gt;&lt;/p&gt;</description>
      <author>tys</author>
      <guid isPermaLink="true">https://cplusplus.tistory.com/20</guid>
      <comments>https://cplusplus.tistory.com/entry/C-stdlist%EC%99%80-%EB%B9%A0%EB%A5%B8-%EA%B2%80%EC%83%89%EC%9D%84-%EB%8F%99%EC%8B%9C%EC%97%90-FindableList#entry20comment</comments>
      <pubDate>Wed, 9 Jul 2025 12:47:24 +0900</pubDate>
    </item>
    <item>
      <title>C++ 멀티쓰레드 데이터 관리: 샤딩(Sharding)</title>
      <link>https://cplusplus.tistory.com/entry/C-%EB%A9%80%ED%8B%B0%EC%93%B0%EB%A0%88%EB%93%9C-%EB%8D%B0%EC%96%B4%ED%8B%B0-%EA%B4%80%EB%A6%AC-%EC%83%A4%EB%94%A9Sharding</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;멀티쓰레드 애플리케이션을 개발하다 보면 여러 쓰레드가 하나의 자료구조에 동시에 접근해야 할 때가 있습니다. &lt;span style=&quot;text-align: start;&quot;&gt;물론 thread_local을 떠 올릴수도 있지만 사용하지 못하는 대부분의 상황에서&lt;/span&gt;&amp;nbsp;std::mutex로 보호된 컨테이너에 동시에 접근한다면 오히려 단일 쓰레드보다 느려집니다.&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;이 문제의 핵심은 모든 쓰레드가 하나의 락을 두고 경쟁한다는 점입니다. 한 번에 하나의 쓰레드만 데이터에 접근하기 때문에 다른 쓰레드는 기다려야 합니다. 마치&amp;nbsp;은행에&amp;nbsp;창구가&amp;nbsp;하나밖에&amp;nbsp;없는&amp;nbsp;상황과&amp;nbsp;같습니다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;샤딩은 이 문제를 해결하는 방법중 하나입니다. 하나의 큰 자료구조를 여러 개의 작은 조각으로 나누고, 각 조각마다 별도의 락을 사용합니다. 이렇게 하면 쓰레드들이 서로 다른 샤드에 접근할 때는 경합이 줄어 성능향상에 도움이 됩니다. 8개 창구가 있는 은행처럼.&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;color: #000000; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot;&gt;여기서는 C++에서 멀티쓰레드 환경에서 발생하는 데이터 동시 접근 문제와 이를 해결하기 위한 샤딩(Sharding)을 어떻게 설계하고 사용하는지 알아봅니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;

            &lt;figure class=&quot;unsupported component-kakaotv&quot; contenteditable=&quot;false&quot; style=&quot;background:#000;margin:16px 0;min-height:72px;padding:10px 16px;display:flex;align-items:center;justify-content:center;text-align:center;box-sizing:border-box;width:100%;max-width:100%;&quot;&gt;
                &lt;p contenteditable=&quot;false&quot; style=&quot;margin:0;color:#8a8a8a;font-size:13px;line-height:1.6;user-select:none;pointer-events:none;&quot;&gt;동영상 서비스가 종료되어 해당 콘텐츠를 재생할 수 없습니다.&lt;/p&gt;
            &lt;/figure&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; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;샤딩 적용전의 인터페이스&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;기본적인 예제를 가정하기 위해 두 개의 클래스를 생각해 보겠습니다. &lt;span style=&quot;text-align: start;&quot;&gt;여기서&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;text-align: start;&quot;&gt;User의 키값은 id로 하겠습니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1860&quot; data-origin-height=&quot;668&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4vrW5/btsOg0LViD2/UKWgrL6hcnUNrppkiPk8Ok/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4vrW5/btsOg0LViD2/UKWgrL6hcnUNrppkiPk8Ok/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4vrW5/btsOg0LViD2/UKWgrL6hcnUNrppkiPk8Ok/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4vrW5%2FbtsOg0LViD2%2FUKWgrL6hcnUNrppkiPk8Ok%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;1860&quot; height=&quot;668&quot; data-origin-width=&quot;1860&quot; data-origin-height=&quot;668&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&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;pre id=&quot;code_1748494115314&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class User
{
public:
  std::string id;
  std::string name;
  std::string email;
};

class UserManager
{
public:
  void add(const User &amp;amp;user)
  {
    std::lock_guard&amp;lt;std::mutex&amp;gt; guard(lock_);
    users[user.id] = user;
  }
  
  std::optional&amp;lt;User&amp;gt; 
  get(const std::string &amp;amp;user_id)
  {
    std::lock_guard&amp;lt;std::mutex&amp;gt; guard(lock_);
    auto it = users.find(user_id);
    if (it != users.end())
      return it-&amp;gt;second;

    return std::nullopt;
  }

  bool del(int user_id)
  {
    std::lock_guard&amp;lt;std::mutex&amp;gt; lock(lock_);
    return users.erase(user_id) &amp;gt; 0;
  }
private:
  std::mutex lock_;
  std::unordered_map&amp;lt;int, User&amp;gt; users_;
};&lt;/code&gt;&lt;/pre&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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;User를 관리하는 UserManager에 접근시마다 매번 락이 발생하게 됩니다. 차라리 싱글 쓰레드에서 처리하는 것이 더 빠를 것입니다. 싱글 쓰레드에서 처리하게 되면 락을 거는 소모는 없기 때문입니다.&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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;샤딩 적용해 보기&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2050&quot; data-origin-height=&quot;679&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cICPEx/btsOjUcwH1z/MQ5spqEqqD1SPTND3ZgHsK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cICPEx/btsOjUcwH1z/MQ5spqEqqD1SPTND3ZgHsK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cICPEx/btsOjUcwH1z/MQ5spqEqqD1SPTND3ZgHsK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcICPEx%2FbtsOjUcwH1z%2FMQ5spqEqqD1SPTND3ZgHsK%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;2050&quot; height=&quot;679&quot; data-origin-width=&quot;2050&quot; data-origin-height=&quot;679&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;&lt;span style=&quot;color: #000000; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot;&gt;중요한 것은 샤딩을 적용할 때 어떤 값을 기준으로 조각낼지 결정해야 합니다. 즉, 샤드된 객체(UserBucket)에 골고루 데이터가 분산되어야 락 경쟁이 줄어들기 때문입니다. 여기서는 키값인 &lt;span style=&quot;color: #000000; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot;&gt;User::id로 하겠습니다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;UserManager는 해시값을 MOD연산하여 std::vector에 인덱스별로 &lt;span style=&quot;color: #000000; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot;&gt;UserBucket을&amp;nbsp;&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;h4 data-ke-size=&quot;size20&quot;&gt;정제하기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot;&gt;음.......생각해보니 UserManager와 UserBucket에 함수가 같습니다. UserBucket가 변경될때 마다 UserManager도 변경해야 하네요. 그래서 다시 정의해 봅니다.&lt;/span&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1592&quot; data-origin-height=&quot;476&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/benT1k/btsQVHIMeeS/QkIEgGDIIhHyIOS45kkTE1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/benT1k/btsQVHIMeeS/QkIEgGDIIhHyIOS45kkTE1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/benT1k/btsQVHIMeeS/QkIEgGDIIhHyIOS45kkTE1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbenT1k%2FbtsQVHIMeeS%2FQkIEgGDIIhHyIOS45kkTE1%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;1592&quot; height=&quot;476&quot; data-origin-width=&quot;1592&quot; data-origin-height=&quot;476&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;이제 UserManager는 UserBucket를 참조로 반환하므로 복사없이 직접 접근이 가능해 졌습니다. 이렇게 하면 아래와 같이 메서드 체이닝을 이용하여 호출할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1748577143030&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;UserManager user_manager;

auto user = user_manager.bucket().get(&quot;my_id&quot;);
if (user.has_value() == false)
{
  std::cout &amp;lt;&amp;lt; &quot;없는 유저입니다.&quot; &amp;lt;&amp;lt; std::endl;
  return;
}

std::cout &amp;lt;&amp;lt; &quot;이메일은 &quot; &amp;lt;&amp;lt; user-&amp;gt;email &amp;lt;&amp;lt; &quot; 입니다.&quot; std::endl;&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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;위와 같은 구현에서 UserBucket은 락을 사용하겠지만, &lt;b&gt;UserManager는 락을 사용하지 않아야 의미가 있습니다.&lt;/b&gt; 그래서 UserManager는 아래와 같이 구현될 수 있습니다.&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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;UserManager 구현&lt;/span&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;생성자&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1748500314360&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class UserManager
{
public:
  UserManager(size_t shard_size = 100) : shard_size_(shard_size)
  {
    buckets_.resize(shard_size_);
  }

private:
  const size_t shard_size_ = 100;
  std::vector&amp;lt;UserBucket&amp;gt; buckets_;
};&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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;생성자에서 미리 std::vector&amp;lt;UserBucket&amp;gt; 생성해 놓습니다. shard 개수는 &lt;span style=&quot;text-align: start;&quot;&gt;애플리케이션이&lt;/span&gt; 존재하는 동안 동일해야 하므로 생성자에서 생성해 놓으면 &quot;std::vector&amp;lt;UserBucket&amp;gt; buckets_&quot; 은 read only 자료구조가 되어 락을 사용할 필요가 없습니다. 이때 UserBucket은 UserManager가 직접 수명을 관리하게 됩니다.&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;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;std::vector에 사용될 index값은 size_t 타입이어야 합니다. User의 키값은 id는 std::string입니다. 따라서 C++ 표준 라이브러리의 스트링 해시를 사용하여 vector에 개별 요소에 접근시 인덱스값으로 사용할 수 있습니다.&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;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;UserBucket 가져오기&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;User클래스의 키값은 std::string id 이기 때문에 이를 해시값으로 만들고 이 해시값을 &lt;span style=&quot;color: #000000; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot;&gt;MOD &lt;/span&gt;연산하여 UserBucket를 가져오도록 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1748503042615&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class UserManager
{
public:
  UserManager(size_t shard_size = 10) : shard_size_(shard_size)
  {
    buckets_.resize(shard_size_);
  }

  UserBucket &amp;amp;bucket(const std::string &amp;amp;id)
  {
    size_t hash_value = std::hash&amp;lt;std::string&amp;gt;{}(id);
    return buckets_[hash_value % shard_size_];
  }

  const UserBucket &amp;amp;bucket(const std::string &amp;amp;id) const
  {
    size_t hash_value = std::hash&amp;lt;std::string&amp;gt;{}(id);
    return buckets_[hash_value % shard_size_];
  }

private:
  const size_t shard_size_ = 10;
  std::vector&amp;lt;UserBucket&amp;gt; buckets_;
};&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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;const 함수도 구현해 줍니다. const키워드가 있는 함수에서 호출할 수 있기 때문입니다. &lt;/span&gt;구현체를 보면 매우 간단합니다. 그저 UserBucket만을 리턴하고 있습니다.&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;UserBucket 구현&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UserBucket는 모든 메서드에서 락을 필요로 합니다. UserManager에서 shard_size값에 따라 나누어지므로 이 버켓은 shard_size개수 만큼 만들어 집니다. 따라서 shard_size만큼 락 경쟁이 줄어듭니다. 만약 shard_size가 100이라면 이론적으로 1/100의 확률로 락 경쟁이 발생합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1748574808361&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class UserBucket
{
public:
  void add(const User &amp;amp;user)
  {
    std::unique_lock&amp;lt;std::mutex&amp;gt; guard(lock_);
    users_[user.id] = user;
  }

  std::optional&amp;lt;User&amp;gt; 
    get(const std::string &amp;amp;user_id) const
  {
    std::lock_guard&amp;lt;std::mutex&amp;gt; guard(lock_);
    // c++17에서 추가된 문법입니다.
    if (auto it = users_.find(user_id); it != users_.end())
      return it-&amp;gt;second;

    return std::nullopt;
  }
  
  std::optional&amp;lt;User&amp;gt;
    extract(const std::string &amp;amp;user_id)
  {
    std::lock_guard&amp;lt;std::mutex&amp;gt; guard(lock_);
    if (auto it = users_.find(user_id); it != users_.end())
    {
      auto node = users_.extract(it);
      return std::move(node.mapped());
    }

    return std::nullopt;
  }

  void del(const std::string &amp;amp;user_id)
  {
    std::lock_guard&amp;lt;std::mutex&amp;gt; guard(lock_);
    users_.erase(user_id);
  }

private:
  // lock_은 락을 걸때 변경작업이 있으므로 const함수에서도
  // 사용할 수 있도록 mutable로 정의합니다.
  mutable std::mutex lock_;
  std::unordered_map&amp;lt;std::string, User&amp;gt; users_;
};&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;C++17에는 extract함수가 추가 되었습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1748575255352&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;auto node = users_.extract(it);
return std::move(node.mapped());&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;여기서 node는 std::unordered_map&amp;lt;std::string, User&amp;gt;::node_type 타입으로 키-값 쌍을 담고 있고, 다음 메서드들을 제공합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;node_type::key() : 키에 접근&lt;/li&gt;
&lt;li&gt;node_type::mapped() : 값에 접근&lt;/li&gt;
&lt;li&gt;node_type::empty() : 비어있는지 확인&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;node.mapped()로 User 객체에 접근할 수 있습니다. extract는 요소를 맵에서 제거하면서 동시에 그 내용을 node로 옮겨줍니다. std::move는 node.mapped()의 값은 참조라서 명시적으로 move를 사용하여 복사 없이 이동되도록 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&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: #000000; text-align: start;&quot; data-darkreader-inline-color=&quot;&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;있습니다.&lt;/span&gt;&lt;/p&gt;</description>
      <author>tys</author>
      <guid isPermaLink="true">https://cplusplus.tistory.com/19</guid>
      <comments>https://cplusplus.tistory.com/entry/C-%EB%A9%80%ED%8B%B0%EC%93%B0%EB%A0%88%EB%93%9C-%EB%8D%B0%EC%96%B4%ED%8B%B0-%EA%B4%80%EB%A6%AC-%EC%83%A4%EB%94%A9Sharding#entry19comment</comments>
      <pubDate>Thu, 29 May 2025 13:09:03 +0900</pubDate>
    </item>
    <item>
      <title>C++ 템플릿 활용: 실행인자 처리: getopt</title>
      <link>https://cplusplus.tistory.com/entry/CPP-%ED%85%9C%ED%94%8C%EB%A6%BF-%ED%99%9C%EC%9A%A9-%EC%8B%A4%ED%96%89%EC%9D%B8%EC%9E%90-%EC%B2%98%EB%A6%AC-getopt-Arguments-and-options</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;C의 getopt는 프로그램에서 인자 호출 시 많이 사용합니다. 보통은 아래와 같이 사용하는데요, 이렇게 하면 인자 처리하는데 많은 코드 양과 다른 부분들이 뒤섞이면서 좀 복잡해집니다.&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;figure class=&quot;unsupported component-kakaotv&quot; contenteditable=&quot;false&quot; style=&quot;background:#000;margin:16px 0;min-height:72px;padding:10px 16px;display:flex;align-items:center;justify-content:center;text-align:center;box-sizing:border-box;width:100%;max-width:100%;&quot;&gt;
                &lt;p contenteditable=&quot;false&quot; style=&quot;margin:0;color:#8a8a8a;font-size:13px;line-height:1.6;user-select:none;pointer-events:none;&quot;&gt;동영상 서비스가 종료되어 해당 콘텐츠를 재생할 수 없습니다.&lt;/p&gt;
            &lt;/figure&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;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;고전 C 스타일&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1748415075451&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;string.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;
#include &amp;lt;getopt.h&amp;gt;

int main(int argc, char *argv[]) 
{
  int opt;
  int a_flag = 0, b_flag = 0, c_flag = 0;
  int a_value = 0;
  int b_value = 3;  // 기본값
  char c_value[256] = {0};
  
  // 명령줄 인수 파싱
  while ((opt = getopt(argc, argv, &quot;a:b:c:v&quot;)) != -1) 
  {
    switch (opt) 
    {
      case 'a':
        a_flag = 1;
        a_value = atoi(optarg);
        break;
      case 'b':
        b_flag = 1;
        b_value = atoi(optarg);
        break;
      case 'c':
        c_flag = 1;
        strncpy(c_value, optarg, sizeof(c_value) - 1);
        break;
      case 'v':
        printf(&quot;version 1.0.0&quot;);
        return 0;
      default:
        fprintf(stderr, &quot;useage: %s [-a value] [-b value] [-c value]\n&quot;, argv[0]);
        exit(EXIT_FAILURE);
    }
  }
  
  // 필수 옵션 확인
  if (!a_flag) 
  {
    fprintf(stderr, &quot;'a' does not exist.\n&quot;);
    exit(EXIT_FAILURE);
  }
  
  // 값 출력
  printf(&quot;a: %d\n&quot;, a_value);
  printf(&quot;b: %d\n&quot;, b_value);
  
  if (c_flag == false) 
  {
    printf(&quot;c: %s\n&quot;, c_value);
  } 
  else 
  {
    fprintf(stderr, &quot;'c' does not exist.\n&quot;);
    exit(EXIT_FAILURE);
  }
  
  return 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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;다른 오픈소스들도 들여다봅니다. boost라이브러리의 Boost.Program_options은 너무 전문적인 데다, 의존성 이슈가 있습니다.&lt;span style=&quot;text-align: start;&quot;&gt;(boost는 고객사마다 호불호가 크다는 거)&lt;/span&gt;&amp;nbsp;다른 관련 라이브러리 들도 많지만, 딱 원하는 기능이 없어서 직접 만들어 봅니다. 이번에는 C++17에서 아주 자주 사용되는 std::optional을 사용합니다.&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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;C++ 스타일 인터페이스 정의&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1747376654084&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &quot;ArgOptions.h&quot;
#include &amp;lt;iostream&amp;gt;

int main(int argc, char **argv)
{
  try
  {
    // 옵션 정의: a(정수), b(정수, 기본값 3), c(문자열), v(버전처리)
    ArgOptions args(argc, argv, &quot;a:b:c:v&quot;);

    // a 옵션 가져오기, 없으면 throw
    std::cout &amp;lt;&amp;lt; &quot;a: &quot; &amp;lt;&amp;lt; args['a'].as_int32() &amp;lt;&amp;lt; std::endl;
    // b 옵션 가져오기, 없으면 기본값 3
    std::cout &amp;lt;&amp;lt; &quot;b: &quot; &amp;lt;&amp;lt; args['b'].as_int32(3) &amp;lt;&amp;lt; std::endl;
    std::cout &amp;lt;&amp;lt; &quot;c: &quot; &amp;lt;&amp;lt; args['c'].as_str() &amp;lt;&amp;lt; std::endl;
    if (args.contains('v') == true)
      std::cout &amp;lt;&amp;lt; &quot;version 1.0.0&quot; &amp;lt;&amp;lt; std::endl;
  }
  catch (const std::exception &amp;amp;e)
  {
    std::cerr &amp;lt;&amp;lt; e.what() &amp;lt;&amp;lt; std::endl;
    std::cout &amp;lt;&amp;lt; &quot;usage: &quot; &amp;lt;&amp;lt; argv[0] &amp;lt;&amp;lt; &quot; [-a value] [-b value] [-c value]&quot; &amp;lt;&amp;lt; std::endl;
    exit(EXIT_FAILURE);
  }

  return 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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;고전 C스타일에서 객체지향적 C++스타일로 바꾸니 다음과 같은 장점들이 생겼습니다.&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;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;약 50줄에서 약 20줄로 줄었습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;필요한 작업에만 집중되어 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;플래그, 분기 처리등이 없어 이해가 쉽습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;반복 코드가 없습니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;새 옵션 추가가 간단합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;기본값 설정이 간단해졌습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;ArgOptions 구현&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;Modern C++ 기능을 활용하여 다음과 같은 내용들이 포함됩니다.&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;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;템플릿, 예외처리, 연산자 오버로딩, std::optional 사용, 조건부 컴파일&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;생성자&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1747379689746&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 사용
ArgOptions args(argc, argv, &quot;a:b:c:v&quot;);

// 생성자
ArgOptions(int argc, char **argv, const std::string &amp;amp;shortopts)&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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;클래스의 인자전달은 main의 argc와 argv, 그리고 shortopts의 값을 전달하여 파싱하고 사용하도록 위와 같이 정의할 수 있습니다.&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;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;연산자 오버로딩 [ ]&lt;/span&gt;&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;int32_t a = args&lt;span style=&quot;color: #006dd7;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;[&lt;/b&gt;&lt;/span&gt;'a'&lt;span style=&quot;color: #ee2323;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;]&lt;/b&gt;&lt;/span&gt;.as_int32();&lt;/span&gt;&lt;/span&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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;연산자 오버로딩은 std::map에서 볼 수 있는 함수로 C++에서는 &amp;lt;&amp;lt;과 더불어 흔하게 사용하는 함수입니다. &lt;span style=&quot;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;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;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;물론 일부 C개발자 분들은 이런 형식들을 잘 받아들이지 못해서, 외계어 같다고 표현하곤 합니다. 혹은 &amp;lt;&amp;lt;나 &amp;amp;연산자를 비트연산자로 혼동하기도 합니다. C++에서는 이 연산자들을 비트연산에도 사용하지만 스트림 연산자와 참조 변수 선언 등에 사용합니다.&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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;operator이라는 키워드를 제외하고는 그냥 함수입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;args['a'] 의&lt;span style=&quot;text-align: start;&quot;&gt;&amp;nbsp;원형은 이렇게 생겼습니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1747382907826&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;operator[](char name) const&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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;args['a'].as_int32() 은 연산자 함수를 호출한 다음 무언가의 멤버함수를 호출하는 형태입니다. 연산자 함수 [ ] 로 &lt;b&gt;중간 객체&lt;/b&gt;를 생성하고 그 객체의 함수를 호출합니다. &lt;b&gt;중간&lt;/b&gt;&lt;b&gt;&amp;nbsp;객체의&lt;/b&gt;&amp;nbsp;세부 사항에 대해서 최종 사용자는 꼭 알 필요는 없습니다.&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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;ArgOptions의 구현은 이렇습니다.&lt;/span&gt;&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;pre id=&quot;code_1747390517624&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class ArgOptions
{
public:
  ArgOptions(int argc, char **argv, const std::string &amp;amp;shortopts);
  ArgOptions(const std::string &amp;amp;shortopts, int argc, char **argv)
  : ArgOptions(argc, argv, shortopts) {}

  ArgValue
  operator[](char name) const
  {
    auto it = values_.find(name);
    if (it == values_.end())
      return ArgValue(false, name, std::nullopt);

    return ArgValue(true, name, it-&amp;gt;second);
  }

  bool contains(char name) const
  {
    return values_.find(name) != values_.end();
  }

private:
  std::map&amp;lt;char, std::optional&amp;lt;std::string&amp;gt;&amp;gt; values_;
};

inline
ArgOptions::ArgOptions(int argc, char **argv, const std::string &amp;amp;shortopts)
{
  int32_t opt;
  while ((opt = getopt(argc, argv, shortopts.c_str())) != -1)
  {
    if (optarg == nullptr)
    {
      values_[(char)opt] = std::nullopt;
      continue;
    }

    values_[(char)opt] = std::string(optarg);
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;중간 객체 ArgValue&lt;/span&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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;위에서 언급된 코드를 다시 보면 아래와 같습니다.&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #000000; text-align: left;&quot; data-darkreader-inline-color=&quot;&quot;&gt;int32_t a = &lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;args&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;['a']&lt;/span&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;.as_int32();&lt;/span&gt;&lt;/span&gt;&lt;/b&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;저는 &lt;/span&gt;중간 객체를 ArgValue로 하겠습니다.&lt;/span&gt;&amp;nbsp;위의 코드 args['a'] 를 풀어보면 이렇습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1747383545773&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ArgValue value = args['a'];
int32_t a = value.as_int32();&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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;그런데 value를 꼭 선언할 필요는 없으므로 함축적으로 args['a'].as_int32() 로 표현할 수 있습니다. 간단한 메서드 체이닝이라 할 수 있습니다.&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;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;int32 이외에도 char, int16, uint16, int32, uin32, int64, uint64........... str 등을 지원하고 싶습니다. &lt;span style=&quot;text-align: start;&quot;&gt;ArgValue의 형변환 인터페이스를 아래와 같이 구성할 수 있습니다. 때로는 &lt;/span&gt;명시적으로 어떤 형식으로 변환하는지 사용자가 인지하도록 구성하기도 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1747384161831&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;char        as_char  (const std::optional&amp;lt;char&amp;gt;        &amp;amp;def_value = std::nullopt) const;
int16_t     as_int16 (const std::optional&amp;lt;int16_t&amp;gt;     &amp;amp;def_value = std::nullopt) const;
uint16_t    as_uint16(const std::optional&amp;lt;uint16_t&amp;gt;    &amp;amp;def_value = std::nullopt) const;
int32_t     as_int32 (const std::optional&amp;lt;int32_t&amp;gt;     &amp;amp;def_value = std::nullopt) const;
uint32_t    as_uint32(const std::optional&amp;lt;uint32_t&amp;gt;    &amp;amp;def_value = std::nullopt) const;
int64_t     as_int64 (const std::optional&amp;lt;int64_t&amp;gt;     &amp;amp;def_value = std::nullopt) const;
uint64_t    as_uint64(const std::optional&amp;lt;uint64_t&amp;gt;    &amp;amp;def_value = std::nullopt) const;
bool        as_bool  (const std::optional&amp;lt;bool&amp;gt;        &amp;amp;def_value = std::nullopt) const;
std::string as_str   (const std::optional&amp;lt;std::string&amp;gt; &amp;amp;def_value = std::nullopt) const;&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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;C++17 std::optional&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&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; &amp;nbsp; std::optional은 C++17에서 도입된 템플릿 클래스입니다. 이 클래스는 값이 있거나 없을 수 있는 상태를 표현합니다.&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;복사,&amp;nbsp;이동,&amp;nbsp;소멸이&amp;nbsp;안전하게&amp;nbsp;처리됩니다.&lt;/li&gt;
&lt;li&gt;메모리&amp;nbsp;할당을&amp;nbsp;추가로&amp;nbsp;하지&amp;nbsp;않습니다.&lt;/li&gt;
&lt;li&gt;값이&amp;nbsp;없을&amp;nbsp;때&amp;nbsp;접근하면&amp;nbsp;예외가&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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&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;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;템플릿으로 공통 함수 만들기&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;템플릿 함수로 모든 숫자형 타입을 하나의 함수로 만들어 낼 수 있습니다. 그러면 모든 타입에 대해 구현할 필요가 없어집니다. 모양이 다른 bool과 char만 템플릿 특수화를 통해 구현합니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1747384357113&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;template&amp;lt;typename T&amp;gt;
T as(const std::optional&amp;lt;T&amp;gt; &amp;amp;def_value = std::nullopt) const
{
  if (exists_ == false)
  {
    if (def_value == std::nullopt)
      throw std::runtime_error(std::string(&quot;'&quot;) + name_ + &quot;' does not exist.&quot;);

    return def_value.value();
  }

  if (value_ == std::nullopt)
  {
    if (def_value == std::nullopt)
      throw std::runtime_error(std::string(&quot;'&quot;) + name_ + &quot;' value or default required.&quot;);

    return def_value.value();
  }

  if constexpr (std::is_same_v&amp;lt;T, std::string&amp;gt;)
    return *value_;
  else
    return to_value_of_type&amp;lt;T&amp;gt;(*value_);
}

template&amp;lt;typename T&amp;gt; T
to_value_of_type(const std::string &amp;amp;in) const
{
  T out;
  std::stringstream ss;
  ss &amp;lt;&amp;lt; in;
  ss &amp;gt;&amp;gt; out;
  
  if (ss.fail() == true)
  throw std::runtime_error(std::string(&quot;'&quot;) + name_ + &quot;' has wrong value '&quot; + in + &quot;'.&quot;);

  return out;
}

// char 특수화
template&amp;lt;&amp;gt; inline char
ArgValue::to_value_of_type&amp;lt;char&amp;gt;(const std::string &amp;amp;in) const
{
  if (in.size() != 1)
    throw std::runtime_error(std::string(&quot;'&quot;) + name_ + std::string(&quot;' &quot;) + &quot;has wrong value.&quot;);

  return in.front();
}

// bool 특수화
template&amp;lt;&amp;gt; inline bool
ArgValue::to_value_of_type&amp;lt;bool&amp;gt;(const std::string &amp;amp;in) const
{
  if (in != &quot;true&quot; &amp;amp;&amp;amp; in != &quot;false&quot;)
    throw std::runtime_error(std::string(&quot;'&quot;) + name_ + std::string(&quot;' &quot;) + &quot;has wrong value. &quot; + in);

  if (in == &quot;false&quot;)
    return false;

  return  true;
}&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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;아래와 같이 as함수를 이용해서 ArgValue의 형변환 사용자 인터페이스가 완성되었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1747385005621&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;char        as_char   (const std::optional&amp;lt;char&amp;gt;        &amp;amp;def_value = std::nullopt) const { return as&amp;lt;char&amp;gt;        (def_value); }
int16_t     as_int16  (const std::optional&amp;lt;int16_t&amp;gt;     &amp;amp;def_value = std::nullopt) const { return as&amp;lt;int16_t&amp;gt;     (def_value); }
uint16_t    as_uint16 (const std::optional&amp;lt;uint16_t&amp;gt;    &amp;amp;def_value = std::nullopt) const { return as&amp;lt;uint16_t&amp;gt;    (def_value); }
int32_t     as_int32  (const std::optional&amp;lt;int32_t&amp;gt;     &amp;amp;def_value = std::nullopt) const { return as&amp;lt;int32_t&amp;gt;     (def_value); }
uint32_t    as_uint32 (const std::optional&amp;lt;uint32_t&amp;gt;    &amp;amp;def_value = std::nullopt) const { return as&amp;lt;uint32_t&amp;gt;    (def_value); }
int64_t     as_int64  (const std::optional&amp;lt;int64_t&amp;gt;     &amp;amp;def_value = std::nullopt) const { return as&amp;lt;int64_t&amp;gt;     (def_value); }
uint64_t    as_uint64 (const std::optional&amp;lt;uint64_t&amp;gt;    &amp;amp;def_value = std::nullopt) const { return as&amp;lt;uint64_t&amp;gt;    (def_value); }
bool        as_bool   (const std::optional&amp;lt;bool&amp;gt;        &amp;amp;def_value = std::nullopt) const { return as&amp;lt;bool&amp;gt;        (def_value); }
std::string as_str    (const std::optional&amp;lt;std::string&amp;gt; &amp;amp;def_value = std::nullopt) const { return as&amp;lt;std::string&amp;gt; (def_value); }&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;전체 구현 코드&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1747385319856&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#pragma once

#include &amp;lt;string&amp;gt;
#include &amp;lt;map&amp;gt;
#include &amp;lt;sstream&amp;gt;
#include &amp;lt;optional&amp;gt;
#include &amp;lt;cstdint&amp;gt;
#include &amp;lt;getopt.h&amp;gt;

class ArgValue
{
public:
  ArgValue(bool exists, char name, const std::optional&amp;lt;std::string&amp;gt; &amp;amp;value)
  : exists_(exists), name_(name), value_(value) {}

  char        as_char   (const std::optional&amp;lt;char&amp;gt;        &amp;amp;def_value = std::nullopt) const { return as&amp;lt;char&amp;gt;        (def_value); }
  int16_t     as_int16  (const std::optional&amp;lt;int16_t&amp;gt;     &amp;amp;def_value = std::nullopt) const { return as&amp;lt;int16_t&amp;gt;     (def_value); }
  uint16_t    as_uint16 (const std::optional&amp;lt;uint16_t&amp;gt;    &amp;amp;def_value = std::nullopt) const { return as&amp;lt;uint16_t&amp;gt;    (def_value); }
  int32_t     as_int32  (const std::optional&amp;lt;int32_t&amp;gt;     &amp;amp;def_value = std::nullopt) const { return as&amp;lt;int32_t&amp;gt;     (def_value); }
  uint32_t    as_uint32 (const std::optional&amp;lt;uint32_t&amp;gt;    &amp;amp;def_value = std::nullopt) const { return as&amp;lt;uint32_t&amp;gt;    (def_value); }
  int64_t     as_int64  (const std::optional&amp;lt;int64_t&amp;gt;     &amp;amp;def_value = std::nullopt) const { return as&amp;lt;int64_t&amp;gt;     (def_value); }
  uint64_t    as_uint64 (const std::optional&amp;lt;uint64_t&amp;gt;    &amp;amp;def_value = std::nullopt) const { return as&amp;lt;uint64_t&amp;gt;    (def_value); }
  bool        as_bool   (const std::optional&amp;lt;bool&amp;gt;        &amp;amp;def_value = std::nullopt) const { return as&amp;lt;bool&amp;gt;        (def_value); }
  std::string as_str    (const std::optional&amp;lt;std::string&amp;gt; &amp;amp;def_value = std::nullopt) const { return as&amp;lt;std::string&amp;gt; (def_value); }

  template&amp;lt;typename T&amp;gt;
  T as(const std::optional&amp;lt;T&amp;gt; &amp;amp;def_value = std::nullopt) const
  {
    if (exists_ == false)
    {
      if (def_value == std::nullopt)
        throw std::runtime_error(std::string(&quot;'&quot;) + name_ + &quot;' does not exist.&quot;);

      return def_value.value();
    }

    if (value_ == std::nullopt)
    {
      if (def_value == std::nullopt)
        throw std::runtime_error(std::string(&quot;'&quot;) + name_ + &quot;' value or default required.&quot;);

      return def_value.value();
    }

    if constexpr (std::is_same_v&amp;lt;T, std::string&amp;gt;)
      return *value_;
    else
      return to_value_of_type&amp;lt;T&amp;gt;(*value_);
  }

private:
  template&amp;lt;typename T&amp;gt; T
  to_value_of_type(const std::string &amp;amp;in) const
  {
    T out;
    std::stringstream ss;
    ss &amp;lt;&amp;lt; in;
    ss &amp;gt;&amp;gt; out;

    if (ss.fail() == true)
      throw std::runtime_error(std::string(&quot;'&quot;) + name_ + &quot;' has wrong value '&quot; + in + &quot;'.&quot;);

    return out;
  }

private:
  bool exists_;
  char name_;
  std::optional&amp;lt;std::string&amp;gt; value_;
};

template&amp;lt;&amp;gt; inline char
ArgValue::to_value_of_type&amp;lt;char&amp;gt;(const std::string &amp;amp;in) const
{
  if (in.size() != 1)
    throw std::runtime_error(std::string(&quot;'&quot;) + name_ + &quot;' has wrong value.&quot;);

  return in.front();
}

template&amp;lt;&amp;gt; inline bool
ArgValue::to_value_of_type&amp;lt;bool&amp;gt;(const std::string &amp;amp;in) const
{
  if (in != &quot;true&quot; &amp;amp;&amp;amp; in != &quot;false&quot;)
    throw std::runtime_error(std::string(&quot;'&quot;) + name_ + &quot;' has wrong value.&quot;);

  if (in == &quot;false&quot;)
    return false;

  return  true;
}

class ArgOptions
{
public:
  ArgOptions(int argc, char **argv, const std::string &amp;amp;shortopts);
  ArgOptions(const std::string &amp;amp;shortopts, int argc, char **argv)
  : ArgOptions(argc, argv, shortopts) {}

  ArgValue
  operator[](char name) const
  {
    auto it = values_.find(name);
    if (it == values_.end())
      return ArgValue(false, name, std::nullopt);

    return ArgValue(true, name, it-&amp;gt;second);
  }

  bool contains(char name) const
  {
    return values_.find(name) != values_.end();
  }

private:
  std::map&amp;lt;char, std::optional&amp;lt;std::string&amp;gt;&amp;gt; values_;
};

inline
ArgOptions::ArgOptions(int argc, char **argv, const std::string &amp;amp;shortopts)
{
  int32_t opt;
  while ((opt = getopt(argc, argv, shortopts.c_str())) != -1)
  {
    if (optarg == nullptr)
    {
      values_[(char)opt] = std::nullopt;
      continue;
    }

    values_[(char)opt] = std::string(optarg);
  }
}&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;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;끝맺음&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;getopt_long 따위를 사용하여 &quot;--xxxx&quot; 지원과, 실행시간에 값에 대한 검사를 더 명확하게 할 수 있을 겁니다.&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;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;단일 책임 원칙 &amp;amp; 관심사 분리:&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;ArgOptions:&amp;nbsp;명령줄&amp;nbsp;인수&amp;nbsp;파싱과&amp;nbsp;값&amp;nbsp;저장을&amp;nbsp;담당&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;ArgValue: 단일 옵션의 값 처리 담당&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;간결한 인터페이스:&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;연산자 오버로딩 [ ] 을 활용해 배열처럼 접근&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;메서드 체이닝을 통한 타입 변환 표현&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;예외 처리 캡슐화:&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;옵션이&amp;nbsp;없거나&amp;nbsp;타입&amp;nbsp;변환&amp;nbsp;실패&amp;nbsp;시의&amp;nbsp;예외&amp;nbsp;처리를&amp;nbsp;ArgValue&amp;nbsp;내부에&amp;nbsp;캡슐화&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;확장성 고려:&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;새로운 타입 변환이 필요하면 ArgValue 클래스만 수정&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;템플릿&amp;nbsp;메서드를&amp;nbsp;사용해&amp;nbsp;코드&amp;nbsp;중복&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;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot; data-darkreader-inline-color=&quot;&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;있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>C++</category>
      <category>cpp</category>
      <category>is_same_v</category>
      <category>std::optional</category>
      <category>template</category>
      <category>오버로딩</category>
      <category>오퍼레이터</category>
      <category>템플릿</category>
      <author>tys</author>
      <guid isPermaLink="true">https://cplusplus.tistory.com/18</guid>
      <comments>https://cplusplus.tistory.com/entry/CPP-%ED%85%9C%ED%94%8C%EB%A6%BF-%ED%99%9C%EC%9A%A9-%EC%8B%A4%ED%96%89%EC%9D%B8%EC%9E%90-%EC%B2%98%EB%A6%AC-getopt-Arguments-and-options#entry18comment</comments>
      <pubDate>Fri, 16 May 2025 15:35:57 +0900</pubDate>
    </item>
    <item>
      <title>C++ 유틸리티: TPS 측정과 SpinLock</title>
      <link>https://cplusplus.tistory.com/entry/C-%EC%9C%A0%ED%8B%B8%EB%A6%AC%ED%8B%B0-TPS-%EC%B8%A1%EC%A0%95%EA%B3%BC-SpinLock</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트에 참여해서 일을 하다 보면 TPS를 측정해야 할 일이 종종 있습니다. 이미 프로젝트에 사용되던 TPS소스를 사용하려 했지만 사용이 쉽지 않았습니다. TPS측정 소스를 보면 시간별 버킷을 이용하는 게 일반적이고, 유독 이런 소스들을 보면 복잡도가 상당히 높은 경향을 보였습니다. 이건 사실 측정 방식의 문제와는 다른 문제 이긴 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;

            &lt;figure class=&quot;unsupported component-kakaotv&quot; contenteditable=&quot;false&quot; style=&quot;background:#000;margin:16px 0;min-height:72px;padding:10px 16px;display:flex;align-items:center;justify-content:center;text-align:center;box-sizing:border-box;width:100%;max-width:100%;&quot;&gt;
                &lt;p contenteditable=&quot;false&quot; style=&quot;margin:0;color:#8a8a8a;font-size:13px;line-height:1.6;user-select:none;pointer-events:none;&quot;&gt;동영상 서비스가 종료되어 해당 콘텐츠를 재생할 수 없습니다.&lt;/p&gt;
            &lt;/figure&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;아래 코드는 어떤 회사에서 사용됐던 TPS 계산 코드입니다,&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;pre id=&quot;code_1747035744265&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int get_TPS(vector&amp;lt;int&amp;gt;&amp;amp; vecTPS,  std::atomic&amp;lt;long long&amp;gt;&amp;amp;  nCurSec, int nRef)
{
  int nCurIndex = (nCurSec/(nRef*1000))%TPS_MAX_INDEX;
  int PreIndex = (nCurIndex+TPS_MAX_INDEX-1)%TPS_MAX_INDEX;
  int val= vecTPS[PreIndex]/nRef;
  vecTPS[PreIndex]=0;
  return val;
}

int check_TPS(vector&amp;lt;int&amp;gt;&amp;amp; vecTPS,  std::atomic&amp;lt;long long&amp;gt;&amp;amp;  nCurSec, int nRef, int nVal)
{
  struct timeval time;
  gettimeofday(&amp;amp;time, NULL);
  long nNewSec = time.tv_sec*1000 + time.tv_usec/1000;

  // ex) nRef==5?   index0:0~4초, index2:5~9초
  int nCurIndex = (nCurSec/(nRef*1000))%TPS_MAX_INDEX;
  int nNewIndex = (nNewSec/(nRef*1000))%TPS_MAX_INDEX;

  if(nCurIndex != nNewIndex || 0 == nCurSec){
    // nRef 시간이 초과 되었거나 구동 후 처음 인경우
    nCurSec = nNewSec;
    nCurIndex = nNewIndex;

    vecTPS[nCurIndex] = 0;
  }

  vecTPS[nCurIndex] += nVal;

  return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&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;li&gt;함수의 목적과 작동 방식에 대한 설명이 거의 없어 사용코드를 분석해야 합니다.&lt;/li&gt;
&lt;li&gt;쓰레드 안전해 보이지 않습니다.(인자로 받아야 할 값들이 thread local인지 등 잘 모르므로)&lt;/li&gt;
&lt;/ul&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;h3 style=&quot;color: #000000; text-align: start; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-ke-size=&quot;size23&quot; data-darkreader-inline-color=&quot;&quot;&gt;TPSMeter 인터페이스 구상&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 아래 내용처럼 단순하게 사용하려고 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1747050673814&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &quot;TPSMeter.h&quot;

class Session
{
public:
  void input_data()
  {
    ........
    // 현재 트랜잭션 1개 추가되고 바로 현재 tps값을 출력
    std::cout &amp;lt;&amp;lt; inbound.add_transaction() &amp;lt;&amp;lt; std::endl;
    .........
  }

  void statistics()
  {
    ........
    // 현재 tps값을 알고 싶음.
    std::cout &amp;lt;&amp;lt; inbound.get_tps() &amp;lt;&amp;lt; std::endl;
    .........
  }
  
private:
  TPSMeter inbound;
}&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;TPS를 측정하는 데 있어 사용자가 더 알아야 할 것이 있을까요?&lt;/span&gt;&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;TPSMeter 구현&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 TPS는 구현을 단순화 하기 위해 해당 시점의 TPS를 측정하도록 &quot;슬라이딩 윈도우(Sliding Window)&quot; 방식으로 구현하였습니다.&amp;nbsp; 시간별 버킷 방식은 정해진 시간 간격(예: 1초)으로 트랜잭션을 그룹화하여 다양한 집계방식으로 TPS를 측정하지만, 슬라이딩 윈도우 방식은 측정 기준으로 지난 1초 동안의 트랜잭션을 정확하게 카운트하는 특징이 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1747036368472&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#pragma once

#include &quot;SpinLockGuard.h&quot;
#include &amp;lt;chrono&amp;gt;
#include &amp;lt;deque&amp;gt;
#include &amp;lt;mutex&amp;gt;

class TPSMeter
{
public:
  // return tps
  size_t add_transaction()
  {
    SpinLockGuard guard(lock_);
    // hint를 사용하여 O(1)에 가깝게 insert 되도록 함.
    transactions_.emplace_back(std::chrono::steady_clock::now());
    return get_tps_nolock();
  }

  size_t get_tps()
  {
    SpinLockGuard guard(lock_);
    return get_tps_nolock();
  }

protected:
  size_t get_tps_nolock()
  {
    cleanup_cutoff();
    return transactions_.size();
  }

  void cleanup_cutoff()
  {
    if (transactions_.empty() == true)
      return;

    auto cutoff = std::chrono::steady_clock::now() - std::chrono::seconds(1);
    if ((*transactions_.rbegin()) &amp;lt; cutoff)
    {
      transactions_.clear();
      return;
    }

    auto cutoff = std::chrono::steady_clock::now() - std::chrono::seconds(1);
    while (transactions_.front() &amp;lt; cutoff)
      transactions_.pop_front();
  }

protected:
  mutable SpinLock lock_;
  std::deque&amp;lt;std::chrono::steady_clock::time_point&amp;gt; transactions_;
};&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;&lt;span style=&quot;color: #1a5490;&quot;&gt;std::chrono::steady_clock&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;steady_clock은 시스템 시간 변경에 영향을 받지 않고 단조롭게 증가(monotonic)하는 시간 간격 측정에 더 적합합니다. 반면, system_clock은 사용자 설정이나 NTP 동기화로 시간이 앞뒤로 조정될 수 있어 현재 시간이나 날짜 표현해 적합합니다.&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;SpinLock과 SpinLockGuard 구현&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #1a5490; --darkreader-inline-color: var(--darkreader-text-333333, #c8c3bc);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Mutex&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-333333, #c8c3bc);&quot; data-darkreader-inline-color=&quot;&quot;&gt;std::mutex는 lock을 얻을때 unlock상태라면 즉시 락을 획득하지만, lock상태라면 커널로 진입하게 됩니다. 이때 커널이 제공하는 대기 큐에 들어가고 sleep 상태로 전환됩니다. 이 과정에서 컨텍스트 스위칭이 발생하며, CPU는 다른 실행가능한 쓰레드를 가져와 실행합니다. 이제 lock이 풀리고 해당 쓰레드의 차례가 오게 되면 다시 컨텍스트 스위칭이 일어나 mutex를 획득합니다.&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;color: #1a5490; --darkreader-inline-color: var(--darkreader-text-333333, #c8c3bc);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Spinlock&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-333333, #c8c3bc);&quot; data-darkreader-inline-color=&quot;&quot;&gt;스핀락은 CPU가 제공하는 원자적(atomic) 연산을 활용하여 이루어 집니다. 즉, lock을 얻을때 이미 lock상태라면 mutex와 달리 컨텍스트 스위칭이 이루어지지 않고 반복해서 원자적(atomic) 연산으로 lock변수를 확인하여 기다립니다. 이를&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-333333, #c8c3bc);&quot; data-darkreader-inline-color=&quot;&quot;&gt;busy-waiting(바쁜 대기) 이라고 합니다. 스핀락은 이와 같이 atomic연산을 활영하여 저수준에서의 동기화 기법을 구현한 것입니다.&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;color: #000000; text-align: left; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;SpinLock은 락이 해제될 때까지 계속 확인하므로(busy-waiting) 컨텍스트 스위칭 오버헤드가 없습니다. 즉, 아주 짧은 임계구간에서는 컨텍스트 스위칭 비용보다 SpinLock의 오버헤드가 훨씬 적어 성능 이점을 얻을 수 있습니다.&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;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;정말 필요한 것들만 간단하기 구현하였습니다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;SpinLock&lt;/h4&gt;
&lt;pre id=&quot;code_1747115830956&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#pragma once

#include &amp;lt;memory&amp;gt;
#include &amp;lt;atomic&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;utility&amp;gt;
#include &amp;lt;functional&amp;gt;
#include &amp;lt;thread&amp;gt;

/**
 * context switching에 빠지지 않고 락을 가져옵니다.
 * 단 쓰레드 수가 코어수보다 적으면 context switching이 발생하므로 주의해야 합니다.
 */
class SpinLock
{
public:
  SpinLock () noexcept = default;
  ~SpinLock() noexcept = default;

  // 복사 생성자, 복사 대입 연산자는 삭제합니다.
  SpinLock(const SpinLock &amp;amp;) = delete;
  SpinLock &amp;amp;operator=(const SpinLock &amp;amp;) = delete;

  void lock()
  {
    while(spin_lock_.test_and_set(std::memory_order_acquire))
      std::this_thread::yield();
    return;
  }

  void unlock()
  {
    spin_lock_.clear(std::memory_order_release);
  }

private:
  std::atomic_flag spin_lock_ = ATOMIC_FLAG_INIT;
};&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;SpinLockGuard&lt;/h4&gt;
&lt;pre id=&quot;code_1747115877464&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#pragma once

#include &quot;SpinLock.h&quot;

class SpinLockGuard
{
public:
  explicit SpinLockGuard(SpinLock &amp;amp;lock, bool use_lock = true)
  : spin_(lock), use_lock_(use_lock)
  {
    acquire();
  }

  SpinLockGuard() = delete;
  SpinLockGuard(const SpinLockGuard &amp;amp;) = delete;
  SpinLockGuard &amp;amp;operator=(const SpinLockGuard &amp;amp;) = delete;

  ~SpinLockGuard()
  {
    release();
  }

  void acquire()
  {
    if (use_lock_ == false) return;
    if (acquire_  == true ) return;

    acquire_ = true;
    spin_.lock();
  }

  void release()
  {
    if (use_lock_ == false) return;
    if (acquire_  == false) return;

    acquire_ = false;
    spin_.unlock();
  }

private:
  SpinLock  &amp;amp;spin_;
  bool      use_lock_ = true;
  bool      acquire_  = 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;&lt;span style=&quot;color: #000000; text-align: start; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&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;있습니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>C++</category>
      <category>Spin</category>
      <category>spinlock</category>
      <category>spinlockguard</category>
      <category>TPS</category>
      <author>tys</author>
      <guid isPermaLink="true">https://cplusplus.tistory.com/17</guid>
      <comments>https://cplusplus.tistory.com/entry/C-%EC%9C%A0%ED%8B%B8%EB%A6%AC%ED%8B%B0-TPS-%EC%B8%A1%EC%A0%95%EA%B3%BC-SpinLock#entry17comment</comments>
      <pubDate>Mon, 12 May 2025 16:42:35 +0900</pubDate>
    </item>
    <item>
      <title>C++ 강력한 동시성 제어(3) - BlockingQueue을 이용한 Logger</title>
      <link>https://cplusplus.tistory.com/entry/C-%EA%B0%95%EB%A0%A5%ED%95%9C-%EB%8F%99%EC%8B%9C%EC%84%B1-%EC%A0%9C%EC%96%B43-BlockingQueue-Logger</link>
      <description>&lt;p style=&quot;color: #333333; text-align: start; --darkreader-inline-color: var(--darkreader-text-333333, #c8c3bc);&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://cplusplus.tistory.com/entry/C-%EA%B0%95%EB%A0%A5%ED%95%9C-%EB%8F%99%EC%8B%9C%EC%84%B1-%EC%A0%9C%EC%96%B41-BlockingQueue&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;C++ 강력한 동시성 제어(1) - BlockingQueue의 이해&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start; --darkreader-inline-color: var(--darkreader-text-333333, #c8c3bc);&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://cplusplus.tistory.com/entry/C-%EA%B0%95%EB%A0%A5%ED%95%9C-%EB%8F%99%EC%8B%9C%EC%84%B1-%EC%A0%9C%EC%96%B42-BlockingQueue-%EA%B5%AC%ED%98%84&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;C++ 강력한 동시성 제어(2) - BlockingQueue 구현&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start; --darkreader-inline-color: var(--darkreader-text-333333, #c8c3bc);&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;C++ 강력한 동시성 제어(3) - BlockingQueue을 이용한 Logger&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start; --darkreader-inline-color: var(--darkreader-text-333333, #c8c3bc);&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start; --darkreader-inline-color: var(--darkreader-text-333333, #c8c3bc);&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;참조&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start; --darkreader-inline-color: var(--darkreader-text-333333, #c8c3bc);&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://cplusplus.tistory.com/entry/stdthread%EC%9D%98-%EC%8B%A4%EC%A0%84%EC%A0%81-%EB%9E%A9%ED%95%91-thread-%ED%81%B4%EB%9E%98%EC%8A%A4-%EA%B5%AC%ED%98%84&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;std::thread의&amp;nbsp;실전적&amp;nbsp;랩핑&amp;nbsp;-&amp;nbsp;thread&amp;nbsp;클래스&amp;nbsp;구현&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start; --darkreader-inline-color: var(--darkreader-text-333333, #c8c3bc);&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://cplusplus.tistory.com/entry/stdconditionvariable%EC%9D%98-%EC%95%88%EC%A0%84%ED%95%9C-%EC%8B%9C%EA%B7%B8%EB%84%90%EB%A7%81&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;std::condition_variable의&amp;nbsp;안전한&amp;nbsp;시그널링&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start; --darkreader-inline-color: var(--darkreader-text-333333, #c8c3bc);&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://cplusplus.tistory.com/entry/C-Lockfree-Queue%EA%B0%80-%ED%95%AD%EC%83%81-%EC%A2%8B%EC%9D%80-%EC%84%A0%ED%83%9D%EC%9D%BC%EA%B9%8C&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;C++&amp;nbsp;Lockfree&amp;nbsp;Queue&amp;nbsp;항상&amp;nbsp;좋은&amp;nbsp;선택일까?&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start; --darkreader-inline-color: var(--darkreader-text-333333, #c8c3bc);&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start; --darkreader-inline-color: var(--darkreader-text-333333, #c8c3bc);&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;아래와 같은 조건으로 간단한 Logger를 BlockingQueue를 이용해 만들어 보려 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start; --darkreader-inline-color: var(--darkreader-text-333333, #c8c3bc);&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;(다만 이전 링크에서 구현한 BlockingVector를 사용할 예정입니다.)&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;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;로그는 로그 기록 순서대로 파일에 출력되어야 합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;로그는 표준출력으로 출력합니다. (간단한 예제이므로)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;--darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;로그 기록은 별도 쓰레드에서 처리하도록 합니다.&lt;/span&gt;&lt;span style=&quot;--darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;--darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;--darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&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;figure class=&quot;unsupported component-kakaotv&quot; contenteditable=&quot;false&quot; style=&quot;background:#000;margin:16px 0;min-height:72px;padding:10px 16px;display:flex;align-items:center;justify-content:center;text-align:center;box-sizing:border-box;width:100%;max-width:100%;&quot;&gt;
                &lt;p contenteditable=&quot;false&quot; style=&quot;margin:0;color:#8a8a8a;font-size:13px;line-height:1.6;user-select:none;pointer-events:none;&quot;&gt;동영상 서비스가 종료되어 해당 콘텐츠를 재생할 수 없습니다.&lt;/p&gt;
            &lt;/figure&gt;
        
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-ke-size=&quot;size23&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;인터페이스 만들기&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;이전 포스팅에서 했던 것처럼 아래와 같이 사용법을 먼저 만들어 봅니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1746673781280&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#define LOGGER Logger(LoggerService::ref(), __FILE__, __LINE__, __FUNCTION__)

int main(void)
{
  // LoggerService는 싱글턴으로 구성합니다.
  LoggerService::ref().start();

  // 템플릿을 사용하여 타입에 구분 없이 사용할 수 있도록 snprintf, sprintf 사용 형식은 버립니다.
  LOGGER &amp;lt;&amp;lt; &quot;hello&quot; &amp;lt;&amp;lt; &quot;world&quot; &amp;lt;&amp;lt; 1 &amp;lt;&amp;lt; 1.2 &amp;lt;&amp;lt; &quot;삼사오&quot;;

  LoggerService::ref().stop();
  return 0;
}

/// 실행되여 표현될 예상 결과입니다. logger &amp;lt;&amp;lt; 연산자는 인자를 받고 자동으로 한칸 띄웁니다. 
main.cpp:  5: hello world 1 1.2 삼사오&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;로거 서비스를 시작합니다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Logger객체를 통해 로그를 남깁니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;RAII 패턴을 응용할 겁니다. 한 줄로 로그를 작성하면 자동으로 로그가 전송되는 구조입니다. 임시 객체의 생명주기를 활용해 별도의 flush()나 send() 같은 메서드를 호출할 필요가 없게 됩니다.&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Logger 임시 객체를 생성합니다. 어디로 어떻게 보내야 할지 모르므로, logger_service를 인자로 받도록 합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Logger은 해당 라인에서 생성되고 체인형태로 로깅할 값들을 수집하게 됩니다.&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;함수대신 스트림 연산자로 많이 사용되는 operator&amp;lt;&amp;lt;을 이용합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;템플릿을 사용하여 타입 안정성을 보장합니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;세미콜론(;)을 만나게 되면 Logger은 소멸되므로 소멸 시 logger_service를 통해 LogWriter로 수집된 로그 데이터를 쓰레드로 보내게 됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&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;h3 style=&quot;color: #000000; text-align: start; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-ke-size=&quot;size23&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;오브젝트 탐색&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start; --darkreader-inline-color: var(--darkreader-text-333333, #c8c3bc);&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;위 인터페이스에 따라 다음과 같이 오브젝트를 구성할 수 있습니다. 이것은 초안입니다.&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;요구사항에 따라서 어떤 오브젝트가 있어야 할지, 오브젝트 구성은 어떻게 해야 할지가 분석, 설계에 해당합니다. 많은 설계자, 개발자들이 오해하는 부분이 설계를 마치면 다시 돌아가면 안 된다고 여기는 겁니다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;하지만 실제로는 요구사항 수집-&amp;gt;분석-&amp;gt;설계를 통해 초안을 만든 후, 이 과정을 반복해야 합니다. 누락된 부분이나 불필요한 요소, 논리적 오류가 없는지 확인해야 합니다. 때로는 구현 단계에 집입하는 상태에서 이 과정을 반복하기도 합니다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이러한 반복 과정을 '정제'라고 하며, 좋은 설계가 나올 때까지 계속해야 합니다. 이런 반복적인 정제 과정을 통해 더 완성도 높은 소프트웨어를 만들 수 있습니다.&lt;/span&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2332&quot; data-origin-height=&quot;584&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjT52F/btsNRUdUBBv/sBUJNdbOjd7dI2dArabCDk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjT52F/btsNRUdUBBv/sBUJNdbOjd7dI2dArabCDk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjT52F/btsNRUdUBBv/sBUJNdbOjd7dI2dArabCDk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbjT52F%2FbtsNRUdUBBv%2FsBUJNdbOjd7dI2dArabCDk%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;2332&quot; height=&quot;584&quot; data-origin-width=&quot;2332&quot; data-origin-height=&quot;584&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start; --darkreader-inline-color: var(--darkreader-text-333333, #c8c3bc);&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start; --darkreader-inline-color: var(--darkreader-text-333333, #c8c3bc);&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;인터페이스 구성안을 근거로 제 구상안에서의 탐색된 오브젝트는 &lt;span style=&quot;text-align: start; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;LoggerService, Logger, LogWriter, BlockingQueue이며 보조 클래스로 Singleton과 Thread 이렇게 총 6개입니다. 각각은 꼭 &quot;단일 책임의 원칙(SRP)에 따라 탐색되어야 합니다. 탐색하는 과정은 이 원칙들에 따라 분석과 설계를 반복해야 합니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start; --darkreader-inline-color: var(--darkreader-text-333333, #c8c3bc);&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start; --darkreader-inline-color: var(--darkreader-text-333333, #c8c3bc);&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;text-align: start; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;여기서는 std::thread를 사용하는데, 사용성에 있어서 여러 가지 제어 함수를 캡슐화시킨 [&lt;a style=&quot;color: #000000;&quot; href=&quot;https://cplusplus.tistory.com/entry/stdthread%EC%9D%98-%EC%8B%A4%EC%A0%84%EC%A0%81-%EB%9E%A9%ED%95%91-thread-%ED%81%B4%EB%9E%98%EC%8A%A4-%EA%B5%AC%ED%98%84&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;std::thread의 실전적 랩핑 - thread 클래스 구현&lt;/a&gt;] 을 사용하려고 합니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;std::thread를 랩핑 하여 사용하는 이유는 직접적으로 사용하면 시작과 종료, join 등의 직접 다루는 코드와 시작, 종료 시의 동기화 문제에 있어서 std::condition_variable도 사용해야 합니다. 또 std::condition_variable는 가짜 깨움과 시그널 놓침의 문제가 동반되므로 이를 해결해야 하는 코드도 추가되어야 합니다. 각 단위로 보면 그리 많은 코드는 아니지만 캡슐화되지 않은 이런 작은 코드들이 모이면 코드의 복잡도를 훨씬 높이기 때문에 분리하여 코드를 단순화시킵니다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;즉, 단일 책임 원칙(SRP)에 따라 책임과 역할을 물리적으로 분리합니다. 이렇게 클래스로 각 역할과 책임을 분리하면 사용하는 쪽에서의 코드가 단순해지고(추상화) 이는 캡슐화로 이어집니다.&lt;/span&gt;&lt;/blockquote&gt;
&lt;p style=&quot;color: #333333; text-align: start; --darkreader-inline-color: var(--darkreader-text-333333, #c8c3bc);&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start; --darkreader-inline-color: var(--darkreader-text-333333, #c8c3bc);&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;간단한 싱글턴 코드: 상속받아 사용하면 간단히 싱글턴 객체로 변신합니다.&lt;/span&gt;&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;pre id=&quot;code_1746879514960&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#pragma once

#include &amp;lt;mutex&amp;gt;

template&amp;lt;typename T&amp;gt;
class Singleton
{
public:
  virtual ~Singleton() = default;

  static T&amp;amp; ref()
  {
    static T instance;  // static 객체로 변경
    return instance;
  }

  // 복사 생성과 대입을 막아요
  Singleton(const Singleton &amp;amp;) = delete;
  Singleton &amp;amp;operator=(const Singleton &amp;amp;) = delete;

protected:
  Singleton() = default;
};&lt;/code&gt;&lt;/pre&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;span style=&quot;color: #000000;&quot;&gt;정제하기(Refinement)&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&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;&lt;span style=&quot;color: #000000;&quot;&gt;저는 일단 탐색된 오브젝트의 초안을 근거로 아래와 같이 설계를 끝냈습니다. 이전에 탐색된 6개의 클래스보다 더 많은 클래스가 탐색되었고 그중 로그로 전달할 LogData 클래스가 생산자-소비자 패턴에 사용될 데이터입니다.&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;color: #000000;&quot;&gt;정제하는 과정을 통해 재사용될 클래스들인 인프라스트럭쳐 레이어(Infrastructure Layer)와 비즈니스 로직에 처리할 클래스들의 모음인 비즈니스 레이어로 나누어졌습니다. (일반적 범주에서 로거도 인프라스트럭쳐이지만 이 글의 목적성에 맞추어 로거를 비즈니스로 표현하였습니다.)&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;span style=&quot;color: #000000;&quot;&gt;재사용 클래스&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;MThread는 추상화 함수를 통해 재활용할 수 있도록 했습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;BlockingQueue도 template로 만들어 재활용할 수 있도록 했습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;BlockingQueueThread는 사실상&amp;nbsp;&lt;span style=&quot;text-align: left; --darkreader-inline-color: var(--darkreader-text-333333, #c8c3bc);&quot; data-darkreader-inline-color=&quot;&quot;&gt;소비자 쓰레드입니다. 다양한 형태의 데이터 형태를 적용할 수 있도록 BlogkingQueue의 템플릿을 그대로 이용하였습니다.&lt;/span&gt;&lt;span style=&quot;text-align: left; --darkreader-inline-color: var(--darkreader-text-333333, #c8c3bc);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;/span&gt;&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;2113&quot; data-origin-height=&quot;1395&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b9aCGz/btsNTJPi9GL/WfkPfi40BkWMIRRv2qzYZ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b9aCGz/btsNTJPi9GL/WfkPfi40BkWMIRRv2qzYZ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b9aCGz/btsNTJPi9GL/WfkPfi40BkWMIRRv2qzYZ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb9aCGz%2FbtsNTJPi9GL%2FWfkPfi40BkWMIRRv2qzYZ1%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;2113&quot; height=&quot;1395&quot; data-origin-width=&quot;2113&quot; data-origin-height=&quot;1395&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;&lt;span style=&quot;color: #000000;&quot;&gt;로거&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;아래 4개의 클래스는 이 글에서의 실제 요구사항을 반영할 비즈니스 레이어입니다. 실제로 재활용될 수 있겠지만, 이 글의 주제에서는 요구사항을 반영할 비즈니스가 됩니다.&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;LogData는 로그에 필요한 데이터 filename, line, function, message로 구성합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Logger는 실제로 사용자 인터페이스로 LogData를 구성하고 LoggerService에서 Logging을 위임합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;LoggerService는 Logger로 받은 데이터를 LogWriter로 BlockingQueue를 통해 전송합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;LogWriter는 최종 소비자 쓰레드로써 BlogkingQueue를 통해 받은 데이터를 출력합니다.&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;color: #000000;&quot;&gt;Thread와 BlogkingQueue를 상속받았으므로 BlockingQueue로부터 받은 데이터를 실제 비즈니스에 집중하여 코드를 구현하면 됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;BlogkingQueue는 템플릿으로써 이를 상속받았을 때 템플릿 인자에 LogData를 주었습니다. 템플릿을 이용하여 낮은 결합도를 유지시켜 재활용할 수 있도록 합니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;구현&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;설계를 끝냈으니 이제 구현을 해봅니다. Singleton, &lt;a style=&quot;color: #000000;&quot; href=&quot;https://cplusplus.tistory.com/entry/stdthread%EC%9D%98-%EC%8B%A4%EC%A0%84%EC%A0%81-%EB%9E%A9%ED%95%91-thread-%ED%81%B4%EB%9E%98%EC%8A%A4-%EA%B5%AC%ED%98%84&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;MThread&lt;/a&gt;, &lt;a style=&quot;color: #000000;&quot; href=&quot;https://cplusplus.tistory.com/entry/C-%EA%B0%95%EB%A0%A5%ED%95%9C-%EB%8F%99%EC%8B%9C%EC%84%B1-%EC%A0%9C%EC%96%B42-BlockingQueue-%EA%B5%AC%ED%98%84&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;BlockingVector&lt;/a&gt; 는 상단과, 이전 포스팅들을 통해 이미 구현되어 있어 가져다 사용하겠습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1747836434174&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;std::thread의 실전적 랩핑 - thread 클래스 구현&quot; data-og-description=&quot;std::condition_variable의 안전한 시그널링std::thread의 실전적 랩핑 - thread 클래스 구현들어가며C++11에서 std::thread가 도입된 이후, 많은 개발자들이 멀티쓰레딩 프로그래밍을 더 쉽게 할 수 있게 되었습&quot; data-og-host=&quot;cplusplus.tistory.com&quot; data-og-source-url=&quot;https://cplusplus.tistory.com/entry/stdthread%EC%9D%98-%EC%8B%A4%EC%A0%84%EC%A0%81-%EB%9E%A9%ED%95%91-thread-%ED%81%B4%EB%9E%98%EC%8A%A4-%EA%B5%AC%ED%98%84&quot; data-og-url=&quot;https://cplusplus.tistory.com/entry/stdthread%EC%9D%98-%EC%8B%A4%EC%A0%84%EC%A0%81-%EB%9E%A9%ED%95%91-thread-%ED%81%B4%EB%9E%98%EC%8A%A4-%EA%B5%AC%ED%98%84&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bn2FHB/hyYU2E90ut/AY97OytgXHWVcqrA61K6D0/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/7fhqh/hyYYAtsJxp/h4nOzqhtidcKcWSjgGCjck/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800&quot;&gt;&lt;a href=&quot;https://cplusplus.tistory.com/entry/stdthread%EC%9D%98-%EC%8B%A4%EC%A0%84%EC%A0%81-%EB%9E%A9%ED%95%91-thread-%ED%81%B4%EB%9E%98%EC%8A%A4-%EA%B5%AC%ED%98%84&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://cplusplus.tistory.com/entry/stdthread%EC%9D%98-%EC%8B%A4%EC%A0%84%EC%A0%81-%EB%9E%A9%ED%95%91-thread-%ED%81%B4%EB%9E%98%EC%8A%A4-%EA%B5%AC%ED%98%84&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bn2FHB/hyYU2E90ut/AY97OytgXHWVcqrA61K6D0/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/7fhqh/hyYYAtsJxp/h4nOzqhtidcKcWSjgGCjck/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800');&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;std::thread의 실전적 랩핑 - thread 클래스 구현&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;std::condition_variable의 안전한 시그널링std::thread의 실전적 랩핑 - thread 클래스 구현들어가며C++11에서 std::thread가 도입된 이후, 많은 개발자들이 멀티쓰레딩 프로그래밍을 더 쉽게 할 수 있게 되었습&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;cplusplus.tistory.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;figure id=&quot;og_1747836452104&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;C++ 강력한 동시성 제어(2) - BlockingQueue 구현&quot; data-og-description=&quot;C++ 강력한 동시성 제어(1) - BlockingQueueC++ 강력한 동시성 제어(2) - BlockingQueue의 구현C++ 강력한 동시성 제어(3) - BlockingQueue을 이용한 Logger 이 글에서는 이전 포스팅 &amp;quot;C++ 강력한 동시성 제어(1) - Blockin&quot; data-og-host=&quot;cplusplus.tistory.com&quot; data-og-source-url=&quot;https://cplusplus.tistory.com/entry/C-%EA%B0%95%EB%A0%A5%ED%95%9C-%EB%8F%99%EC%8B%9C%EC%84%B1-%EC%A0%9C%EC%96%B42-BlockingQueue-%EA%B5%AC%ED%98%84&quot; data-og-url=&quot;https://cplusplus.tistory.com/entry/C-%EA%B0%95%EB%A0%A5%ED%95%9C-%EB%8F%99%EC%8B%9C%EC%84%B1-%EC%A0%9C%EC%96%B42-BlockingQueue-%EA%B5%AC%ED%98%84&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cJ0XzJ/hyYW7d7Q6X/KDNESYf337UAUL5nDguK7k/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/cqRNWF/hyYWRPUIXj/ZtqFTnWn8P10osDb2NxruK/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800&quot;&gt;&lt;a href=&quot;https://cplusplus.tistory.com/entry/C-%EA%B0%95%EB%A0%A5%ED%95%9C-%EB%8F%99%EC%8B%9C%EC%84%B1-%EC%A0%9C%EC%96%B42-BlockingQueue-%EA%B5%AC%ED%98%84&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://cplusplus.tistory.com/entry/C-%EA%B0%95%EB%A0%A5%ED%95%9C-%EB%8F%99%EC%8B%9C%EC%84%B1-%EC%A0%9C%EC%96%B42-BlockingQueue-%EA%B5%AC%ED%98%84&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cJ0XzJ/hyYW7d7Q6X/KDNESYf337UAUL5nDguK7k/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/cqRNWF/hyYWRPUIXj/ZtqFTnWn8P10osDb2NxruK/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800');&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;C++ 강력한 동시성 제어(2) - BlockingQueue 구현&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;C++ 강력한 동시성 제어(1) - BlockingQueueC++ 강력한 동시성 제어(2) - BlockingQueue의 구현C++ 강력한 동시성 제어(3) - BlockingQueue을 이용한 Logger 이 글에서는 이전 포스팅 &quot;C++ 강력한 동시성 제어(1) - Blockin&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;cplusplus.tistory.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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;BlockingVectorThread 구현&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이미 쓰레드처리와, BlockingVector처리가 위임되었기에 구현이 간결해집니다. 이제 BlockingVectorThread는 BlockingVector기반의 소비자 쓰레드로 사용할 수 있습니다. 이를 상속받아 템플릿 인자와 MThread::run을 구현함으로써 원하는 비즈니스 로직을 처리할 수 있습니다. 재사용성이 가능해집니다.&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;color: #000000;&quot;&gt;즉, 이를 사용함으로써 소비자 쓰레드의 복잡한 부분은 위임된 채로 비즈니스 로직에 집중할 수 있게 합니다.&lt;/span&gt;&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;pre id=&quot;code_1746944413406&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#pragma once

#include &amp;lt;MThread.h&amp;gt;
#include &amp;lt;BlockingVector.h&amp;gt;

template&amp;lt;typename T&amp;gt;
class BlockingVectorThread : public MThread
{
public:
  BlockingVectorThread(const size_t &amp;amp;reserve_size = 10000);
  virtual ~BlockingVectorThread() {}

          bool start() override;
  // MThread에는 stop이 없고 join만 있습니다.
  virtual bool stop ();

  void    reserve(size_t size) { waiter_.reserve(size); }
  size_t  size   () const      { return waiter_.size(); }
  
  // MThread의 가상함수 인터페이스 입니다. 잊지 않도록 주석처리로 남겨놓았습니다.
  // virtual void run() = 0;

protected:
  // 요기 BlockingVector 있어요.
  BlockingVector&amp;lt;T&amp;gt; waiter_;
};

template&amp;lt;typename T&amp;gt;
BlockingVectorThread&amp;lt;T&amp;gt;::BlockingVectorThread(const size_t &amp;amp;reserve_size)
: waiter_(reserve_size, false)
{
}

template&amp;lt;typename T&amp;gt; bool
BlockingVectorThread&amp;lt;T&amp;gt;::start()
{
  if (MThread::is_run() == true)
    return true;

  waiter_.open();
  return MThread::start();
}

template&amp;lt;typename T&amp;gt; bool
BlockingVectorThread&amp;lt;T&amp;gt;::stop()
{
  if (MThread::is_run() == false)
    return true;

  // 이때 BlockingVector는 close를 전달 받습니다.
  waiter_.close();
  
  // 쓰레드가 종료되기를 기다립니다.
  return MThread::join();
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 style=&quot;color: #000000; text-align: start; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-ke-size=&quot;size23&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 style=&quot;color: #000000; text-align: start; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-ke-size=&quot;size23&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;LogWriter 구현&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;아래와 같이 소비자 쓰레드 관련된 코드들을 BlockingVectorThread에 위임함으로써 오로지 Log출력 코드에만 집중할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1746944914232&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &quot;LogData.h&quot;
#include &amp;lt;BlockingVectorThread.h&amp;gt;

class LogWriter : public BlockingVectorThread&amp;lt;LogData&amp;gt;
{
public:
  void write(const LogData &amp;amp;log) const
  {
    std::cout &amp;lt;&amp;lt; log.filename &amp;lt;&amp;lt; &quot;:&quot; &amp;lt;&amp;lt; log.line &amp;lt;&amp;lt; &quot;:&quot; &amp;lt;&amp;lt; log.function &amp;lt;&amp;lt; &quot;: &quot;
              &amp;lt;&amp;lt; log.message &amp;lt;&amp;lt; std::endl;
  }
  
protected:
  void run() override
  {
    std::vector&amp;lt;LogData&amp;gt; logs;
    while (waiter_.pop(logs) &amp;gt;= 0)
    {
      for (auto &amp;amp;log : logs)
        write(log);
    }
  }
};&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;span style=&quot;color: #000000;&quot;&gt;LoggerService 구현&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;LoggerService도 꽤 간결해졌습니다. 모든 것 들은 단일 책임 원칙(SRP)에 따라 캡슐화되었기 때문에 코드가 간결해졌습니다. 오롯이 LoggerService 구현에만 집중할 수 있습니다. LogWriter는 BlockingVector로써 종료 시 가지고 있는 모든 데이터를 출력하도록 되어 있기 때문에 종료에도 안심할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1746945184692&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &quot;LogWriter.h&quot;
#include &amp;lt;Singleton.h&amp;gt;

class LoggerService : public Singleton
{
public:
  bool start()
  {
    return writer_.start();
  }
  
  bool stop()
  {
    return writer_.stop();
  }
  
  void write(const LogData &amp;amp;log)
  {
    // 실패시 직접 화면 출력
    if (writer_.push(log) != 0)
      writer_.write(log);
  }

private:
  LogWriter writer_;
};&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;span style=&quot;color: #000000;&quot;&gt;LogData, Logger 구현&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1746952127752&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#pragma once

#include &amp;lt;string&amp;gt;

struct LogData
{
  std::string filename;
  int32_t     line;
  std::string function;
  std::string message;

  LogData(const std::string&amp;amp; fname, int32_t line, const std::string &amp;amp;func, const std::string &amp;amp;message = &quot;&quot;)
  : filename(fname), line(line), function(func), message(message) {}
};&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1746952215025&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#pragma once
#include &quot;LogData.h&quot;
#include &quot;LoggerService.h&quot;
#include &amp;lt;sstream&amp;gt;

// 편의를 위한 매크로 정의
#define LOGGER Logger(LoggerService::ref(), __FILE__, __LINE__, __FUNCTION__)

class Logger
{
public:
  Logger(LoggerService&amp;amp; service, const char *filename, int32_t line, const char *function)
  : service_(service), data_(filename, line, function) {}

  // 소멸자에서 로그 데이터를 서비스로 전송
  ~Logger()
  {
    data_.message = stream_.str();
    service_.write(data_);
  }
    
  // 다양한 타입을 지원하는 템플릿 operator
  template&amp;lt;typename T&amp;gt;
  Logger&amp;amp; operator&amp;lt;&amp;lt;(const T&amp;amp; value)
  {
        // 값을 스트림에 추가하고 자동으로 공백 추가
    if (stream_.tellp() &amp;gt; 0) // 스트림이 비어있지 않으면 공백 추가
        stream_ &amp;lt;&amp;lt; &quot; &quot;;
    stream_ &amp;lt;&amp;lt; value;
    return *this;
  }

private:
  LoggerService &amp;amp;service_;
  LogData data_;
  std::ostringstream stream_;
};&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;&lt;span style=&quot;color: #000000;&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;&lt;span style=&quot;color: #000000;&quot;&gt;※&amp;nbsp;&lt;span style=&quot;text-align: start; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;본 블로그의 소스코드는&amp;nbsp;자유롭게 사용하셔도 되나, 사용 시 발생하는 모든 문제에 대한 책임은 전적으로 사용자에게 있습니다.&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;</description>
      <author>tys</author>
      <guid isPermaLink="true">https://cplusplus.tistory.com/14</guid>
      <comments>https://cplusplus.tistory.com/entry/C-%EA%B0%95%EB%A0%A5%ED%95%9C-%EB%8F%99%EC%8B%9C%EC%84%B1-%EC%A0%9C%EC%96%B43-BlockingQueue-Logger#entry14comment</comments>
      <pubDate>Sun, 11 May 2025 15:40:30 +0900</pubDate>
    </item>
    <item>
      <title>C++ 강력한 동시성 제어(2) - BlockingQueue 구현</title>
      <link>https://cplusplus.tistory.com/entry/C-%EA%B0%95%EB%A0%A5%ED%95%9C-%EB%8F%99%EC%8B%9C%EC%84%B1-%EC%A0%9C%EC%96%B42-BlockingQueue-%EA%B5%AC%ED%98%84</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://cplusplus.tistory.com/entry/C-%EA%B0%95%EB%A0%A5%ED%95%9C-%EB%8F%99%EC%8B%9C%EC%84%B1-%EC%A0%9C%EC%96%B41-BlockingQueue&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-darkreader-inline-color=&quot;&quot;&gt;C++ 강력한 동시성 제어(1) - BlockingQueue 이해&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;C++ 강력한 동시성 제어(2) - BlockingQueue 구현&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://cplusplus.tistory.com/entry/C-%EA%B0%95%EB%A0%A5%ED%95%9C-%EB%8F%99%EC%8B%9C%EC%84%B1-%EC%A0%9C%EC%96%B43-BlockingQueue-Logger&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-darkreader-inline-color=&quot;&quot;&gt;C++ 강력한 동시성 제어(3) - BlockingQueue을 이용한 Logger&lt;/a&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;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;이 글에서는 이전 포스팅 &quot;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://cplusplus.tistory.com/manage/newpost/12?type=post&amp;amp;returnURL=ENTRY&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-darkreader-inline-color=&quot;&quot;&gt;C++ 강력한 동시성 제어(1) - BlockingQueue&lt;/a&gt;&quot; 에서 구현해야 할 BlockingQueue를 std::deque가 아닌 std::vector기반으로 구현합니다. 이 구현은 boost::lockfree::queue보다 빠르며 표준 라이브러리를 이용한 구현으로 안정성과 편의성(자유로운 데이터형식, 디버깅)이 높고 외부 의존성을 피할 수 있습니다.&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;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;std::vector기반의 BlockingQueue 벤치마크는 &quot;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://cplusplus.tistory.com/entry/C-Lockfree-Queue%EA%B0%80-%ED%95%AD%EC%83%81-%EC%A2%8B%EC%9D%80-%EC%84%A0%ED%83%9D%EC%9D%BC%EA%B9%8C&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-darkreader-inline-color=&quot;&quot;&gt;C++ Lockfree Queue 항상 좋은 선택일까?&lt;/a&gt;&quot; 를 참고하실 수 있습니다.&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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;왜 std::deque가 아닌 std::vector인가?&lt;/span&gt;&lt;/h3&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;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;std::vector도 큐처럼 사용할 수 있습니다. 선입선출 방식으로 동작할 수 있는 자료구조입니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;std::vector는 reserve() 메서드를 통해 필요한 메모리를 미리 할당받을 수 있습니다. std::deque에서 제공되지 않는 기능으로 push 오버헤드를 크게 감소 시킵니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;모든 STL 컨테이너가 그렇지만 swap을 지원합니다. 일반적인 BlockingQueue는 각 원소를 pop 할 때마다 락 경합이 발생하지만, swap을 활용하면 락 경쟁을 최소화하여 처리 성능이 극적으로 향상됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;성능에 결정적인 영향을 미치는 두 가지 요소는 메모리 선할당과 swap입니다. 특히 swap 연산은 전체 성능 향상의 핵심입니다.&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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;인터페이스&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;open: 큐를 오픈합니다. 대부분 서로 다른 쓰레드에서 사용되므로 사용 종료를 알리도록 close를 만들기 위함입니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;close: 큐를 close 합니다. pop에서 close를 인지하여야 합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;push: 데이터를 넣을 수 있도록 합니다. 물론 open이 되지 않은 상태이거나 close 된 상태도 알려야 합니다. 또한 std::vector는 push_front가 없으므로 FIFO자료구조를 유지할 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;pop: 기본적으로 블록 되는 함수이며, 데이터가 도착하면 데이터를 돌려주어야 하며 push와 마찬가지 close 된 상태를 알려줄 수 있어야 합니다. pop은 swap의 형태이므로 인자로 std::vector&amp;lt;T&amp;gt;를 받습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;size: 현재 큐에 쌓인 크기를 알 수 있도록 합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;reserve: std::vector를 사용하므로 미리 메모리를 예약받도록 합니다. 속도상 큰 이점이 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;템플릿 기반 설계를 통해 STL 컨테이너와 동일하게 다양한 데이터 타입을 지원하도록 합니다. &lt;span style=&quot;text-align: start;&quot;&gt;boost::lockfree::queue와 같은 복잡한 데이터 타입 제한이 없습니다.&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;boost::lockfree::queue 데이터 타입에 제한&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp; 1. Trivially Copyable 타입만 지원: POD(Plain Old Data) 타입이거나 trivially copyable 한 타입이어야 합니다&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp; 2. 복잡한 객체 제한: std::string, std::vector 같은 동적 메모리를 사용하는 타입은 직접 사용할 수 없습니다&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp; 3. 포인터 사용 주의: 포인터는 저장할 수 있지만, 메모리 관리를 별도로 해야 합니다&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp; 4. 크기 제한: 일반적으로 작은 크기의 데이터에 최적화되어 있습니다&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;이러한 제한 때문에 복잡한 객체를 다룰 때는 포인터나 인덱스를 사용하는 등의 우회 방법이 필요합니다.&lt;/span&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;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;구현 가이드&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;인터페이스를 구성하기 위해 사용자가 pop을 호출할 때에는 데이터가 없는 경우 Blocking 되어야 합니다. 이것을 지원하기 위해서는 std::condition_variable를 사용해야 합니다. std::condition_variable는 &quot;가짜 깨움&quot;과 &quot;시그널 놓침&quot;을 제어해 주어야 합니다. 구현하고자 하는 BlockingVector에서 직접 제어하게 되면 시그널과의 결합도, 코드의 복잡도가 높아지고 가독성이 떨어집니다.&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;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;그러므로 시그널 처리를 다른 클래스에 위임하여 std::condition_variable의 사용성을 단순화시켜야 합니다. 단일 책임 원칙에도 부합하며 추상화와 캡슐화도 잘 지켜지게 됩니다. 이를 위해 이전에 포스팅 &quot;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://cplusplus.tistory.com/entry/stdconditionvariable%EC%9D%98-%EC%95%88%EC%A0%84%ED%95%9C-%EC%8B%9C%EA%B7%B8%EB%84%90%EB%A7%81&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-darkreader-inline-color=&quot;&quot;&gt;std::condition_variable의 안전한 시그널링&lt;/a&gt;&quot;에서 구현된 MSignal을 사용합니다. &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;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;구현코드에서는 std::condition_variable 처리를 MSignal에 위임하여 추상화된, 캡슐화된 시그널을 사용함으로써 BlockingQueue 고유 기능에만 집중합니다. 물리적으로 코드가 분리되었기 때문입니다.&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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;인터페이스&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1746891162968&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;MSignal.h&amp;gt;
#include &amp;lt;vector&amp;gt;

template&amp;lt;typename T&amp;gt;
class BlockingVector
{
public:
  /**
   * @brief 생성자
   * @param reserve_size 벡터의 초기 예약 크기 (기본값: 10000)
   * @param open 초기 열림 상태 (기본값: true)
   */
  explicit BlockingVector(const size_t &amp;amp;reserve_size = 10000, const bool &amp;amp;open = true);

  ~BlockingVector() = default;

  /**
   * @brief 벡터를 열어서 작업 가능하게 함
   */
  void open();

  /**
   * @brief 벡터를 닫아서 더 이상의 작업을 차단
   */
  void close();

  /**
   * @brief 벡터가 열려있는지 확인
   * @return 열려있으면 true, 닫혔으면 false
   */
  bool is_open() const;

  /**
   * @brief 벡터의 용량 예약
   * @param size 예약할 용량
   * @return 실제 예약된 용량
   */
  size_t reserve(size_t size);

  /**
   * @brief 벡터의 용량 확인
   * @return size 현재 용량
   */
  size_t capacity() const;

  /**
   * @brief 벡터의 예약된 용량 확인
   * @return size 예약된 용량
   */
  size_t reserve_size() const;

  /**
   * @brief 아이템을 벡터에 추가
   * @param item 추가할 아이템
   * @return 성공시 0, 닫혔으면 -1, EAGAIN 꽉참.
   */
  int push(const T &amp;amp;item);

  // move 버전 추가
  int push(T &amp;amp;&amp;amp;item);

  /**
   * @brief 아이템을 벡터에 추가
   * @param item 추가할 아이템
   * @param remain_size 미처리된 큐개수
   * @return 성공시 0, 닫혔으면 -1, EAGAIN 꽉참.
   */
  int push(const T &amp;amp;item, size_t &amp;amp;remain_size);

  /**
   * @brief 벡터에서 아이템들을 꺼냄
   * @param[out] items 아이템을 받을 벡터
   * @param tmout_msec 타임아웃 (밀리초)
   * @return 성공시 0, 닫혔으면 -1, 타임아웃시 ETIMEDOUT
   */
  int pop(std::vector&amp;lt;T&amp;gt; &amp;amp;items, const uint32_t &amp;amp;tmout_msec = 0);

  /**
   * @brief 현재 크기 반환
   * @return 벡터 내 아이템 개수
   */
  size_t size() const;

  /**
   * @brief 컨테이너 복사본 반환
   * @return 내부 벡터의 복사본
   */
  std::vector&amp;lt;T&amp;gt; container() const;

protected:
  /**
   * @brief 벡터의 열림/닫힘 상태
   * true: 열림 상태, false: 닫힘 상태
   */
  bool open_ = true;
  
  /**
   * @brief 동기화를 위한 신호 객체
   */
  mutable MSignal signal_;
  
  /**
   * @brief 내부 데이터 저장 컨테이너
   */
  std::vector&amp;lt;T&amp;gt; container_;

  /**
   * @brief 벡터의 예약 크기
   */
  size_t reserve_size_ = 10000;
};&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;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;메서드 코드&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1746891725840&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;template&amp;lt;typename T&amp;gt;
BlockingVector&amp;lt;T&amp;gt;::BlockingVector(const size_t &amp;amp;reserve_size, const bool &amp;amp;open)
: open_(open) { this-&amp;gt;reserve(reserve_size); }

template&amp;lt;typename T&amp;gt; void
BlockingVector&amp;lt;T&amp;gt;::open()
{
  auto lock_guard = signal_.acquire_scoped_lock();
  open_ = true;
}

template&amp;lt;typename T&amp;gt; void
BlockingVector&amp;lt;T&amp;gt;::close()
{
  signal_.notify_one([&amp;amp;]()
  {
    open_ = false;
  });
}

template&amp;lt;typename T&amp;gt; bool
BlockingVector&amp;lt;T&amp;gt;::is_open() const
{
  auto lock_guard = signal_.acquire_scoped_lock();
  return open_;
}

template&amp;lt;typename T&amp;gt; size_t
BlockingVector&amp;lt;T&amp;gt;::reserve(size_t size)
{
  auto lock_guard = signal_.acquire_scoped_lock();
  container_.reserve(size);
  reserve_size_ = container_.capacity();
  return reserve_size_;
}

template&amp;lt;typename T&amp;gt; size_t
BlockingVector&amp;lt;T&amp;gt;::capacity() const
{
  auto lock_guard = signal_.acquire_scoped_lock();
  return container_.capacity();
}

template&amp;lt;typename T&amp;gt; size_t
BlockingVector&amp;lt;T&amp;gt;::reserve_size() const
{
  auto lock_guard = signal_.acquire_scoped_lock();
  return reserve_size_;
}

template&amp;lt;typename T&amp;gt; int
BlockingVector&amp;lt;T&amp;gt;::push(const T &amp;amp;item)
{
  auto lock_guard = signal_.acquire_scoped_lock();
  if (open_ == false)
    return -1;

  if (container_.size() &amp;gt;= this-&amp;gt;container_.capacity())
    return EAGAIN;

  container_.push_back(item);

  signal_.notify_one(lock_guard);
  return 0;
}

// move 버전 추가
template&amp;lt;typename T&amp;gt; int
BlockingVector&amp;lt;T&amp;gt;::push(T &amp;amp;&amp;amp;item)
{
  auto lock_guard = signal_.acquire_scoped_lock();
  if (open_ == false)
    return -1;

  if (container_.size() &amp;gt;= this-&amp;gt;container_.capacity())
    return EAGAIN;

  container_.emplace_back(std::forward&amp;lt;T&amp;gt;(item));

  signal_.notify_one(lock_guard);
  return 0;
}

template&amp;lt;typename T&amp;gt; int
BlockingVector&amp;lt;T&amp;gt;::push(const T &amp;amp;item, size_t &amp;amp;remain_size)
{
  auto lock_guard = signal_.acquire_scoped_lock();
  if (open_ == false)
    return -1;

  if (container_.size() &amp;gt;= this-&amp;gt;container_.capacity())
    return EAGAIN;

  container_.push_back(item);
  remain_size = container_.size();

  signal_.notify_one(lock_guard);
  return 0;
}

template&amp;lt;typename T&amp;gt; int
BlockingVector&amp;lt;T&amp;gt;::pop(std::vector&amp;lt;T&amp;gt; &amp;amp;items, const uint32_t &amp;amp;tmout_msec)
{
  items.clear();
  if (items.capacity() &amp;lt; reserve_size_)
    items.reserve(reserve_size_);

  while (true)
  {
    auto lock_guard = signal_.acquire_scoped_lock();
    if (container_.size() &amp;gt; 0)
    {
      container_.swap(items);
      reserve_size_ = container_.capacity();
      return 0;
    }

    if (open_ == false)
      return -1;

    if (signal_.wait(lock_guard, tmout_msec) == false)
      return ETIMEDOUT;
  }
}

template&amp;lt;typename T&amp;gt; size_t
BlockingVector&amp;lt;T&amp;gt;::size() const
{
  auto lock_guard = signal_.acquire_scoped_lock();
  return container_.size();
}

template&amp;lt;typename T&amp;gt; std::vector&amp;lt;T&amp;gt;
BlockingVector&amp;lt;T&amp;gt;::container() const
{
  auto lock_guard = signal_.acquire_scoped_lock();
  return container_;
}&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;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;사용예제&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1746892729266&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &quot;BlockingVector.h&quot;
#include &amp;lt;thread&amp;gt;
#include &amp;lt;iostream&amp;gt;

int main() 
{
  BlockingVector&amp;lt;int&amp;gt; blocking_vector;
  
  // 생산자
  std::thread producer([&amp;amp;blocking_vector]()
  {
    for (int index = 0; index &amp;lt; 10; ++index)
      blocking_vector.push(index);

    blocking_vector.close();
  });
  
  // 소비자
  std::thread consumer([&amp;amp;blocking_vector]()
  {
    std::vector&amp;lt;int&amp;gt; items;
    
    // items는 자동 clear되도록 되어 있습니다. 100ms 타임아웃
    while (blocking_vector.pop(items, 100) &amp;gt;= 0) 
    {
      std::cout &amp;lt;&amp;lt; &quot;받은 개수: &quot; &amp;lt;&amp;lt; items.size() &amp;lt;&amp;lt; std::endl;

      for (auto &amp;amp;item : items)
        std::cout &amp;lt;&amp;lt; item &amp;lt;&amp;lt; std::endl;
    }
    
    std::cout &amp;lt;&amp;lt; &quot;blocking_vector closed&quot; &amp;lt;&amp;lt; std::endl;
  });
  
  producer.join();
  consumer.join();
  
  return 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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;※&amp;nbsp;&lt;span style=&quot;text-align: start;&quot;&gt;본 블로그의 소스코드는 자유롭게 사용하셔도 되나, 사용 시 발생하는 모든 문제에 대한 책임은 전적으로 사용자에게 있습니다.&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>blockingqueue</category>
      <category>C++</category>
      <category>cpp</category>
      <category>std::vector</category>
      <category>Vector</category>
      <category>동시성</category>
      <author>tys</author>
      <guid isPermaLink="true">https://cplusplus.tistory.com/16</guid>
      <comments>https://cplusplus.tistory.com/entry/C-%EA%B0%95%EB%A0%A5%ED%95%9C-%EB%8F%99%EC%8B%9C%EC%84%B1-%EC%A0%9C%EC%96%B42-BlockingQueue-%EA%B5%AC%ED%98%84#entry16comment</comments>
      <pubDate>Sun, 11 May 2025 00:29:40 +0900</pubDate>
    </item>
    <item>
      <title>C++ 람다 활용: 시그널 핸들링</title>
      <link>https://cplusplus.tistory.com/entry/CPP-%EC%8B%9C%EA%B7%B8%EB%84%90-%ED%95%B8%EB%93%A4%EB%A7%81-%EB%9E%8C%EB%8B%A4-%ED%99%9C%EC%9A%A9</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;고전적 시그널 처리 방법은 정적 함수를 만들고 signal함수를 통해 정적 함수를 등록해야 합니다. 시그널은 기본적으로 커널의 인터럽트를 통해 처리되므로 &lt;span style=&quot;color: #333333; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot;&gt;핸들링하기 위해서는 몇 가지 조심스럽게 처리해야 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;

            &lt;figure class=&quot;unsupported component-kakaotv&quot; contenteditable=&quot;false&quot; style=&quot;background:#000;margin:16px 0;min-height:72px;padding:10px 16px;display:flex;align-items:center;justify-content:center;text-align:center;box-sizing:border-box;width:100%;max-width:100%;&quot;&gt;
                &lt;p contenteditable=&quot;false&quot; style=&quot;margin:0;color:#8a8a8a;font-size:13px;line-height:1.6;user-select:none;pointer-events:none;&quot;&gt;동영상 서비스가 종료되어 해당 콘텐츠를 재생할 수 없습니다.&lt;/p&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span&gt;먼저 고전적인 시그널 핸들링 코드입니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1746708167839&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 전역 함수로 시그널 핸들러 정의
static void signal_handler(int signal_number) 
{
  std::cout &amp;lt;&amp;lt; &quot;시그널 번호 &quot; &amp;lt;&amp;lt; signal_number &amp;lt;&amp;lt; &quot;를 받았어요 (SIGINT)\n&quot;;
}

// 고전적인 방법으로 시그널 핸들러 등록하는 예제
int main()
{
  std::cout &amp;lt;&amp;lt; &quot;고전적인 방법으로 시그널 핸들러를 등록하는 예제\n&quot;;
  std::cout &amp;lt;&amp;lt; &quot;Ctrl+C를 눌러보세요.\n&quot;;

  // 시그널 핸들러 등록
  std::signal(SIGINT, signal_handler);

  // 무한 루프
  while(true) 
  {
    std::cout &amp;lt;&amp;lt; &quot;프로그램 실행 중...\n&quot;;
    std::this_thread::sleep_for(std::chrono::seconds(1));
  }

  return 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;&lt;span style=&quot;color: #333333; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span&gt;위와 같은 코드의 시그널이 처리되는 과정을 살펴보면 아래와 같습니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size23&quot;&gt;시그널이 처리되는 과정(Linux)&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;프로그램이 실행 중일 때 시그널이 발생합니다 (예: Ctrl+C로 SIGINT 발생)&lt;/li&gt;
&lt;li&gt;운영체제는 현재 실행 중인 쓰레드를&amp;nbsp;&lt;b&gt;인터럽트&lt;/b&gt;합니다. 즉, 쓰레드가 실행 중이던 코드가 일시 중단됩니다.&lt;/li&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;이런 방식은 실제로 함수 호출 스택의 컨텍스트 전환(context switching)을 수반하는데, 운영체제에 의해 현재 실행 컨텍스트가 저장되고 시그널 핸들러 컨텍스트로 전환된 다음, 핸들러 실행 후 다시 원래 컨텍스트로 복원되는 과정입니다.&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-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size23&quot;&gt;시그널 처리의 함정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중요한 점은 시그널 핸들러 내에서 할 수 있는 작업이 제한된다는 것인데요, 시그널 핸들러는 &quot;&lt;b&gt;비동기적 시그널 안전(async-signal-safe)&lt;/b&gt;&quot;한 함수만 호출해야 합니다. 그렇지 않으면 데이터 손상이나 교착 상태(deadlock) 등의 문제가 발생할 수 있습니다.&lt;/p&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;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;쓰레드가 뮤텍스(mutex)를 획득하는 중이거나 이미 획득한 상태에서 시그널이 발생&lt;/li&gt;
&lt;li&gt;쓰레드가 인터럽트 되고 시그널 핸들러가 실행됨&lt;/li&gt;
&lt;li&gt;시그널 핸들러가&lt;span&gt;&amp;nbsp;&lt;/span&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;이미 락이 걸려 있으므로 데드락(deadlock) 발생 가능&lt;/li&gt;
&lt;li&gt;또는 재귀적 뮤텍스가 아니라면 정의되지 않은 동작(undefined behavior) 발생&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&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;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;비동기 시그널 안전(async-signal-safe)&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;함수만 호출할 것&lt;/li&gt;
&lt;li&gt;락을 획득하지 말 것&lt;/li&gt;
&lt;li&gt;복잡한 메모리 할당/해제를 수행하지 말 것&lt;/li&gt;
&lt;li&gt;복잡한 I/O 작업을 수행하지 말 것&lt;/li&gt;
&lt;li&gt;전역 변수 수정 시 매우 주의할 것&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 많은 프로그램에서는 시그널 핸들러에서 복잡한 작업을 직접 수행하지 않고, 대신 시그널 발생 여부를 표시하는 플래그(volatile sig_atomic_t 타입 변수따위)만 설정한 후, 메인 루프에서 이 플래그를 주기적으로 확인하여 안전하게 처리하는 방식을 사용합니다. 이런 방식으로 시그널 핸들러와 일반 코드 간의 동기화 문제를 회피할 수 있기 때문이죠.&lt;/p&gt;
&lt;figure id=&quot;og_1746708389136&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;signal-safety(7) - Linux manual page&quot; data-og-description=&quot;signal-safety(7) &amp;mdash; Linux manual page signal-safety(7) Miscellaneous Information Manual signal-safety(7) NAME &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; top signal-safety - async-signal-safe functions DESCRIPTION &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; top An async-signal-safe function is one that can be safely c&quot; data-og-host=&quot;www.man7.org&quot; data-og-source-url=&quot;https://www.man7.org/linux/man-pages/man7/signal-safety.7.html&quot; data-og-url=&quot;https://www.man7.org/linux/man-pages/man7/signal-safety.7.html&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://www.man7.org/linux/man-pages/man7/signal-safety.7.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.man7.org/linux/man-pages/man7/signal-safety.7.html&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&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;signal-safety(7) - Linux manual page&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;signal-safety(7) &amp;mdash; Linux manual page signal-safety(7) Miscellaneous Information Manual signal-safety(7) NAME &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; top signal-safety - async-signal-safe functions DESCRIPTION &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; top An async-signal-safe function is one that can be safely c&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.man7.org&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;text-align: center;&quot; data-ke-size=&quot;size20&quot;&gt;기본적인 시그널 핸들링의 주의점을 살펴보았으므로&lt;/h4&gt;
&lt;h4 style=&quot;text-align: center;&quot; data-ke-size=&quot;size20&quot;&gt;C++ 람다함수를 활용하여 간단하게 시그널 핸들링하는 방법을 구현해 봅니다.&lt;/h4&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&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;&lt;span style=&quot;color: #000000; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot;&gt;이전처럼, &lt;/span&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot; data-darkreader-inline-color=&quot;&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;고전적인 방법으로는 시그널 핸들러를 정적 함수로 만들고 시그널 함수를 통해 등록해야 합니다. 즉, 시그널 함수와 시그널 핸들러 함수가 물리적으로 다른 위치에 있을 수 밖에 없으므로, 가독성이 떨어지고 실수도 생길 수 있습니다.&lt;/p&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_1746693656920&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;

// 람다 시그널 핸들러 인터페이스
int main() 
{
  // 람다를 SIGINT 핸들러로 등록 (Ctrl+C)
  // 시그널(SIGINT)과 그걸 어떻게 처리할지 한번에 등록합니다.
  lambda_signal_handler&amp;lt;SIGINT&amp;gt;([](int sig) 
  {
    std::cout &amp;lt;&amp;lt; &quot;시그널 번호 &quot; &amp;lt;&amp;lt; sig &amp;lt;&amp;lt; &quot;를 받았어요 (SIGINT)\n&quot;;
  });

  // 람다를 SIGTERM 핸들러로 등록합니다. sig인자 없이도 가능하게 합니다.
  lambda_signal_handler&amp;lt;SIGTERM&amp;gt;([]() 
  {
    std::cout &amp;lt;&amp;lt; &quot;시그널 SIGTERM을 받았어요\n&quot;;
  });

  // 무한 루프
  while(true)
  {
    std::cout &amp;lt;&amp;lt; &quot;프로그램 실행 중...\n&quot;;
    std::this_thread::sleep_for(std::chrono::seconds(1));
  }

  return 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;물리적으로 동일한 위치에 시그널과 함께 함수 내용을 기록하므로 코드 가독성을 높이고 실수도 줄일 수 있습니다.&lt;/p&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;span style=&quot;color: #333333; caret-color: auto; letter-spacing: 0px; --darkreader-inline-color: var(--darkreader-text-333333, #c8c3bc);&quot; data-darkreader-inline-color=&quot;&quot;&gt;혹시 모를 정적함수도 시그널 핸들러로 사용할 수 있도록 아래와 같이 하려고 합니다.&lt;/span&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1746708855289&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 정적 함수 정의
static void handle_signal(int sig) 
{
  std::cout &amp;lt;&amp;lt; &quot;정적 함수: 시그널 번호 &quot; &amp;lt;&amp;lt; sig &amp;lt;&amp;lt; &quot;를 받았어요 (SIGINT)\n&quot;;
}

// 정적 함수를 시그널 핸들러로 활용하는 예제
int main() 
{
  // 정적 함수를 SIGINT 핸들러로 등록 (Ctrl+C)
  lambda_signal_handler&amp;lt;SIGINT&amp;gt;(handle_signal);

  // 무한 루프
  while(true) 
  {
    std::cout &amp;lt;&amp;lt; &quot;프로그램 실행 중...\n&quot;;
    std::this_thread::sleep_for(std::chrono::seconds(1));
  }

  return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;LambdaSignalHandler 구현&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;사용방법을 구상했으므로 이제는 사용성에 맞게 구현합니다. 먼저 LambdaSignalHandler를 정의하려고 합니다. &lt;span style=&quot;color: #333333; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span&gt;그래서 &lt;/span&gt;&lt;/span&gt;위에서 정의한 사용법에서는 템플릿 인자로 시그널 번호를 사용했으므로 시그널 번호를 템플릿 인자로 사용할 겁니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;구현하기 전에 중요한 것은 signal-safe 해야 합니다. 때문에 mutex나 std::map등을 사용해서는 안됩니다. 위에서 말한 복잡한 I/O등이 동반되기 때문입니다.&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;따라서 템플릿 인자로 타입이 아닌 int형의 sig_num을 받습니다. 템플릿 인자로 타입뿐 아니라 상수 전달도 가능하며, 이를 비타입 템플릿 파라미터라고 합니다. 이는 컴파일 시간에 전달되는 상수 값입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 위에서 사용한 lambda_signal_handler함수를 friend함수로 하여 사용자가 LambdaSignalHandler를 다루는 행위를 없애 버립니다.&lt;/p&gt;
&lt;pre id=&quot;code_1746691844370&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;template&amp;lt;int sig_num&amp;gt;
class LambdaSignalHandler
{
protected:
  // 우측값 참조 버전
  template&amp;lt;int&amp;gt; friend void lambda_signal_handler(std::function&amp;lt;void(int)&amp;gt; &amp;amp;&amp;amp;handler);
  // 인수가 없는 함수를 위한 오버로드 (우측값 참조)
  template&amp;lt;int&amp;gt; friend void lambda_signal_handler(std::function&amp;lt;void()&amp;gt; &amp;amp;&amp;amp;handler);

  // 왼쪽값 참조(const 참조) 버전
  template&amp;lt;int&amp;gt; friend void lambda_signal_handler(const std::function&amp;lt;void(int)&amp;gt; &amp;amp;handler);
  // 인수가 없는 함수를 위한 오버로드 (왼쪽값 참조)
  template&amp;lt;int&amp;gt; friend void lambda_signal_handler(const std::function&amp;lt;void()&amp;gt; &amp;amp;handler);

  static void wrapper(int signal)
  {
    if (handler_fn != nullptr) handler_fn(signal);
  }

  static std::function&amp;lt;void(int)&amp;gt; handler_fn;
};

template&amp;lt;int sig_num&amp;gt; std::function&amp;lt;void(int)&amp;gt; LambdaSignalHandler&amp;lt;sig_num&amp;gt;::handler_fn = nullptr;

template&amp;lt;int sig_num&amp;gt; void
lambda_signal_handler(const std::function&amp;lt;void(int)&amp;gt; &amp;amp;handler)
{
  LambdaSignalHandler&amp;lt;sig_num&amp;gt;::handler_fn = handler;  // 복사
  std::signal(sig_num, LambdaSignalHandler&amp;lt;sig_num&amp;gt;::wrapper);
}

template&amp;lt;int sig_num&amp;gt; void
lambda_signal_handler(const std::function&amp;lt;void()&amp;gt; &amp;amp;handler)
{
  lambda_signal_handler&amp;lt;sig_num&amp;gt;([h = handler](int) { h(); });  // 복사본 캡처
}

template&amp;lt;int sig_num&amp;gt; void
lambda_signal_handler(std::function&amp;lt;void(int)&amp;gt; &amp;amp;&amp;amp;handler)
{
  LambdaSignalHandler&amp;lt;sig_num&amp;gt;::handler_fn = std::move(handler);  // 이동
  std::signal(sig_num, LambdaSignalHandler&amp;lt;sig_num&amp;gt;::wrapper);
}

template&amp;lt;int sig_num&amp;gt; void
lambda_signal_handler(std::function&amp;lt;void()&amp;gt; &amp;amp;&amp;amp;handler)
{
  lambda_signal_handler&amp;lt;sig_num&amp;gt;([h = std::move(handler)](int) { h(); });  // 이동 캡처
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&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;일반적으로 프로그램 시작 시 한 번만 핸들러를 등록하는 형태로 사용해야 합니다.&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;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;C++의 템플릿 특성을 활용하면 고수준의 언어 다루듯이 사용성을 높일 수 있습니다.&lt;/span&gt;&lt;/blockquote&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&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;있습니다.&lt;/span&gt;&lt;/p&gt;</description>
      <author>tys</author>
      <guid isPermaLink="true">https://cplusplus.tistory.com/15</guid>
      <comments>https://cplusplus.tistory.com/entry/CPP-%EC%8B%9C%EA%B7%B8%EB%84%90-%ED%95%B8%EB%93%A4%EB%A7%81-%EB%9E%8C%EB%8B%A4-%ED%99%9C%EC%9A%A9#entry15comment</comments>
      <pubDate>Thu, 8 May 2025 18:55:16 +0900</pubDate>
    </item>
    <item>
      <title>C++ 강력한 동시성 제어(1) - BlockingQueue의 이해</title>
      <link>https://cplusplus.tistory.com/entry/C-%EA%B0%95%EB%A0%A5%ED%95%9C-%EB%8F%99%EC%8B%9C%EC%84%B1-%EC%A0%9C%EC%96%B41-BlockingQueue</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;C++ 강력한 동시성 제어(1) - BlockingQueue의 이해&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start; --darkreader-inline-color: var(--darkreader-text-333333, #c8c3bc);&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;a href=&quot;https://cplusplus.tistory.com/entry/C-%EA%B0%95%EB%A0%A5%ED%95%9C-%EB%8F%99%EC%8B%9C%EC%84%B1-%EC%A0%9C%EC%96%B42-BlockingQueue-%EA%B5%AC%ED%98%84&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;C++ 강력한 동시성 제어(2) - BlockingQueue 구현&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start; --darkreader-inline-color: var(--darkreader-text-333333, #c8c3bc);&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;a href=&quot;https://cplusplus.tistory.com/entry/C-%EA%B0%95%EB%A0%A5%ED%95%9C-%EB%8F%99%EC%8B%9C%EC%84%B1-%EC%A0%9C%EC%96%B43-BlockingQueue-Logger&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;C++ 강력한 동시성 제어(3) - BlockingQueue을 이용한 Logger&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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot; data-darkreader-inline-color=&quot;&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;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;특히 BlockingQueue는&amp;nbsp;&lt;b&gt;동시성 프로그래밍이 필요한 어플리케이션 개발에서는 꼭 알아야 할 지식&lt;/b&gt;이라고 할 수 있습니다. &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;국내에서 많이 사용되는 언어인 JAVA(java.util.concurrent.BlockingQueue)나 Go(channel) 등은 블록킹큐 패턴을 지원합니다. 하지만 C++에서는 이를 빌트인으로 지원하지 않기에 사용자가 직접 구현해야 합니다.&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;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;--darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;여기서는 BlockingQueue가 무엇인지 간단히 알아보고. 다음 글들을 통해 BlockingQueue를 구현합니다.&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;h3 id=&quot;h3-3&quot; style=&quot;color: #172f47; text-align: start; --darkreader-inline-color: var(--darkreader-text-172f47, #cac6bf);&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;BlockingQueue 구성요소&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;아래 한 장의 그림은 블럭킹큐의 모든 것을 말해줍니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2194&quot; data-origin-height=&quot;755&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgjBsE/btsNJ5z4piY/nRnewjrYAa4r0lhW11SAPK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgjBsE/btsNJ5z4piY/nRnewjrYAa4r0lhW11SAPK/img.png&quot; data-alt=&quot;블럭킹큐 구성&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgjBsE/btsNJ5z4piY/nRnewjrYAa4r0lhW11SAPK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbgjBsE%2FbtsNJ5z4piY%2FnRnewjrYAa4r0lhW11SAPK%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;619&quot; height=&quot;213&quot; data-origin-width=&quot;2194&quot; data-origin-height=&quot;755&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;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;블럭킹큐는 생산자 쓰레드와 소비자 쓰레드 두 개체에서 서로 공유되는 큐입니다. 소비자 쓰레드는 블럭킹큐의 pop함수를 통해 큐로 데이터가 도착할 때까지 대기하게 됩니다. 이때 소비자 쓰레드에서 블럭킹 큐로 데이터를 전달하면 소비자 쓰레드의 pop함수는 깨어나 큐의 데이터를 가져오게 됩니다. &lt;/span&gt;&lt;/p&gt;
&lt;figure data-ke-type=&quot;image&quot; data-ke-style=&quot;alignCenter&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span style=&quot;letter-spacing: 0px; color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;/span&gt;
&lt;figcaption style=&quot;display: none;&quot;&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&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;size18&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;BlockingQueue&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;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;블럭킹 큐는 컨테이너로 생산자(프로듀서)와 소비자(컨슈머) 사이의 버퍼역할을 하며 Queue(FIFO, First-In, First-Out)형식으로 동작합니다. 이외에 우선순위등을 적용하는 비 FIFO큐도 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;생산자(Producer)&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;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;소비자에게 전달할 데이터를 push하는 주체입니다. 블럭킹큐는 push함수를 제공함으로써 생산자로부터 데이터를 전달할 수 있도록 합니다. 큐가 가득 찼을 때 다음과 같은 기능을 제공할 수 있습니다, 즉시반환만을 구현하는 것이 좋습니다. 생산자 측면에서의 블럭킹은 대체로 나쁜 설계를 만들어 낼 수 있기 때문입니다.&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;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;블럭킹: 대기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;즉시반환: 가득 참 상태를 반환하고 요청 거부&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;타임아웃: 일정 시간만 대기 후 타임아웃&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;소비자(Consumer)&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;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;소비자는 블럭킹큐를 통해 생산자로부터 전달받은 데이터를 수신하게 됩니다. 만약 수신된 데이터가 없다면 수신될 때까지 대기하게 됩니다. 또 pop은 아래와 같은 기능을 제공할 수 있습니다. 여기서는 아래 두 가지 기능을 모두 구현할 겁니다.&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;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;블럭킹: 대기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;타임아웃: 일정 시간만 대기 후 타임아웃&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 style=&quot;color: #172f47; text-align: start; --darkreader-inline-color: var(--darkreader-text-172f47, #cac6bf);&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 id=&quot;h3-3&quot; style=&quot;color: #172f47; text-align: start; --darkreader-inline-color: var(--darkreader-text-172f47, #cac6bf);&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;BlockingQueue는 어디에 사용하는가?&lt;/span&gt;&lt;/h3&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;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;로그 수집&lt;br /&gt;생산자는 로깅 데이터를 블럭킹큐에 담으면 소비자는 로깅 데이터를 디스크에 기록하거나 화면에 출력합니다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Database처리&lt;br /&gt;생산자는 DB에 입력할 데이터를 소비자는 데이터를 DB에 기록하게 하여 책임을 분리할 수 있습니다. 이렇게 물리적으로 분리된 코드는 생산자 데이터 처리에 집중할 수 있게 하고 소비자는 DB기록에 집중할 수 있게 합니다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;외부연동 요청처리&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;연동된 클라이언트 혹은 서버로부터 요청을 처리하는 어플리케이션의 경우, 요청을 받는 쓰레드(생산자)와 실제로 처리하는 쓰레드(소비자)를 분리함으로써 요청을 받는 코드와, 처리하는 코드를 분리할 수 있습니다. 이렇게 함으로써 설계원칙중 하나인&amp;nbsp; 단일책임 원칙을 지키게 됩니다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;※&lt;span style=&quot;background-color: #ffffff; letter-spacing: 0px; --darkreader-inline-bgcolor: var(--darkreader-background-ffffff, #181a1b);&quot; data-darkreader-inline-bgcolor=&quot;&quot;&gt; 큐를 두고 처리하는 외부 연동 요청처리는 숏/롱 트랜잭션을 분리하여 롱트랜잭션인 경우만 큐를 통해 처리하는 것이 바람직합니다. 이는 아래에 다시 말하겠지만 블럭킹 큐에 pop과 push는 적지 않은 오버헤드가 있기 때문입니다.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;※&lt;span style=&quot;letter-spacing: 0px;&quot;&gt; 언급된 외부연동 처리는 아키텍쳐 패턴인 Half Sync/Async 패턴이라고 봐도 무방합니다. 일반적으로 Half Sync/Async패턴의 큐잉계층을 블럭킹큐로 사용하여 비동기계층과 동기계층을 연동하게 합니다.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;span style=&quot;color: #000000; text-align: left; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;※ BlockingQueue를 활용한 아키텍처로는 Half Sync/Half Async 패턴이 대표적이며, 추가로 관심이 있으시다면 Reactor, Proactor, Leader/Followers 패턴도 함께 참고해 보실 수 있습니다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1746445711613&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Half-Sync/Half-Async Pattern in Java: Enhancing System Performance with Dual Processing&quot; data-og-description=&quot;Learn how the Half-Sync/Half-Async design pattern in Java improves concurrency and system efficiency by decoupling asynchronous and synchronous processing. Explore real-world examples, programmatic implementations, and key use cases.&quot; data-og-host=&quot;java-design-patterns.com&quot; data-og-source-url=&quot;https://java-design-patterns.com/patterns/half-sync-half-async/&quot; data-og-url=&quot;https://java-design-patterns.com/patterns/half-sync-half-async/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://java-design-patterns.com/patterns/half-sync-half-async/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://java-design-patterns.com/patterns/half-sync-half-async/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&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;Half-Sync/Half-Async Pattern in Java: Enhancing System Performance with Dual Processing&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Learn how the Half-Sync/Half-Async design pattern in Java improves concurrency and system efficiency by decoupling asynchronous and synchronous processing. Explore real-world examples, programmatic implementations, and key use cases.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;java-design-patterns.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;[코드가 자바이긴 하지만...;;]&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; 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: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;물론 이 외에도 다양한 곳에 응용할 수 있습니다. 그래서 강력한 패턴이며 아주 유용합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;h3-3&quot; style=&quot;color: #172f47; text-align: start; --darkreader-inline-color: var(--darkreader-text-172f47, #cac6bf);&quot; data-ke-size=&quot;size23&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;BlockingQueue 장점과 단점&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;장점&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;블럭킹 큐를 통해 역할과 책임이 분리됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;손쉽게 구현할 수 있으며 쓰레드간 데이터 동기화가 쉽습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;데이터의 전달과정에 관계 없이, 생산자, 소비자 코드는 오로지 비즈니스로직에 집중할 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;생산자와 소비자간의 데이터 전달 간의 락을 사용하게 되므로 데이터 전달 과정의 오버헤드가 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;생산자 측면에서 큐가 가득 찼을 경우의 예외처리에 신경 써야 합니다. 즉, 설계상 가득 찼을 때의 예외도 하나의 기능으로써 포함되어야 합니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 style=&quot;color: #172f47; text-align: start; --darkreader-inline-color: var(--darkreader-text-172f47, #cac6bf);&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 id=&quot;h3-3&quot; style=&quot;color: #172f47; text-align: start; --darkreader-inline-color: var(--darkreader-text-172f47, #cac6bf);&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;BlockingQueue의 또 다른 대안 Lockfree Queue&lt;/span&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;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left; --darkreader-inline-bgcolor: var(--darkreader-background-ffffff, #181a1b); --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-bgcolor=&quot;&quot; data-darkreader-inline-color=&quot;&quot;&gt;위의 단점에 나온 전달 과정의 오버헤드를 없애기 위한 시도 중 하나가 Lockfree Queue입니다. Lockfree Queue에 대한 내용은 다음에 상세히? 언급되어 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1746445993131&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;C++ Lockfree Queue가 항상 좋은 선택일까?&quot; data-og-description=&quot;동시성 프로그래밍에서 락-프리(queue) 데이터 구조는 매력적으로 보일 수 있습니다. 블로킹이 없는 비블로킹 설계 덕분에 높은 성능과 낮은 지연 시간을 기대할 수 있기 때문입니다. 그러나 락-&quot; data-og-host=&quot;cplusplus.tistory.com&quot; data-og-source-url=&quot;https://cplusplus.tistory.com/entry/C-Lockfree-Queue%EA%B0%80-%ED%95%AD%EC%83%81-%EC%A2%8B%EC%9D%80-%EC%84%A0%ED%83%9D%EC%9D%BC%EA%B9%8C&quot; data-og-url=&quot;https://cplusplus.tistory.com/entry/C-Lockfree-Queue%EA%B0%80-%ED%95%AD%EC%83%81-%EC%A2%8B%EC%9D%80-%EC%84%A0%ED%83%9D%EC%9D%BC%EA%B9%8C&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/eQehm/hyYMSJbxlE/eF2yv5nAkLtYLm5BPHjwD1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/bFUw31/hyYMTH6M1J/yZHEkKe8KTIHf1kCupcR41/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800&quot;&gt;&lt;a href=&quot;https://cplusplus.tistory.com/entry/C-Lockfree-Queue%EA%B0%80-%ED%95%AD%EC%83%81-%EC%A2%8B%EC%9D%80-%EC%84%A0%ED%83%9D%EC%9D%BC%EA%B9%8C&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://cplusplus.tistory.com/entry/C-Lockfree-Queue%EA%B0%80-%ED%95%AD%EC%83%81-%EC%A2%8B%EC%9D%80-%EC%84%A0%ED%83%9D%EC%9D%BC%EA%B9%8C&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/eQehm/hyYMSJbxlE/eF2yv5nAkLtYLm5BPHjwD1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/bFUw31/hyYMTH6M1J/yZHEkKe8KTIHf1kCupcR41/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800');&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;C++ Lockfree Queue가 항상 좋은 선택일까?&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;동시성 프로그래밍에서 락-프리(queue) 데이터 구조는 매력적으로 보일 수 있습니다. 블로킹이 없는 비블로킹 설계 덕분에 높은 성능과 낮은 지연 시간을 기대할 수 있기 때문입니다. 그러나 락-&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;cplusplus.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;해당 글을 참조하시면 &quot;정말로 필요하지 않으면 Lockfree(CAS경합)를 구현하여 사용하지 말아라&quot;라는 C++ Core Guidelines의 권고를 확인할 수 있습니다. 구현과 테스트가 정말로 어렵고, 외부 라이브러리의 사용에도 추가적으로 작업해야 할 요소들도 있기 때문입니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>blockingqueue</category>
      <category>blockingvector</category>
      <category>C++</category>
      <category>lockfree</category>
      <category>Pattern</category>
      <category>Queue</category>
      <author>tys</author>
      <guid isPermaLink="true">https://cplusplus.tistory.com/12</guid>
      <comments>https://cplusplus.tistory.com/entry/C-%EA%B0%95%EB%A0%A5%ED%95%9C-%EB%8F%99%EC%8B%9C%EC%84%B1-%EC%A0%9C%EC%96%B41-BlockingQueue#entry12comment</comments>
      <pubDate>Mon, 5 May 2025 21:26:33 +0900</pubDate>
    </item>
    <item>
      <title>C++ thread local storage: 경쟁 없는 리소스 관리</title>
      <link>https://cplusplus.tistory.com/entry/thread-local-storage-%EA%B2%BD%EC%9F%81-%EC%97%86%EB%8A%94-%EB%A6%AC%EC%86%8C%EC%8A%A4-%EA%B4%80%EB%A6%AC</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span&gt;C++은 11 버전부터 Thread Local Storage를 지원하기 시작했습니다. 그 이전에는 Windows NT (__declspec(thread)) - 1994, POSIX threads (pthread_key_*) - 1995, GCC (__thread) - 2002에서 지원하기 시작했습니다. &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;span&gt;이 오래된 Thread Local Storage(이하 TLS)는 그만큼 &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;매우 유용한 기능이라 볼 수 있습니다. errno도 대표적인 TLS변수입니다. TLS의 장점은 각 개별 쓰레드가 필요한 리소스 경쟁을 피하면서도 &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;&amp;nbsp;&lt;/p&gt;

            &lt;figure class=&quot;unsupported component-kakaotv&quot; contenteditable=&quot;false&quot; style=&quot;background:#000;margin:16px 0;min-height:72px;padding:10px 16px;display:flex;align-items:center;justify-content:center;text-align:center;box-sizing:border-box;width:100%;max-width:100%;&quot;&gt;
                &lt;p contenteditable=&quot;false&quot; style=&quot;margin:0;color:#8a8a8a;font-size:13px;line-height:1.6;user-select:none;pointer-events:none;&quot;&gt;동영상 서비스가 종료되어 해당 콘텐츠를 재생할 수 없습니다.&lt;/p&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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;같이보면 좋은 글&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://cplusplus.tistory.com/entry/C-%EB%A9%80%ED%8B%B0%EC%93%B0%EB%A0%88%EB%93%9C-Read-Only-%EB%8D%B0%EC%9D%B4%ED%84%B0%EC%85%8B%EC%9D%98-%EC%97%85%EB%8D%B0%EC%9D%B4%ED%8A%B8%EC%99%80-%EB%9D%BD%ED%94%84%EB%A6%AC-%EC%A0%91%EA%B7%BC-RCU&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-darkreader-inline-color=&quot;&quot;&gt;C++&amp;nbsp;멀티쓰레드:&amp;nbsp;Read-Only&amp;nbsp;데이터셋의&amp;nbsp;업데이트와&amp;nbsp;락프리&amp;nbsp;접근&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://cplusplus.tistory.com/entry/C17-Lock-Free-RCU-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC-cppurcu&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-darkreader-inline-color=&quot;&quot;&gt;C++&amp;nbsp;std::mutex보다&amp;nbsp;빠른&amp;nbsp;RCU(Lock-free)&amp;nbsp;라이브러리:&amp;nbsp;cppurcu&lt;/a&gt;&lt;/span&gt;&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; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;들어가며&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;C++에서는 thread도 contional_variable(signal)도 그렇고 사용법만 보면 아주 쉬어 보이고 단순합니다. 특히나 C++11 이후에 와서는 그 사용법들이 더 단순해지고 명료해 보이기도 합니다. 소켓도 그렇습니다. recv와 send만 있으면 이제 다 할 수 있을 것만 같습니다. &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;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start; --darkreader-inline-color: var(--darkreader-text-333333, #c8c3bc);&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start; --darkreader-inline-color: var(--darkreader-text-333333, #c8c3bc);&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;thread local stroage도 마찬가지입니다. thread_local의 기능만 보면 이처럼 단순할 수가 없습니다. 키워드만 달면 thread전용이 되고 경쟁도 필요 없어지니까요. 하지만 다른 곳에서 참조해야 하며 객체별로 고유의 thread_local을 갖는다던가 하는 것은 또 다른 테크닉과 아키텍처와의 싸움이 됩니다.&lt;/span&gt;&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; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-ke-size=&quot;size26&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;thread_local: 특징&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span&gt;thread_local 변수는&amp;nbsp;&lt;/span&gt;&lt;span&gt;쓰레드가 생성될 때 자기 자신만의 것으로 만들어집니다. 다른 쓰레드들 또한 생성될 때 각자 자기만의 my_name을 갖게 되는 것입니다. 서로 다른 쓰레드의 my_name을 접근할 수는 없습니다. (남의 이름을 훔치는 방법이 아주 없진 않습니다.)&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1738849305871&quot; class=&quot;cpp&quot; style=&quot;background-color: #f8f8f8; color: #383a42; --darkreader-inline-bgcolor: var(--darkreader-background-f8f8f8, #1c1e1f); --darkreader-inline-color: var(--darkreader-text-383a42, #c1bcb4);&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot; data-darkreader-inline-bgcolor=&quot;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;code&gt;// 전역 또는 지역 변수, 정의된 변수는 다른 곳에서 thread_local을 통해 접근할 수 있습니다.
thread_local std::string my_name;

// 해당 파일 내에서만 접근 가능하지만, 같은 쓰레드 내에서 공유되는 변수가 됩니다.
// 즉, 링크 단위(local linkage)를 제한하는 static과,
// 쓰레드마다 고유 인스턴스를 가지게 하는 thread_local이 조합된 형태입니다.
static thread_local std::string my_name;

void foo()
{
  // 함수내에서 같은 쓰레드에서만 동일한 변수에 접근할 수 있습니다.
  // 이 함수를 여러번 호출하더라도 쓰레드에 한정해서 static한 동일한 변수가 됩니다.
  thread_local std::string my_name;
}

class Foo
{
  // 같은 쓰레드에서만 동일한 클래스 멤버변수에 접근할 수 있습니다. 
  // 선언시에는 static키워드를 포함해야 합니다. 이는 각 객체 인스턴스마다
  // 다른 메모리를 가져야 하는 non-static 멤버와 thread_local의 특성이 충돌하기 때문입니다
  static thread_local std::string my_name;
}&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;thread_local 변수의 생명주기는 쓰레드의 생명 주기에 맞춰 생성되고 소멸됩니다. 즉, 각 쓰레드마다 독립적인 인스턴스를 가지며, 해당 쓰레드가 종료될 때까지 살아 있게 됩니다. &lt;span style=&quot;text-align: start;&quot;&gt;저는 적은 정보량의 경우 데이터가 중복되더라도 각각의 쓰레드에 동일한 데이터의 TLS를 사용하곤 합니다. 동시성에서 경쟁에서의 손실보다는 메모리 손실에서 얻는 이득이 훨씬 크 경우가 있기 때문입니다.&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;blockquote style=&quot;color: #666666; text-align: left; --darkreader-inline-color: var(--darkreader-text-666666, #a8a095);&quot; data-ke-style=&quot;style2&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;C++ Core Guidelines&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;소량의 데이터는 잠금 메커니즘을 사용하여 공유하는 것보다 복사하고 액세스하는 것이 더 저렴합니다. 복사는 자연스럽게 고유한 소유권을 부여하고(코드를 단순화) 데이터 경쟁 가능성을 제거합니다.&lt;/span&gt;&lt;/blockquote&gt;
&lt;figure id=&quot;og_1738849172542&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;C++ Core Guidelines&quot; data-og-description=&quot;&quot; data-og-host=&quot;isocpp.github.io&quot; data-og-source-url=&quot;https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#cp31-pass-small-amounts-of-data-between-threads-by-value-rather-than-by-reference-or-pointer&quot; data-og-url=&quot;https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#cp31-pass-small-amounts-of-data-between-threads-by-value-rather-than-by-reference-or-pointer&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bNxsGt/hyYb7eUOdq/AoJxSXLLU8kMOBz9MdSIT1/img.png?width=1412&amp;amp;height=619&amp;amp;face=0_0_1412_619,https://scrap.kakaocdn.net/dn/ZCqaK/hyYcao9S0Q/xAPi9NZ6UMOMiev6qRMMak/img.png?width=1412&amp;amp;height=604&amp;amp;face=0_0_1412_604&quot;&gt;&lt;a href=&quot;https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#cp31-pass-small-amounts-of-data-between-threads-by-value-rather-than-by-reference-or-pointer&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#cp31-pass-small-amounts-of-data-between-threads-by-value-rather-than-by-reference-or-pointer&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bNxsGt/hyYb7eUOdq/AoJxSXLLU8kMOBz9MdSIT1/img.png?width=1412&amp;amp;height=619&amp;amp;face=0_0_1412_619,https://scrap.kakaocdn.net/dn/ZCqaK/hyYcao9S0Q/xAPi9NZ6UMOMiev6qRMMak/img.png?width=1412&amp;amp;height=604&amp;amp;face=0_0_1412_604');&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;C++ Core Guidelines&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;isocpp.github.io&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 style=&quot;color: #000000; text-align: start; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;언제 사용하나?&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;제가 사용할 때 가장 즐겨 쓰는 경우는 아래 같은 것입니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1734859520917&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 기본적인 커넥터 예시
thread_local Connection *conn = nullptr;

// 단순하게 커넥션을 가져오는 함수
Connection* get_connection()
{
  if (conn == nullptr)
    conn = new Connection(&quot;host&quot;, &quot;user&quot;, &quot;pwd&quot;);

  return conn;
}&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;이렇게 사용하면 각 thread별로 커넥터가 만들어집니다. 별도로 커넥션 풀을 만들 필요가 없겠죠? 쓰레드가 get_connection을 호출하지 않는 이상 연결은 없습니다. &lt;span style=&quot;text-align: start;&quot;&gt;물론 실제 구현시에는 많은 예외 처리가 필요하지만 이제 접하는 분이라면 thread_local에 대한 기본 개념은 충분히 설명되지 않았을까 싶습니다.&lt;/span&gt;&lt;/span&gt;&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; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-ke-size=&quot;size26&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;제약 조건&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;thread_local은 비정적 변수로 사용할 수 없습니다. thread_local 선언만으로도 쓰레드에 한정된 static 성질을 갖게 되어 클래스 내 비정적 멤버를 가질 수 없습니다. 이것은 쓰레드가 생성될 때 만들어지는 변수이기 때문입니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1737274141497&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Connection
{
public:
  .....
  static thread_local connection conn;
};&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;conn은 Connection 클래스가 인스턴스화되지 않더라도 쓰레드가 생성되면 접근 가능한 변수가 되어버립니다. 즉 동일한 쓰레드라면 Connection이라는 인스턴스가 1개든 100개든 동일한 conn을 가지므로 인스턴스별 고유한 conn을 가질 수 없게 됩니다.&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;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;예를 들어 동일한 쓰레드내에서 DB를 사용하는 Connection클래스라 할 때, 하나의 Connection에는 A데이터베이스를 또 다른 Connection에는 B데이터 베이스를 사용하고자 한다면 thread_local이 유용한 기능임에도 사용하기 어렵습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1737282660733&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 그 어디선가...
// 서로 다른 객체로 선언하더라도 Connection 클래스내
// static thread_local connection conn;의 선언으로 동일한 conn에 접근하게 됩니다.
Connection a;
Connection b;

.....
.......
..

// 아래는 동일 쓰레드라 가정할 때,
// 여기서 a.conn과 b.conn은 동일 객체
DBStream stream(query, a.conn);

std::cout &amp;lt;&amp;lt; reinterpret_cast&amp;lt;uintptr_t&amp;gt;(&amp;amp;a.conn)
          &amp;lt;&amp;lt; &quot;==&quot;
          &amp;lt;&amp;lt; reinterpret_cast&amp;lt;uintptr_t&amp;gt;(&amp;amp;b.conn) &amp;lt;&amp;lt; std::endl;&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;하지만 아래와 같은 트릭으로 동일한 쓰레드 내에서 서로 다른 인스턴스가 서로 다른 thread local stroage를 가질 수 있게 하며 생성과 소멸도 내가 원하는 시점에 할 수도 있습니다.&lt;/span&gt;&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; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;인스턴스별 Thread Local Storage&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;위의 제약조건을 가진 탓에 객체의 인스턴스별로 thread local storage를 갖기 어렵지만, 다음과 같은 방법으로 해결할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1737283116636&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#pragma once

#include &amp;lt;map&amp;gt;
#include &amp;lt;memory&amp;gt;

template &amp;lt;typename T&amp;gt;
class ThreadLocalInstance
{
public:
  virtual ~ThreadLocalInstance()
  {
    storage_.erase(self_);
  }

  .....get, set...

protected:
  /** @brief 현재 인스턴스의 주소값을 정수형으로 변환하여 저장 (키로 사용) */
  uintptr_t self_ = reinterpret_cast&amp;lt;uintptr_t&amp;gt;(this);

  /** @brief 쓰레드별 저장소. 인스턴스 주소를 키로 사용하는 맵 */
  static thread_local std::map&amp;lt;uintptr_t, std::shared_ptr&amp;lt;T&amp;gt;&amp;gt; storage_;
};&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;&lt;span style=&quot;color: #000000; text-align: start; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;가장 중요한 부분은 thread_local std::map&amp;lt;uintptr_t, std::shared_ptr&amp;lt;T&amp;gt;&amp;gt; storage_; 입니다. 자기 자신을 키로 하는 thread local storage를 갖게 되는 것입니다.&lt;/span&gt;&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; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-ke-size=&quot;size26&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;다른 쓰레드의 저장소들을 가져오려면?&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;이제 앞서 얘기한 남의 이름을 가져오는 방법입니다. 사실 저는 잘 사용하지 않습니다. 경쟁조건이 붙기도 하기 때문입니다. atomic을 사용하는 방법이 있긴 합니다만;;;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1737283780061&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;deque&amp;gt;
#include &amp;lt;mutex&amp;gt;

class MyTls; ///&amp;lt; 전방위 선언

std::mutex repo_lock;
std::deque&amp;lt;MyTls *&amp;gt; repo;

class MyTls
{
public:
  MyTls() 
  {
    std::lock_guard&amp;lt;std::mutex&amp;gt; guard(repo_lock);
    repo.push_back(this);
  }
};

thread_local MyTls my_tls;&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;MyTls가 생성될 때마다 repo에 저장되는 걸 볼 수 있습니다.&lt;/span&gt;&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; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;마무리&lt;/span&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;pre id=&quot;code_1737284817219&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#pragma once

#include &amp;lt;map&amp;gt;
#include &amp;lt;stdint.h&amp;gt;
#include &amp;lt;memory&amp;gt;

template &amp;lt;typename T&amp;gt;
class ThreadLocalInstance
{
public:
  ThreadLocalInstance() {}
  ThreadLocalInstance(const std::shared_ptr&amp;lt;T&amp;gt; &amp;amp;value)
  {
    set(self_, value);
  }

  virtual ~ThreadLocalInstance()
  {
    storage_.erase(self_);
  }

  virtual ThreadLocalInstance&amp;lt;T&amp;gt; &amp;amp;
  set(const std::shared_ptr&amp;lt;T&amp;gt; &amp;amp;value)
  {
    storage_[self_] = value;
    return *this;
  }

  virtual bool
  has()
  {
    return storage_.count(self_) &amp;gt; 0;
  }

  virtual std::shared_ptr&amp;lt;T&amp;gt;
  get(std::function&amp;lt;void(std::shared_ptr&amp;lt;T&amp;gt; &amp;amp;instance)&amp;gt; creator = nullptr)
  {
    if (has() == false)
      if (creator != nullptr)
        creator(storage_[self_]);
    return storage_[self_];
  }

  virtual const std::shared_ptr&amp;lt;T&amp;gt;
  get() const
  {
    auto it = storage_.find(self_);
    if (it == storage_.end()) return nullptr;
    return it-&amp;gt;second;
  }

  void
  clear()
  {
    storage_.clear();
  }

protected:
  /** @brief 현재 인스턴스의 주소값을 정수형으로 변환하여 저장 (키로 사용) */
  uintptr_t self_ = reinterpret_cast&amp;lt;uintptr_t&amp;gt;(this);

  /** @brief 쓰레드별 저장소. 인스턴스 주소를 키로 사용하는 맵 */
  static thread_local std::map&amp;lt;uintptr_t, std::shared_ptr&amp;lt;T&amp;gt;&amp;gt; storage_;
};

template &amp;lt;typename T&amp;gt;
thread_local std::map&amp;lt;uintptr_t, std::shared_ptr&amp;lt;T&amp;gt;&amp;gt; ThreadLocalInstance&amp;lt;T&amp;gt;::storage_;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p style=&quot;text-align: right;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;- 끝 -&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&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;있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;div id=&quot;gtx-anchor&quot; style=&quot;position: absolute; visibility: hidden; left: 10px; top: 1480px; width: 847.656px; height: 44.8646px;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;jfk-bubble gtx-bubble&quot; style=&quot;visibility: visible; left: 4px; top: 1555px; opacity: 1;&quot; role=&quot;alertdialog&quot; aria-describedby=&quot;bubble-13&quot;&gt;
&lt;div id=&quot;bubble-13&quot; class=&quot;jfk-bubble-content-id&quot;&gt;
&lt;div id=&quot;gtx-host&quot; style=&quot;min-width: 200px; max-width: 400px;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;jfk-bubble-closebtn-id jfk-bubble-closebtn&quot; tabindex=&quot;0&quot; role=&quot;button&quot; aria-label=&quot;Close&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;jfk-bubble-arrow-id jfk-bubble-arrow jfk-bubble-arrowup&quot; style=&quot;left: 424px;&quot;&gt;
&lt;div class=&quot;jfk-bubble-arrowimplbefore&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;jfk-bubble-arrowimplafter&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
      <category>C++</category>
      <category>cpp</category>
      <category>thread local stroage</category>
      <category>thread_local</category>
      <author>tys</author>
      <guid isPermaLink="true">https://cplusplus.tistory.com/10</guid>
      <comments>https://cplusplus.tistory.com/entry/thread-local-storage-%EA%B2%BD%EC%9F%81-%EC%97%86%EB%8A%94-%EB%A6%AC%EC%86%8C%EC%8A%A4-%EA%B4%80%EB%A6%AC#entry10comment</comments>
      <pubDate>Mon, 20 Jan 2025 10:19:08 +0900</pubDate>
    </item>
    <item>
      <title>고인물은 싫어하는 C++ 템플릿 활용: concat_with_delimiter</title>
      <link>https://cplusplus.tistory.com/entry/%EA%B3%A0%EC%9D%B8%EB%AC%BC%EC%9D%80-%EC%8B%AB%EC%96%B4%ED%95%98%EB%8A%94-C-%ED%85%9C%ED%94%8C%EB%A6%BF-%ED%99%9C%EC%9A%A9-concatwithdelimiter</link>
      <description>&lt;h3 style=&quot;color: #000000; text-align: start; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-ke-size=&quot;size23&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;한국에서 푸대접받는 C++ 템플릿&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;--darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot;&gt;맞습니다. 고인물들은 템플릿을 지독히 싫어합니다. 가끔 휴식시간에 이야기를 나누어 보면 &quot;템플릿 오류메시지가 너무 복잡하다, 코드 따라가기 힘들다. 무슨 외계어냐&quot;라는 비아냥을 들을 때가 있습니다. &lt;/span&gt;&lt;span style=&quot;--darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&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;&amp;nbsp;&lt;/p&gt;

            &lt;figure class=&quot;unsupported component-kakaotv&quot; contenteditable=&quot;false&quot; style=&quot;background:#000;margin:16px 0;min-height:72px;padding:10px 16px;display:flex;align-items:center;justify-content:center;text-align:center;box-sizing:border-box;width:100%;max-width:100%;&quot;&gt;
                &lt;p contenteditable=&quot;false&quot; style=&quot;margin:0;color:#8a8a8a;font-size:13px;line-height:1.6;user-select:none;pointer-events:none;&quot;&gt;동영상 서비스가 종료되어 해당 콘텐츠를 재생할 수 없습니다.&lt;/p&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;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;템플릿을 이용한 구현과 문자열세기의 TRADE-OFF&lt;/span&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;저도 오래된 개발자이다 보니 C부터 시작했습니다. C로 할 때는 디버깅에 많은 시간을 보내야만 했습니다. 배열 인덱스를 잘못 가리키는 문제를 찾는다던가, 도대체 &lt;span style=&quot;text-align: start; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&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;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;템플릿은 해석하기도 만들기도 다소 난해하고 어렵지만 디버깅에 쏟는 시간을 구현시간에 쏟는 trade-off 전략입니다. 템플릿은 내가 하기 싫은 일, 특히 실수하기 쉬운 일을 컴파일러에게 맡기는 일입니다. 템플릿은 컴파일러에게 코드를 만들어 달라는 요청과도 같습니다.&lt;/span&gt;&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; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-ke-size=&quot;size23&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;C로 만든 손으로 문자열 세기&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;아래 코드는 C로 CSV를 출력하는 예제입니다. 실제 프로덕션 환경에서 자주 볼 수 있는 C코드입니다. 프로세스를 구성하는 소스 코드에서 아주 많이 자주 보는 코드로, 로그를 남기는 코드로도 많이 사용합니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1734840300461&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;   char buffer[1024];
   sprintf(buffer, &quot;%s,%d,%.1f,%s,%d,%s,%.2f,%s,%d,%d&quot;, 
          &quot;a&quot;, 123, 4.4f, &quot;333&quot;, 42, &quot;hello&quot;, 3.14f, &quot;world&quot;, 777, 999);&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;포맷 지정자와 실제 인자 타입 불일치 가능성&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;버퍼 오버플로우 위험(물론 snprintf도 있지만, 다양한 경우에 대응하도록 만들기 쉽지 않습니다.)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;유지보수의 어려움&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;데이터 손실 가능성&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;개발 시에도, 디버깅 시에도 문자열을 세어가며 비교해야 합니다. 더불어 인자로 넘겼던 버퍼들의 크기와 타입도 모두 맞추어 주어야 합니다. &lt;span style=&quot;text-align: start; --darkreader-inline-color: var(--darkreader-text-e8e6e3, #d8d4cf);&quot;&gt;모던 C++을 모르는 많은 개발자들이 이런 것으로 반나절 이상 시간을 보내기도 합니다. &lt;/span&gt;물론 모든 걸 신경 써가며 작성이 불가능한 일은 아닐 겁니다.&lt;/span&gt;&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; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;템플릿 가변인자를 이용한 문자열, concat_with_delimiter 인터페이스&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;더 이상 문자열 맞추는 데에 소비하지&amp;nbsp;않게 하려 합니다. 그래서 아래와 같은 간단한 사용법을 먼저 구상합니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1734840402720&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;std::string my_csv = concat_with_delimiter(&quot;,&quot;, 
                                           &quot;a&quot;, 123, 4.4f, &quot;333&quot;, 
                                           42, &quot;hello&quot;, 3.14f, &quot;world&quot;, 
                                           777, 999);&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;여기서 맨 앞에 &quot;,&quot;은 delimiter입니다. 뒤에 인자가 문자로 오든, float이 오든 컴파일러와 템플릿이 알아서 만들어 주게 됩니다. 그래서 오롯이 구현에만 집중하며 더 이상 문자열 맞추는 일은 구현에도 디버깅에도 하지 않게 합니다.&lt;/span&gt;&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; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-ke-size=&quot;size23&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;C++17: concat_with_delimiter&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;C++17에서는 총 3개의 함수로 구성됩니다. C++11 버전은 아래에 있습니다. 더 복잡합니다.&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;color: #000000; text-align: start; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;첫 번째 함수입니다. 일반 타입에 대한 처리함수는 std::stringstream을 이용합니다. std::stringstream도 템플릿 구현체입니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1734842388160&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 일반 타입에 대한 처리 입니다.
template&amp;lt;typename T&amp;gt; std::stringstream &amp;amp;
concat_with_delimiter_helper(const T &amp;amp;t, const std::string &amp;amp;delim, std::stringstream &amp;amp;stream)
{
  stream &amp;lt;&amp;lt; t &amp;lt;&amp;lt; delim;
  return stream;
}&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;두 번째 함수입니다. 사실 없어도 되지만 저는 boolean형식의 경우 &quot;true&quot;와 &quot;false&quot;로 표기되는 것을 좋아합니다. 그래서 boolean의 경우 템플릿 특수화를 통해 제가 좋아하는 형식으로 표현되도록 했습니다. 이 외에도 자신의 편의에 따라 특수화할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1734842907397&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;template&amp;lt;&amp;gt; inline std::stringstream &amp;amp;
concat_with_delimiter_helper(bool t, const std::string &amp;amp;delim, std::stringstream &amp;amp;stream)
{
  stream &amp;lt;&amp;lt; (t ? &quot;true&quot; : &quot;false&quot;) &amp;lt;&amp;lt; delim;
  return stream;
}&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;세 번째 함수입니다. 가장 중요한 실제 사용자가 호출하는 함수입니다. 보셨다시피 이 함수와 헬퍼가 되는 위 함수를 구분하기 위해 헬퍼 함수들은 concat_with_delimter_helper이라고 지었습니다. 함수의 파라미터와 이름이 동일한 경우는 컴파일이 되지 않기 때문입니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1734843291042&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;template&amp;lt;typename... ARGS&amp;gt; std::string
concat_with_delimiter(const std::string &amp;amp;delim, const ARGS &amp;amp;... args)
{
  std::stringstream stream;

  (concat_with_delimiter_helper(args, delim, stream), ...); ///&amp;lt; 폴드표현식

  std::string str = stream.str();
  if (str.empty() == true)
    return str;

  if (delim.empty() == false)
    if (str.length() &amp;lt; delim.length())
      return str;

  return str.substr(0, str.length()-delim.length());
}&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;이 세 번째 함수는 특별합니다. C++17에서 도입된 폴드 표현식을 사용했습니다.&lt;/span&gt;&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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;폴드 표현식&lt;/span&gt;&lt;/blockquote&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; --darkreader-inline-color: var(--darkreader-text-333333, #c8c3bc);&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;폴드 표현식은 가변 개수의 인자들에 대해 동일한 연산을 반복 적용하는 것을 간단하게 표현하는 방법입니다. 예를 들어 여러 숫자들을 모두 더하거나, 여러 문자열을 모두 연결하거나, 여러 작업을 순차적으로 실행하는 것 같은 상황에서 사용합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start; --darkreader-inline-color: var(--darkreader-text-333333, #c8c3bc);&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start; --darkreader-inline-color: var(--darkreader-text-333333, #c8c3bc);&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;우리가 흔히 쓰는 &quot;+&quot;, &quot;,&quot; 같은 이항 연산자를 펼쳐서(fold) 여러 인자들 사이에 자동으로 넣어주는 거죠. 마치 종이를 접었다 펼치듯이 하나의 표현식이 여러 번 반복되어 펼쳐지는 것을 표현한 용어입니다. 이게 없었을 때는 재귀나 다른 복잡한 방식으로 구현해야 했습니다. 이를 C++17에서는 훨씬 단순하고 직관적으로 표현할 수 있게 되었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1734869452990&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;template&amp;lt;typename... Args&amp;gt;
auto sum(Args... args) { return (... + args); }

sum(1, 2, 3, 4);

-&amp;gt; ((1 + 2) + 3) + 4&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;C++11: concat_with_delimiter&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;이거 만들 때는 진짜 공부 많이 했습니다. 근데 까먹었습니다. 그래서 설명은 생략합니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1734843510123&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;template&amp;lt;typename ARG&amp;gt; void
concat_with_delimiter_helper(std::stringstream &amp;amp;stream, const ARG &amp;amp;arg)
{
  stream &amp;lt;&amp;lt; arg;
}

template&amp;lt;&amp;gt; inline void //&amp;lt; 명시적 특수화(explicit specialization)의 경우에는 자동 inline이 적용되지 않습니다
concat_with_delimiter_helper(std::stringstream &amp;amp;stream, const bool &amp;amp;arg)
{
  if (arg == true) stream &amp;lt;&amp;lt; &quot;true&quot;;
  else stream &amp;lt;&amp;lt; &quot;false&quot;;
}

template&amp;lt;typename ARG&amp;gt; void
concat_with_delimiter(size_t &amp;amp;index, std::stringstream &amp;amp;stream, const std::string &amp;amp;delim, const ARG &amp;amp;arg)
{
  if (index &amp;gt; 0) stream &amp;lt;&amp;lt; delim;
  concat_with_delimiter_stream(stream, arg);
}

template&amp;lt;typename FIRST, typename... ARGS&amp;gt; void
concat_with_delimiter(size_t &amp;amp;index, std::stringstream &amp;amp;stream, const std::string &amp;amp;delim, const FIRST &amp;amp;first, const ARGS &amp;amp;... args)
{
  if (index &amp;gt; 0) stream &amp;lt;&amp;lt; delim;
  concat_with_delimiter_stream(stream, first);
  concat_with_delimiter(++index, stream, delim, args...);
}

template&amp;lt;typename... ARGS&amp;gt; std::string
concat_with_delimiter(const std::string &amp;amp;delim, const ARGS &amp;amp;... args)
{
  std::stringstream stream;
  size_t index = (size_t)-1;
  concat_with_delimiter(++index, stream, delim, args...);
  return stream.str();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-ke-size=&quot;size23&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;C++17 &amp;amp; C++11 concat_with_delimiter&lt;/span&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;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1734844308022&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#if __cplusplus &amp;gt;= 201703L
// C++17 이상인 경우
template&amp;lt;typename T&amp;gt; std::stringstream &amp;amp;
concat_with_delimiter_helper(const T &amp;amp;t, const std::string &amp;amp;delim, std::stringstream &amp;amp;stream)
{
  stream &amp;lt;&amp;lt; t &amp;lt;&amp;lt; delim;
  return stream;
}

template&amp;lt;&amp;gt; inline std::stringstream &amp;amp;
concat_with_delimiter_helper(bool t, const std::string &amp;amp;delim, std::stringstream &amp;amp;stream)
{
  stream &amp;lt;&amp;lt; (t ? &quot;true&quot; : &quot;false&quot;) &amp;lt;&amp;lt; delim;
  return stream;
}

template&amp;lt;typename... ARGS&amp;gt; std::string
concat_with_delimiter(const std::string &amp;amp;delim, const ARGS &amp;amp;... args)
{
  std::stringstream stream;

  (concat_with_delimiter_helper(args, delim, stream), ...); ///&amp;lt; 폴드표현식

  std::string str = stream.str();
  if (str.empty() == true)
    return str;

  if (delim.empty() == false)
    if (str.length() &amp;lt; delim.length())
      return str;

  return str.substr(0, str.length()-delim.length());
}

#else
// C++17 미만인 경우
template&amp;lt;typename ARG&amp;gt; void
concat_with_delimiter_helper(std::stringstream &amp;amp;stream, const ARG &amp;amp;arg)
{
  stream &amp;lt;&amp;lt; arg;
}

template&amp;lt;&amp;gt; inline void
concat_with_delimiter_helper(std::stringstream &amp;amp;stream, const bool &amp;amp;arg)
{
  if (arg == true) stream &amp;lt;&amp;lt; &quot;true&quot;;
  else stream &amp;lt;&amp;lt; &quot;false&quot;;
}

template&amp;lt;typename ARG&amp;gt; void
concat_with_delimiter(size_t &amp;amp;index, std::stringstream &amp;amp;stream, const std::string &amp;amp;delim, const ARG &amp;amp;arg)
{
  if (index &amp;gt; 0) stream &amp;lt;&amp;lt; delim;
  concat_with_delimiter_helper(stream, arg);
}

template&amp;lt;typename FIRST, typename... ARGS&amp;gt; void
concat_with_delimiter(size_t &amp;amp;index, std::stringstream &amp;amp;stream, const std::string &amp;amp;delim, const FIRST &amp;amp;first, const ARGS &amp;amp;... args)
{
  if (index &amp;gt; 0) stream &amp;lt;&amp;lt; delim;
  concat_with_delimiter_helper(stream, first);
  concat_with_delimiter(++index, stream, delim, args...);
}

template&amp;lt;typename... ARGS&amp;gt; std::string
concat_with_delimiter(const std::string &amp;amp;delim, const ARGS &amp;amp;... args)
{
  std::stringstream stream;
  size_t index = (size_t)-1;
  concat_with_delimiter(++index, stream, delim, args...);
  return stream.str();
}
#endif&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-ke-size=&quot;size23&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;결론&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&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;있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <author>tys</author>
      <guid isPermaLink="true">https://cplusplus.tistory.com/9</guid>
      <comments>https://cplusplus.tistory.com/entry/%EA%B3%A0%EC%9D%B8%EB%AC%BC%EC%9D%80-%EC%8B%AB%EC%96%B4%ED%95%98%EB%8A%94-C-%ED%85%9C%ED%94%8C%EB%A6%BF-%ED%99%9C%EC%9A%A9-concatwithdelimiter#entry9comment</comments>
      <pubDate>Sun, 22 Dec 2024 14:04:26 +0900</pubDate>
    </item>
    <item>
      <title>std::thread의 실전적 랩핑 - thread 클래스 구현</title>
      <link>https://cplusplus.tistory.com/entry/stdthread%EC%9D%98-%EC%8B%A4%EC%A0%84%EC%A0%81-%EB%9E%A9%ED%95%91-thread-%ED%81%B4%EB%9E%98%EC%8A%A4-%EA%B5%AC%ED%98%84</link>
      <description>&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;std::thread의&amp;nbsp;실전적&amp;nbsp;랩핑&amp;nbsp;-&amp;nbsp;thread&amp;nbsp;클래스&amp;nbsp;구현&lt;/span&gt;&lt;br /&gt;&lt;a style=&quot;background-color: #e6f5ff; color: #0070d1; text-align: start;&quot; href=&quot;https://cplusplus.tistory.com/entry/stdconditionvariable%EC%9D%98-%EC%95%88%EC%A0%84%ED%95%9C-%EC%8B%9C%EA%B7%B8%EB%84%90%EB%A7%81&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;std::condition_variable의&amp;nbsp;안전한&amp;nbsp;시그널링&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start; --darkreader-inline-color: #e8e6e3;&quot; data-ke-size=&quot;size26&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;들어가며&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start; --darkreader-inline-color: #c8c3bc;&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;C++11에서 std::thread가 도입된 이후, 많은 개발자들이 멀티쓰레딩 프로그래밍을 더 쉽게 할 수 있게 되었습니다. 하지만 실제 프로덕션 환경에서는 std::thread를 직접 사용하는 것보다는 적절히 랩핑 하여 사용하는 것이 더 안전하고 유지보수하기 좋습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start; --darkreader-inline-color: #c8c3bc;&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start; --darkreader-inline-color: #c8c3bc;&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;이 글에서는 실전에서 사용할 수 있는 쓰레드 래퍼 클래스를 처음부터 구현해 보면서, 각 단계에서의 설계 결정과 그 이유를 살펴보겠습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start; --darkreader-inline-color: #c8c3bc;&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/p&gt;

            &lt;figure class=&quot;unsupported component-kakaotv&quot; contenteditable=&quot;false&quot; style=&quot;background:#000;margin:16px 0;min-height:72px;padding:10px 16px;display:flex;align-items:center;justify-content:center;text-align:center;box-sizing:border-box;width:100%;max-width:100%;&quot;&gt;
                &lt;p contenteditable=&quot;false&quot; style=&quot;margin:0;color:#8a8a8a;font-size:13px;line-height:1.6;user-select:none;pointer-events:none;&quot;&gt;동영상 서비스가 종료되어 해당 콘텐츠를 재생할 수 없습니다.&lt;/p&gt;
            &lt;/figure&gt;
        
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start; --darkreader-inline-color: #e8e6e3;&quot; data-ke-size=&quot;size26&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;std::thread 사용 시의 실제 문제점들&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start; --darkreader-inline-color: #c8c3bc;&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;실제 프로젝트에서 std::thread를 직접 사용하면서 마주치는 주요 문제들이 있습니다:&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start; --darkreader-inline-color: #c8c3bc;&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start; --darkreader-inline-color: #e8e6e3;&quot; data-ke-size=&quot;size20&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;쓰레드 시작/종료 시점 관리의 어려움&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1734704330374&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;std::thread t([]{
    // 작업 수행
});  
// 이 시점에 쓰레드가 실제로 시작됐나?&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;쓰레드가 생성되었다고 해서 바로 실행되는 것이 아닙니다. OS의 스케줄링에 따라 실제 실행 시점은 달라질 수 있죠. 시작 시점을 정확히 알기 위해선 별도의 동기화 작업이 필요합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start; --darkreader-inline-color: #e8e6e3;&quot; data-ke-size=&quot;size20&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;예외 처리의 까다로움&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1734704394780&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;try {
    std::thread t([]{
        throw std::runtime_error(&quot;error&quot;);  // 이 예외는?
    });
} catch (...) {
    // 여기서 캐치되지 않습니다
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;쓰레드 내부에서 발생한 예외는 메인 쓰레드로 전파되지 않습니다. 이를 처리하기 위해서는 별도의 예외 처리 메커니즘이 필요합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start; --darkreader-inline-color: #e8e6e3;&quot; data-ke-size=&quot;size20&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;쓰레드 간 통신의 복잡성&lt;/span&gt;&lt;/h4&gt;
&lt;div style=&quot;color: #333333; text-align: start; --darkreader-inline-color: #c8c3bc;&quot; data-darkreader-inline-color=&quot;&quot;&gt;
&lt;div&gt;
&lt;div style=&quot;background-color: #282c34; color: #abb2bf; text-align: left; --darkreader-inline-bgcolor: #20232a; --darkreader-inline-color: #b9b3aa;&quot; data-darkreader-inline-bgcolor=&quot;&quot; data-darkreader-inline-color=&quot;&quot;&gt;
&lt;pre id=&quot;code_1734704408859&quot; class=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;cpp&quot;&gt;&lt;code&gt;std::mutex mtx;
std::condition_variable cv;
bool ready = false;

// 이런 코드가 프로젝트 전반에 반복됩니다
{
    std::unique_lock&amp;lt;std::mutex&amp;gt; lock(mtx);
    cv.wait(lock, [&amp;amp;]{ return ready; });
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&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; --darkreader-inline-color: #e8e6e3;&quot; data-ke-size=&quot;size20&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;보일러플레이트 코드&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;그 외에 std::thread를 그대로 사용하는 경우 매 구현마다 오류 처리나 시작, 종료의 동기화, 생명주기 관리등을 각각 구현해야 합니다. 이는 &lt;span style=&quot;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;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start; --darkreader-inline-color: #e8e6e3;&quot; data-ke-size=&quot;size26&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;std::thread 래퍼 클래스 구현&lt;/span&gt;&lt;/h2&gt;
&lt;h4 style=&quot;color: #000000; text-align: start; --darkreader-inline-color: #e8e6e3;&quot; data-ke-size=&quot;size20&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;인터페이스 정의하기&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;설계할때 제가 이용하는 방법 중 하나는 어떻게 사용할지 먼저 그려보는 겁니다. &lt;/span&gt;&lt;span style=&quot;text-align: start;&quot;&gt;이게 요구사항이 되는 거겠죠?&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;text-align: start;&quot;&gt;저는 아래와 같이 구현할 예정입니다. start, stop의 쌍을 이루어야겠지만, 여기서는 std::thread의 join 패턴을 따라 구현하고자 합니다. 기존 쓰레드 인터페이스의 의미론(semantics)을 자연스럽게 반영하려는 의도입니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1734704488917&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 인터페이스 클래스를 상속받아 사용하려는 클래스 구현
class MyThread : public MThread
{
protected:
  void run()
  {
    for (size_t index = 0; index &amp;lt; 3; ++index)
    {
      std::cout &amp;lt;&amp;lt; &quot;run &quot; &amp;lt;&amp;lt; index &amp;lt;&amp;lt; std::endl;
      std::this_thread::sleep_for(std::chrono::seconds(1));
    }
  }
};

MyThread thread; ///&amp;lt; 인스턴스 생성

thread.start(); ///&amp;lt; 쓰레드 기동
thread.join();  ///&amp;lt; 쓰레드 종료시까지 대기&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&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;&lt;/span&gt;&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;&lt;span style=&quot;color: #666666;&quot; data-darkreader-inline-color=&quot;&quot;&gt;클래스 핵심 원칙&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #666666;&quot; data-darkreader-inline-color=&quot;&quot;&gt;클래스 관리의 핵심은 관련된 상태(데이터)와 행위(동작)를 하나의 응집된 단위로 격리시키는 것입니다. 이는 단순한 코드 구조화 하는 것 이상의 개념으로, 다음과 같은 이점을 제공합니다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #666666;&quot; data-darkreader-inline-color=&quot;&quot;&gt;첫째, 상태와 행위가 동일한 공간 내에 존재함으로써 데이터의 일관성과 무결성이 자연스럽게 보장됩니다. 외부에서 쓰레드의 내부 상태를 직접 조작할 수 없으므로, 예측 가능하고 안전한 동작이 보장됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #666666;&quot; data-darkreader-inline-color=&quot;&quot;&gt;둘째, 명확한 책임 경계를 설정함으로써 코드의 가독성과 유지보수성이 크게 향상됩니다. 각 클래스는 자신의 고유한 책임만을 수행하며, 이는 단일 책임 원칙(Single Responsibility Principle)을 따르게 됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #666666;&quot; data-darkreader-inline-color=&quot;&quot;&gt;마지막으로, 이러한 설계는 시스템의 모듈성을 높여 변경과 확장에 유연하게 대응할 수 있게 합니다. 쓰레드 관련 로직을 수정해야 할 때, 해당 클래스만 집중적으로 살펴보면 되므로 시스템 전반에 미치는 영향을 최소화할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #666666;&quot; data-darkreader-inline-color=&quot;&quot;&gt;이러한 접근 방식은 견고하고 유지보수 가능한 설계하는 핵심 원칙이 됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start; --darkreader-inline-color: #e8e6e3;&quot; data-ke-size=&quot;size20&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;std::thread detach&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;여기서 detach 인터페이스는 배제합니다. 실제 프로덕션 환경에서 detach 사용 사례와 그로 인한 문제점이 빈번했기 때문입니다. detach 사용 시 발생하는 주요 기술적 부채는 다음과 같습니다.&lt;/span&gt;&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;쓰레드가 가진 리소스 수명관리가 어려워집니다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;실행상태나 종료여부를 명시적으로 알기 어렵습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;프로세스가 종료될 때 detach 된 쓰레드가 아직 실행 중이라면 예측되지 않은 강제 종료를 하게됩니다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;위와 같은 문제로 join보다 설계가 어려워집니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;특히 detach된 쓰레드가 소멸된 이후에도 그 쓰레드의 리소스에 접근을 시도할 수 있어 위험하며 강제 종료될 때는 데이터 손실이 발생할 수 있습니다. 여러분은 detach를 사용하여 실행된 프로세스가 종료될 때 종종 segmentation fault가 발생하는 모습을 볼 수 있었을 겁니다.&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;color: #000000; text-align: start; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;최소한 쓰레드 한정해서 detach의 &quot;일단 던지고 잊어버리기(fire and forget)&quot; 전략은 설계에 있어 치명적 위험요소입니다.&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;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;C++ 창시자 Bjarne Stroustrup과 &lt;span style=&quot;text-align: start; --darkreader-inline-color: #cdc8c2;&quot;&gt;ISO C++ 표준 위원회 의장&lt;/span&gt; Herb Sutter가 주도하는 C++ 핵심 가이드라인에서는 thread의 detach()를 사용하지 말 것(&quot;Don&amp;rsquo;t&lt;span style=&quot;text-align: start;&quot;&gt; detach() a thread&quot;)&lt;/span&gt;을 강하게 말하고 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1734763329400&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;C++ Core Guidelines&quot; data-og-description=&quot;&quot; data-og-host=&quot;isocpp.github.io&quot; data-og-source-url=&quot;https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rconc-detached_thread&quot; data-og-url=&quot;https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rconc-detached_thread&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/nLnXn/hyXOqUiwIX/jmzycivaaV2hl6bRAxYxf0/img.png?width=1412&amp;amp;height=619&amp;amp;face=0_0_1412_619,https://scrap.kakaocdn.net/dn/ccDfMw/hyXOrr8Hzs/AtXrLskdmK2LazadBOMQL0/img.png?width=1412&amp;amp;height=604&amp;amp;face=0_0_1412_604&quot;&gt;&lt;a href=&quot;https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rconc-detached_thread&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rconc-detached_thread&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/nLnXn/hyXOqUiwIX/jmzycivaaV2hl6bRAxYxf0/img.png?width=1412&amp;amp;height=619&amp;amp;face=0_0_1412_619,https://scrap.kakaocdn.net/dn/ccDfMw/hyXOrr8Hzs/AtXrLskdmK2LazadBOMQL0/img.png?width=1412&amp;amp;height=604&amp;amp;face=0_0_1412_604');&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;C++ Core Guidelines&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;isocpp.github.io&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;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;한국의 C++ 코드에서는 detach를 빈번하게 찾아볼 수 있습니다. 논리적 실체가 뚜렷한 소프트웨어에 있어서 이런 관행은 지양해야 할 요소입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start; --darkreader-inline-color: #c8c3bc;&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start; --darkreader-inline-color: #e8e6e3;&quot; data-ke-size=&quot;size20&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;기본적인 쓰레드 클래스&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start; --darkreader-inline-color: #c8c3bc;&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;아래와 같은 인터페이스로 우선 기본적인 사용자 인터페이스를 만들겠습니다. 자바 스타일을 가져왔습니다. :)&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1734704563597&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class MThread
{
public:
  MThread();
  virtual ~MThread();

protected:
  /// @brief 상속받아 구현하여야할 가상함수. 쓰레드가 시작되면 최초로 불려지는 함수
  virtual void run() = 0;

protected:
  void execute();

protected:
  std::shared_ptr&amp;lt;std::thread&amp;gt; thread_;

private:
  bool run_ = false;
  std::string err_str_;
};&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start; --darkreader-inline-color: #c8c3bc;&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;shared_ptr을 사용하여 쓰레드 객체의 수명을 자동으로 관리하고, 실행 상태를 추적합니다.&lt;/span&gt; 여기서 executor는 저수준의 std::thread를 객체지향적인 클래스로 랩핑 하여, 더 높은 수준의 추상화를 제공하는 핵심 함수입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start; --darkreader-inline-color: #c8c3bc;&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start; --darkreader-inline-color: #e8e6e3;&quot; data-ke-size=&quot;size20&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;시작, 종료 동기화&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start; --darkreader-inline-color: #c8c3bc;&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;쓰레드의 시작과 종료 시점을 정확히 제어하기 위한 동기화 메커니즘을 추가해야 합니다. 동기화를 위해 std::condition_ variable를 사용할 텐데요, 가짜깨움, 시그널 놓침 문제가 있기 때문에 std::condition_variable를 랩핑하여 해당 문제를 해결한 MSignal을 사용할 겁니다. MSignal을 사용해서 동기화된 start와 join을 추가합니다. error도 추가합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start; --darkreader-inline-color: #c8c3bc;&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1746499160476&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;std::condition_variable의 안전한 시그널링&quot; data-og-description=&quot;들어가며쓰레드 간 동기화와 시그널링은 멀티쓰레드 프로그래밍에서 가장 기본적이면서도 까다로운 문제 중 하나입니다. 특히 생산자-소비자 패턴, 작업 큐, 이벤트 처리 등의 상황에서 쓰레드&quot; data-og-host=&quot;cplusplus.tistory.com&quot; data-og-source-url=&quot;https://cplusplus.tistory.com/entry/stdconditionvariable%EC%9D%98-%EC%95%88%EC%A0%84%ED%95%9C-%EC%8B%9C%EA%B7%B8%EB%84%90%EB%A7%81&quot; data-og-url=&quot;https://cplusplus.tistory.com/entry/stdconditionvariable%EC%9D%98-%EC%95%88%EC%A0%84%ED%95%9C-%EC%8B%9C%EA%B7%B8%EB%84%90%EB%A7%81&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bEWC4X/hyYM2Zupnj/CBXrteBcIwk4xak73fYcAK/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/ba7rn7/hyYRsPHLUH/Mz71Y63KjpkaoKlixk2ys0/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800&quot;&gt;&lt;a href=&quot;https://cplusplus.tistory.com/entry/stdconditionvariable%EC%9D%98-%EC%95%88%EC%A0%84%ED%95%9C-%EC%8B%9C%EA%B7%B8%EB%84%90%EB%A7%81&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://cplusplus.tistory.com/entry/stdconditionvariable%EC%9D%98-%EC%95%88%EC%A0%84%ED%95%9C-%EC%8B%9C%EA%B7%B8%EB%84%90%EB%A7%81&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bEWC4X/hyYM2Zupnj/CBXrteBcIwk4xak73fYcAK/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/ba7rn7/hyYRsPHLUH/Mz71Y63KjpkaoKlixk2ys0/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800');&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;std::condition_variable의 안전한 시그널링&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;들어가며쓰레드 간 동기화와 시그널링은 멀티쓰레드 프로그래밍에서 가장 기본적이면서도 까다로운 문제 중 하나입니다. 특히 생산자-소비자 패턴, 작업 큐, 이벤트 처리 등의 상황에서 쓰레드&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;cplusplus.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;pre id=&quot;code_1734704743263&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class MThread
{
public:
  MThread();

  virtual ~MThread();

  virtual bool start(); ///&amp;lt; 쓰레드 시작

  virtual bool join(); ///&amp;lt; 쓰레드 종료 대기

  virtual std::string error() const; ///&amp;lt; 실패시 오류 출력

protected:
  /// 상속받아 구현할 가상함수. 쓰레드가 시작되면 최초로 불려지는 함수
  virtual void run() = 0;

protected:
  void execute();

protected:
  std::shared_ptr&amp;lt;std::thread&amp;gt; thread_;

private:
  bool        run_ = false;
  MSignal     run_signal_; ///&amp;lt; 동기화에 사용
  std::string err_str_;
};&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;전체 인터페이스와 세부항목까지 완성되었습니다. 이제는 구현할 차례입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start; --darkreader-inline-color: #e8e6e3;&quot; data-ke-size=&quot;size20&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;구현&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;간단한 함수들은 inline처리하여 MThread.h에 구현하였으며, static함수와 start함수는 MThread.cpp에 구현하였습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1734705472072&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// MThread.h
inline
MThread::MThread() : run_(false) {}

inline
MThread::~MThread() {}

inline std::string
MThread::error() const
{
  return err_str_;
}

inline bool
MThread::join()
{
  // executor로 실행된 쓰레드 함수가 종료되기를 기다립니다.
  run_signal_.wait([&amp;amp;]() { return run_ == false; });
  thread_-&amp;gt;join();
  return true;
}

// MThread.cpp
// 이 함수는 헤더에 static으로 선언된 함수입니다.
// 이 함수를 std::thread에서 호출하기 쉽도록 static으로 선언했던 것입니다.
void
MThread::execute()
{
  // MThread::run을 호출하기 전과 후에 run_플래그를 설정합니다. 
  // MSignal은 lock를 사용하여 run_변경을 안전하게 처리하며 signal을 보내 줍니다.
  run_signal_.notify_one([&amp;amp;]() { thread-&amp;gt;run_ = true; } );
  run();
  run_signal_.notify_one([&amp;amp;]() { thread-&amp;gt;run_ = false; } );
}

bool
MThread::start()
{
  // 각종 예외처리를 해줍니다.
  try
  {
    err_str_.clear();
    // 쓰레드를 구동시킵니다.
    thread_ = std::make_shared&amp;lt;std::thread&amp;gt;(&amp;amp;MThread::executor, this);
  }
  catch (const std::bad_alloc&amp;amp; e)
  {
    std::cerr &amp;lt;&amp;lt; &quot;Memory allocation failed: &quot; &amp;lt;&amp;lt; e.what() &amp;lt;&amp;lt; std::endl;
    err_str_ = e.what();
    return false;
  }
  catch (const std::invalid_argument&amp;amp; e)
  {
    std::cerr &amp;lt;&amp;lt; &quot;Invalid argument exception: &quot; &amp;lt;&amp;lt; e.what() &amp;lt;&amp;lt; std::endl;
    err_str_ = e.what();
    return false;
  }
  catch (const std::exception&amp;amp; e)
  {
    std::cerr &amp;lt;&amp;lt; &quot;Exception occurred: &quot; &amp;lt;&amp;lt; e.what() &amp;lt;&amp;lt; std::endl;
    err_str_ = e.what();
    return false;
  }

  if (thread_ == nullptr)
  {
    err_str_ = &quot;Unknown error: thread_ is nullptr&quot;;
    return false;
  }

  // 리턴값이 true면 wait에서 빠져나옵니다.
  // 리턴값이 false면 락을 풀고 wait 상태로 진입
  run_signal_.wait([&amp;amp;]() { return run_; });

  return true;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start; --darkreader-inline-color: #e8e6e3;&quot; data-ke-size=&quot;size26&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;사용 예시&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;아래와 같이 구현할 수 있습니다. 사용자는 자신의 쓰레드에 stop()을 구현하여 명시적 종료를 쓰레드에게 요청할 수도 있을겁니다. 이를테면 종료전 잔업처리를 하고 종료하는 우아한 종료가 될 수 있습니다.&lt;/span&gt;&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;pre id=&quot;code_1734744269245&quot; class=&quot;cpp&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start; --darkreader-inline-bgcolor: #1c1e1f; --darkreader-inline-color: #c1bcb4;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;cpp&quot; data-darkreader-inline-bgcolor=&quot;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;thread&amp;gt;
#include &amp;lt;chrono&amp;gt;
#include &amp;lt;memory&amp;gt;

class Thread1 : public MThread 
{
public:
   struct Data 
   {
       int id = 1;
       std::string message = &quot;T1 Message&quot;;
       std::vector&amp;lt;int&amp;gt; numbers = {1, 2, 3};
   };
   virtual ~Thread1() {}
   
protected:
   void run() override 
   {
       std::cout &amp;lt;&amp;lt; &quot;Processing T1: &quot; &amp;lt;&amp;lt; data_.message &amp;lt;&amp;lt; &quot;\n&quot;;
       for(auto&amp;amp; num : data_.numbers) num *= 2;
       std::this_thread::sleep_for(std::chrono::seconds(1));
   }

private:
   Data data_;
};

class Thread2 : public MThread 
{
public:
   struct Data 
   {
       std::string name = &quot;T2&quot;;
       double value = 42.0;
   };
   virtual ~Thread2() {}

protected:
   void run() override 
   {
       std::cout &amp;lt;&amp;lt; &quot;Processing T2: &quot; &amp;lt;&amp;lt; data_.name &amp;lt;&amp;lt; &quot;\n&quot;;
       data_.value *= 2;
       std::this_thread::sleep_for(std::chrono::seconds(1));
   }

private:
   Data data_;
};

int main() 
{
   Thread1 t1(data1);
   Thread2 t2(data2);
   
   t1.start();
   t2.start();
   t1.join();
   t2.join();
   
   return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2 style=&quot;color: #000000; text-align: start; --darkreader-inline-color: #e8e6e3;&quot; data-ke-size=&quot;size26&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;br /&gt;결론&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start; --darkreader-inline-color: #c8c3bc;&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;쓰레드의 생명주기 관리&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;예외를 안전하게 처리&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;쓰레드 간 통신을 단순화&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;코드 재사용성 향상&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start; --darkreader-inline-color: #c8c3bc;&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;프로덕션 환경에서는 이보다 더 많은 기능(예: 쓰레드 풀, 작업 큐 등)이 필요할 수 있지만, 이 기본 구현을 확장하여 필요한 기능을 추가할 수 있습니다. 가장 중요한 것은 단일책임하에 안전성과 사용성을 확보하는 것입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start; --darkreader-inline-color: #c8c3bc;&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start; --darkreader-inline-color: #c8c3bc;&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;※&amp;nbsp;&lt;/span&gt;본 블로그의 소스코드는 자유롭게 사용하셔도 되나, 사용 시 발생하는 모든 문제에 대한 책임은 전적으로 사용자에게 있습니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;div id=&quot;gtx-trans&quot; style=&quot;position: absolute; left: 693px; top: 4564.86px;&quot;&gt;
&lt;div class=&quot;gtx-trans-icon&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>C++</category>
      <category>std::thread</category>
      <category>thread</category>
      <author>tys</author>
      <guid isPermaLink="true">https://cplusplus.tistory.com/8</guid>
      <comments>https://cplusplus.tistory.com/entry/stdthread%EC%9D%98-%EC%8B%A4%EC%A0%84%EC%A0%81-%EB%9E%A9%ED%95%91-thread-%ED%81%B4%EB%9E%98%EC%8A%A4-%EA%B5%AC%ED%98%84#entry8comment</comments>
      <pubDate>Fri, 20 Dec 2024 23:49:03 +0900</pubDate>
    </item>
    <item>
      <title>std::condition_variable의 안전한 시그널링</title>
      <link>https://cplusplus.tistory.com/entry/stdconditionvariable%EC%9D%98-%EC%95%88%EC%A0%84%ED%95%9C-%EC%8B%9C%EA%B7%B8%EB%84%90%EB%A7%81</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;std::condition_variable의&amp;nbsp;안전한&amp;nbsp;시그널링&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://cplusplus.tistory.com/entry/stdthread%EC%9D%98-%EC%8B%A4%EC%A0%84%EC%A0%81-%EB%9E%A9%ED%95%91-thread-%ED%81%B4%EB%9E%98%EC%8A%A4-%EA%B5%AC%ED%98%84&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;std::thread의&amp;nbsp;실전적&amp;nbsp;랩핑&amp;nbsp;-&amp;nbsp;thread&amp;nbsp;클래스&amp;nbsp;구현&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&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;size18&quot;&gt;&lt;a href=&quot;https://cplusplus.tistory.com/entry/C17%EB%A1%9C-%EC%A7%84%ED%99%94%ED%95%98%EA%B8%B0-stdconditionvariable-stdthread-%EB%9E%A9%ED%95%91&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;C++17로&amp;nbsp;진화하기:&amp;nbsp;std::condition_variable 안전한 랩핑&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1766925684109&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;C++17로 진화하기: std::condition_variable의 안전한 랩핑&quot; data-og-description=&quot;이전에 아래 내용을 C++11로 랩핑 하였는데요, std::condition_variable의 안전한 시그널링앞서 소개한 위 코드를 C++17로 다시 랩핑 하려 합니다. 특히 std::condition_variable에서의 C++11의 복잡했던 템플릿식&quot; data-og-host=&quot;cplusplus.tistory.com&quot; data-og-source-url=&quot;https://cplusplus.tistory.com/entry/C17%EB%A1%9C-%EC%A7%84%ED%99%94%ED%95%98%EA%B8%B0-stdconditionvariable-stdthread-%EB%9E%A9%ED%95%91&quot; data-og-url=&quot;https://cplusplus.tistory.com/entry/C17%EB%A1%9C-%EC%A7%84%ED%99%94%ED%95%98%EA%B8%B0-stdconditionvariable-stdthread-%EB%9E%A9%ED%95%91&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/382Ae/hyZQF2tQQW/63Wgx8jQ0PBInqAdLsbzrK/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/blPU1Q/hyZPKDddzf/OeVmZl2XPGp5ahWo2TaZrk/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800&quot;&gt;&lt;a href=&quot;https://cplusplus.tistory.com/entry/C17%EB%A1%9C-%EC%A7%84%ED%99%94%ED%95%98%EA%B8%B0-stdconditionvariable-stdthread-%EB%9E%A9%ED%95%91&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://cplusplus.tistory.com/entry/C17%EB%A1%9C-%EC%A7%84%ED%99%94%ED%95%98%EA%B8%B0-stdconditionvariable-stdthread-%EB%9E%A9%ED%95%91&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/382Ae/hyZQF2tQQW/63Wgx8jQ0PBInqAdLsbzrK/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/blPU1Q/hyZPKDddzf/OeVmZl2XPGp5ahWo2TaZrk/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800');&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;C++17로 진화하기: std::condition_variable의 안전한 랩핑&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;이전에 아래 내용을 C++11로 랩핑 하였는데요, std::condition_variable의 안전한 시그널링앞서 소개한 위 코드를 C++17로 다시 랩핑 하려 합니다. 특히 std::condition_variable에서의 C++11의 복잡했던 템플릿식&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;cplusplus.tistory.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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[[아래 글은 C++11 기준]]&lt;/p&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: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;Modern C++에서는 std::condition_variable 클래스를 통해 쓰레드 간 시그널링과 동기화를 위한 메커니즘을 제공합니다. 여기서는 lost wakeup과 spurious wakeup과 같은 일반적인 문제를 피하면서 안정적인 시그널 처리 클래스 구현을 목표로 합니다.&lt;/span&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;std::&lt;span style=&quot;text-align: start;&quot;&gt;condition variable&lt;/span&gt;&amp;nbsp;처리의 문제점&lt;/span&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;시그널 놓침&lt;/span&gt;(&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;Lost Wakeup&lt;/span&gt;)&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;이 문제는 수신자가 wait로 진입하기 전에 시그널이 전송되면 발생하는 문제 입니다. 이런 경우 시그널이 손실되어 수신자가 계속해서 대기할 수 있습니다. 특히 타이밍이 중요한 부분에서는 문제가 될 수 있습니다.&amp;nbsp;&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;가짜 깨움(Spurious&lt;/span&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt; Wakeup&lt;/span&gt;)&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;가짜 깨움은 아무 이유 없이 condition_variable에서 대기 중인 쓰레드들이 깨어나는 현상입니다. 이는 OS 레벨의 최적화로 인해 발생하며, POSIX 시스템을 포함한 대부분의 운영체제에서 나타날 수 있습니다. 이런 가짜 깨움은 추적이 어려운 버그를 발생시킬 수도 있기 때문에 이에 대한 예외 상황 처리를 해주어야 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1748429181767&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;Condition Variable Spurious Wakes | Just Software Solutions - Custom Software Development&quot; data-og-description=&quot;Condition variables are a useful mechanism for waiting until an event occurs or some &amp;quot;condition&amp;quot; is satisfied. For example, in my implementation of a thread-safe queue I use a condition variable to avoid busy-waiting in wait_and_pop() when the queue is emp&quot; data-og-host=&quot;www.justsoftwaresolutions.co.uk&quot; data-og-source-url=&quot;https://www.justsoftwaresolutions.co.uk/threading/condition-variable-spurious-wakes.html&quot; data-og-url=&quot;https://www.justsoftwaresolutions.co.uk/threading/condition-variable-spurious-wakes.html&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://www.justsoftwaresolutions.co.uk/threading/condition-variable-spurious-wakes.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.justsoftwaresolutions.co.uk/threading/condition-variable-spurious-wakes.html&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&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;Condition Variable Spurious Wakes | Just Software Solutions - Custom Software Development&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Condition variables are a useful mechanism for waiting until an event occurs or some &quot;condition&quot; is satisfied. For example, in my implementation of a thread-safe queue I use a condition variable to avoid busy-waiting in wait_and_pop() when the queue is emp&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.justsoftwaresolutions.co.uk&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;figure id=&quot;og_1748430393611&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;futex(2) - Linux manual page&quot; data-og-description=&quot;futex(2) &amp;mdash; Linux manual page futex(2) System Calls Manual futex(2) NAME &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; top futex - fast user-space locking LIBRARY &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; top Standard C library (libc, -lc) SYNOPSIS &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; top #include /* Definition of FUTEX_* constants */ #includ&quot; data-og-host=&quot;www.man7.org&quot; data-og-source-url=&quot;https://www.man7.org/linux/man-pages/man2/futex.2.html&quot; data-og-url=&quot;https://www.man7.org/linux/man-pages/man2/futex.2.html&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://www.man7.org/linux/man-pages/man2/futex.2.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.man7.org/linux/man-pages/man2/futex.2.html&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&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;futex(2) - Linux manual page&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;futex(2) &amp;mdash; Linux manual page futex(2) System Calls Manual futex(2) NAME &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; top futex - fast user-space locking LIBRARY &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; top Standard C library (libc, -lc) SYNOPSIS &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; top #include /* Definition of FUTEX_* constants */ #includ&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.man7.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p style=&quot;text-align: left;&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;condition_variable을 직접 사용하여 이러한 문제들을 해결하려면 중복 코드가 발생하기 쉽고, 다른 코드들과 뒤섞였 있을 때 정확한 동작을 보장하기 어려울 수 있습니다. &lt;span style=&quot;text-align: start;&quot;&gt;따라서 Signal의 클래스 구현은 이런 문제들을 Signal 클래스 안에서 해결하고 밖으로 노출시키지 않는 데 있습니다.&lt;/span&gt;&lt;/span&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;Signal 인터페이스&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;Signal 클래스는 std::condition_variable을 랩핑 하여 위의 문제들을 안전하게 처리하면서 std::condition_variable의 기능을 흡사하게 모방하는데 목적이 있습니다. 아래 코드는 C++11을 기준으로 작성되었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1734673708613&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 기본 wait - 무한 대기
signal.wait();

// timeout이 있는 wait - 1000ms 동안만 대기
// wait의 리턴값은 false면 timeout, true면 wakeup을 기본으로 합니다.
bool received = signal.wait(1000);

// 조건 함수가 있는 wait - ready변수가 false이면 대기, true이면 빠져 나옵니다.
// 람다식에서 대기전 락을 걸고 대기전에 람다함수를 호출 합니다. 즉, 최초 1회는 무조건 호출합니다.
// 이때 함수 리턴이 false이면 대기 -&amp;gt; 깨어났을때 다시 func호출 -&amp;gt; 이때 fale이면 다시 대기...
// 이는 condition_variable::wait의 동작과 같습니다.
signal.wait([&amp;amp;]() { return ready; });

// timeout과 조건 함수가 모두 있는 wait
bool success = signal.wait(1000, [&amp;amp;]() { return ready &amp;amp;&amp;amp; !error; });

// lock을 직접 제어하는 wait, false : timeout, true : wakeup
auto guard = signal.acquire_scoped_lock();
bool ok = signal.wait(guard, 1000);&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;아래 notify_one은 실행 시 자동으로&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;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;락을 획득하고&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;람다를 실행하고&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;시그널을 보내고&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;락을 해제합니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1734674429630&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 1. 기본 notify_one
signal.notify_one() // 대기하는 signal을 깨웁니다.

// 2. 기본 람다 notify_one - 리턴값 없음
signal.notify_one([&amp;amp;]() { shared_data = &quot;new data&quot;; });

// 3. lock을 받는 notify_one - 리턴값 없음
signal.notify_one([&amp;amp;](std::unique_lock&amp;lt;std::mutex&amp;gt; &amp;amp;guard)
                  {
                      shared_data = &quot;new data&quot;; 
                      guard.unlock(); 
                      do_something_else();
                      guard.lock();
                  });

// 4. 리턴값이 있는 notify_one, 리턴값 타입은 마음대로 지정 가능
bool success = signal.notify_one([&amp;amp;]() -&amp;gt; bool
                                 { 
                                     shared_data = &quot;new data&quot;; 
                                     return true; 
                                 });

// 5. lock을 받고 리턴값도 있는 notify_one, 리턴값 타입은 마음대로 지정 가능
std::string result = signal.notify_one([&amp;amp;](std::unique_lock&amp;lt;std::mutex&amp;gt; &amp;amp;guard) -&amp;gt; std::string
                                       {
                                         shared_data = &quot;new data&quot;;
                                         guard.unlock();
                                         do_something_else();
                                         guard.lock();
                                         return &quot;completed&quot;;
                                       });&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;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;특징&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; letter-spacing: 0px;&quot; data-darkreader-inline-color=&quot;&quot;&gt;Lost Wakeup &amp;amp; &lt;/span&gt;&lt;span style=&quot;text-align: start;&quot;&gt;Spurious wakeup&lt;/span&gt;&lt;span style=&quot;color: #000000; letter-spacing: 0px;&quot; data-darkreader-inline-color=&quot;&quot;&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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;signaled_ 플래그를 사용하여 시그널 상태 추적&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;시그널이 발생했을 때 상태를 저장하고 대기자가 있을 때 처리&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;wait가 실행 중이지 않는 상태에서 signal이 와도 signaled_플래그를 확인하여 시그널 처리&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;안전한 1:1 시그널링&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;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;notify_one() 메서드를 통한 단일 수신자 통신&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;1:N 통신에서는 시그널 손실 문제로 notify_all 사용을 제한&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;std::condition_variable 래핑하기&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;notify_one의 특수화가 다소 복잡해 보입니다. 위 인터페이스에서 정의한 구현을 하기 위해서는 어쩔 수 없습니다. 만약 람다 인터페이스를 제거한다면 간단해질 겁니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1734675019912&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class MSignal
{
public:
  MSignal () = default;
  ~MSignal() = default;

  bool wait(uint32_t tmout_msec = 0);

  void wait(std::function&amp;lt;bool()&amp;gt; func);

  // false : timeout, true : wakeup
  bool wait(uint32_t tmout_msec, std::function&amp;lt;bool()&amp;gt; func);

  // lock은 자동 언락됨. 이때 첫번째 인자 lock는 acquire_scoped_lock의 락을 사용해야함.
  // false : timeout, true : wakeup
  bool wait(std::unique_lock&amp;lt;std::mutex&amp;gt; &amp;amp;guard, uint32_t tmout_msec = 0);

  void notify_one();

  std::unique_lock&amp;lt;std::mutex&amp;gt;
       acquire_scoped_lock() const;

  void notify_one(std::unique_lock&amp;lt;std::mutex&amp;gt; &amp;amp;locked_guard);
  
  // 기본 케이스 - 인자가 없는 람다, non-void 리턴
  template&amp;lt;typename F,
           typename = typename std::enable_if&amp;lt;!std::is_void&amp;lt;decltype(std::declval&amp;lt;F&amp;gt;()())&amp;gt;::value&amp;gt;::type&amp;gt;
  auto notify_one(F&amp;amp;&amp;amp; func) -&amp;gt; decltype(std::declval&amp;lt;F&amp;gt;()());

  // void 리턴 특수화 - 인자가 없는 람다
  template&amp;lt;typename F,
           typename = typename std::enable_if&amp;lt;std::is_void&amp;lt;decltype(std::declval&amp;lt;F&amp;gt;()())&amp;gt;::value&amp;gt;::type,
           typename = void&amp;gt;
  void notify_one(F&amp;amp;&amp;amp; func);

  // unique_lock을 인자로 받는 람다 특수화, non-void 리턴
  template&amp;lt;typename F,
           typename = typename std::enable_if&amp;lt;!std::is_void&amp;lt;decltype(std::declval&amp;lt;F&amp;gt;()(std::declval&amp;lt;std::unique_lock&amp;lt;std::mutex&amp;gt;&amp;amp;&amp;gt;()))&amp;gt;::value&amp;gt;::type,
           typename = void,
           typename = void&amp;gt;
  auto notify_one(F&amp;amp;&amp;amp; func) -&amp;gt; decltype(std::declval&amp;lt;F&amp;gt;()(std::declval&amp;lt;std::unique_lock&amp;lt;std::mutex&amp;gt;&amp;amp;&amp;gt;()));
  
  // void 리턴 특수화 - unique_lock을 인자로 받는 람다
  template&amp;lt;typename F,
           typename = typename std::enable_if&amp;lt;std::is_void&amp;lt;decltype(std::declval&amp;lt;F&amp;gt;()(std::declval&amp;lt;std::unique_lock&amp;lt;std::mutex&amp;gt;&amp;amp;&amp;gt;()))&amp;gt;::value&amp;gt;::type,
           typename = void, // 더미파라미터
           typename = void,
           typename = void&amp;gt;
  void notify_one(F&amp;amp;&amp;amp; func);

protected:
  void notify_one_nolock();

private:
  mutable std::mutex      lock_;
  std::condition_variable cond_;
  bool                    signaled_ = false;
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #e8e6e3; text-align: start; --darkreader-inline-color: #d8d4cf;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;Signal 클래스 구현&lt;/span&gt;&lt;/h2&gt;
&lt;h3 style=&quot;color: #e8e6e3; text-align: start; --darkreader-inline-color: #d8d4cf;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;고급 기능: 템플릿 기반 notify_one 구현&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #c8c3bc; text-align: start; --darkreader-inline-color: #c1bcb4;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;Signal 클래스는 람다 함수를 활용한 다양한 형태의 notify_one을 제공하도록 인터페이스를 정의하였습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1734675690732&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 아래 public로 노출되는 함수가 공통으로 사용할 함수입니다.
// 시그널이 호출되면 signaled_를 true로 바꾸어 줍니다.
// 이미 signaled_가 true이면 중복해서 깨우지 않도록 합니다.
// wait함수 진입전 notify_one이 되더라도 wait에 진입하면 signaled_로 깨웠는지 확인할 수 있습니다.
void notify_one_nolock()
{
  if (signaled_ == true)
    return;

  signaled_ = true;
  cond_.notify_one();
}

// 기본적인 notify_one
void notify_one()
{
  std::lock_guard&amp;lt;std::mutex&amp;gt; guard(lock_);
  notify_one_nolock();
}

// 기본 케이스 - 인자가 없는 람다, non-void 리턴
template&amp;lt;typename F,
         typename = typename std::enable_if&amp;lt;!std::is_void&amp;lt;decltype(std::declval&amp;lt;F&amp;gt;()())&amp;gt;::value&amp;gt;::type&amp;gt;
auto notify_one(F &amp;amp;&amp;amp;func) -&amp;gt; decltype(std::declval&amp;lt;F&amp;gt;()())
{
  std::lock_guard&amp;lt;std::mutex&amp;gt; guard(lock_);
  auto result = std::forward&amp;lt;F&amp;gt;(func)();
  notify_one_nolock();
  return result;
}

// void 리턴 특수화 - 인자가 없는 람다
template&amp;lt;typename F,
         typename = typename std::enable_if&amp;lt;std::is_void&amp;lt;decltype(std::declval&amp;lt;F&amp;gt;()())&amp;gt;::value&amp;gt;::type,
         typename = void&amp;gt;
void notify_one(F &amp;amp;&amp;amp;func)
{
  std::lock_guard&amp;lt;std::mutex&amp;gt; guard(lock_);
  std::forward&amp;lt;F&amp;gt;(func)();
  notify_one_nolock();
}

// unique_lock을 인자로 받는 람다 특수화, non-void 리턴
template&amp;lt;typename F,
         typename = typename std::enable_if&amp;lt;!std::is_void&amp;lt;decltype(std::declval&amp;lt;F&amp;gt;()(std::declval&amp;lt;std::unique_lock&amp;lt;std::mutex&amp;gt;&amp;amp;&amp;gt;()))&amp;gt;::value&amp;gt;::type,
         typename = void,
         typename = void&amp;gt;
auto notify_one(F &amp;amp;&amp;amp;func) -&amp;gt; decltype(std::declval&amp;lt;F&amp;gt;()(std::declval&amp;lt;std::unique_lock&amp;lt;std::mutex&amp;gt;&amp;amp;&amp;gt;()))
{
  std::unique_lock&amp;lt;std::mutex&amp;gt; guard(lock_);
  auto result = std::forward&amp;lt;F&amp;gt;(func)(guard);
  notify_one_nolock();
  return result;
}

// void 리턴 특수화 - unique_lock을 인자로 받는 람다
template&amp;lt;typename F,
         typename = typename std::enable_if&amp;lt;std::is_void&amp;lt;decltype(std::declval&amp;lt;F&amp;gt;()(std::declval&amp;lt;std::unique_lock&amp;lt;std::mutex&amp;gt;&amp;amp;&amp;gt;()))&amp;gt;::value&amp;gt;::type,
         typename = void, // 더미파라미터
         typename = void,
         typename = void&amp;gt;
void notify_one(F &amp;amp;&amp;amp;func)
{
  std::unique_lock&amp;lt;std::mutex&amp;gt; guard(lock_);
  std::forward&amp;lt;F&amp;gt;(func)(guard);
  notify_one_nolock();
}

std::unique_lock&amp;lt;std::mutex&amp;gt;
      acquire_scoped_lock() const
{
  return std::unique_lock&amp;lt;std::mutex&amp;gt;(this-&amp;gt;lock_);
}

void notify_one(std::unique_lock&amp;lt;std::mutex&amp;gt; &amp;amp;guard)
{
  if (guard.owns_lock() == false)
    guard.lock();

  notify_one_nolock();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #c8c3bc; text-align: start; --darkreader-inline-color: #c1bcb4;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #c8c3bc; text-align: start; --darkreader-inline-color: #c1bcb4;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;템플릿 해석은 아래와 같습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1734676361602&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  template&amp;lt;typename F,
           typename = typename std::enable_if&amp;lt;!std::is_void&amp;lt;decltype(std::declval&amp;lt;F&amp;gt;()())&amp;gt;::value&amp;gt;::type&amp;gt;
  auto notify_one(F&amp;amp;&amp;amp; func) -&amp;gt; decltype(std::declval&amp;lt;F&amp;gt;()())&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #c8c3bc; text-align: start; --darkreader-inline-color: #c1bcb4;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #c8c3bc; text-align: start; --darkreader-inline-color: #c1bcb4;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;decltype(std::declval&amp;lt;F&amp;gt;()())&amp;nbsp;-&amp;nbsp;람다의&amp;nbsp;반환&amp;nbsp;타입을&amp;nbsp;얻음&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;std::is_void&amp;lt;...&amp;gt; - 그 타입이 void인지 체크&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;std::enable_if&amp;lt;!...&amp;gt;::type - void가 아닐때(! not)만 이 특수화가 활성화되도록 컴파일 함&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #c8c3bc; text-align: start; --darkreader-inline-color: #c1bcb4;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #c8c3bc; text-align: start; --darkreader-inline-color: #c1bcb4;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;더미 파라미터(Dummy Parameter)는 C++ 템플릿 특수화에서 중요한 테크닉입니다.&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;오버로딩 구분: 같은 시그니처를 가진 템플릿들을 구분하기 위해 사용&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;--darkreader-inline-color: #e8e6e3;&quot;&gt;SFINAE 활용: 컴파일 시점에서 특정 조건에 따라 함수 오버로드를 선택&lt;/span&gt;&lt;span style=&quot;--darkreader-inline-color: #e8e6e3;&quot;&gt;&lt;/span&gt;&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;예를 들어,&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1734676254069&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// void 리턴 특수화
template&amp;lt;typename F,
         typename = typename std::enable_if&amp;lt;std::is_void&amp;lt;...&amp;gt;&amp;gt;::type,
         typename = void&amp;gt;  // 더미 파라미터
void notify_one(F&amp;amp;&amp;amp; func);

// non-void 리턴 특수화
template&amp;lt;typename F,
         typename = typename std::enable_if&amp;lt;!std::is_void&amp;lt;...&amp;gt;&amp;gt;::type&amp;gt;
auto notify_one(F&amp;amp;&amp;amp; func) -&amp;gt; decltype(...);&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;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;첫 번째 버전은 두 개의 typename 파라미터를 가짐&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;두 번째 버전은 한 개의 typename 파라미터를 가짐&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;이렇게 더미 파라미터를 추가함으로써 컴파일러가 두 템플릿을 서로 다른 것으로 인식하게 만듭니다. 이는 &quot;두 템플릿이 완전히 같은 시그니처를 가지는 것을 방지&quot;하는 효과가 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #e8e6e3; text-align: start; --darkreader-inline-color: #d8d4cf;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;wait 함수의 구현&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;std::condition_variable::wait 의 동작과 같도록 하되, 가짜 깨움과, 시그널 놓침을 notify_one에서 설정된 signaled_로 확인하여 &lt;span style=&quot;color: #000000; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot;&gt;signaled_가 true인 경우만 람다함수인 func를 실행하도록 합니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1734675501835&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 리턴값 : false = timeout
// 리턴값 : true  = wakeup
bool wait(uint32_t tmout_msec = 0) { return this-&amp;gt;wait(tmout_msec, nullptr); }

// 깨어났을때 사용자 함수 실행합니다.
// 만약 사용자 함수가 true를 리턴하면 wait에서 깨어나고 false를 리턴하면 계속 대기합니다.
void wait(std::function&amp;lt;bool()&amp;gt; func) { this-&amp;gt;wait(0, func); }

inline bool
MSignal::wait(uint32_t tmout_msec, std::function&amp;lt;bool()&amp;gt; func)
{
  size_t count = 0;
  // std::condition_variable::wait에 전달할 함수입니다.
  std::function&amp;lt;bool()&amp;gt; pred = [&amp;amp;]()
  {    
    // 현재 시그널 상태값을 보관합니다.
    const bool signaled = signaled_;

    // 진짜 시그널이 왔는지 확인하고 진짜 시그널이면 
    // 다음 대기를 위해 false로 바꾸어줍니다.
    if (signaled_ == true)
      signaled_ = false;

    // 사용자 함수가 있다면
    if (func != nullptr)
    {
      // condition_variable::wait함수는 최초 1회 사용자 함수를 무조건 실행하도록 되어 있습니다. 
      if (count++  == 0   ) return func();

      // 시그널이 온 경우만 사용자 함수를 실행합니다.
      if (signaled == true) return func();
    }

    // 사용자 함수가 없는 경우 시그널이 왔다면 true를, 아니면 false를 리턴할 것입니다.
    // condition_variable::wait는 true인 경우 깨어나고, fasle인 경우 다시 대기합니다.
    return signaled;
  };

  std::unique_lock&amp;lt;std::mutex&amp;gt; guard(lock_);
  if (tmout_msec == 0)
  {
    cond_.wait(guard, pred);
    return true;
  }

  std::chrono::milliseconds timeout(tmout_msec);
  return cond_.wait_for(guard, timeout, pred);
}

// MSignal::acquire_scoped_lock()의 락을 인자로 받는 wait함수도 만들어 줍니다.
// MSignal::acquire_scoped_lock()은 wait함수 구현부에 정의되어 있습니다.
inline bool
MSignal::wait(std::unique_lock&amp;lt;std::mutex&amp;gt; &amp;amp;guard, uint32_t tmout_msec)
{
  std::function&amp;lt;bool()&amp;gt; pred = [&amp;amp;]()
  {
    if (signaled_ == true)
    {
      signaled_ = false;
      return true;
    }

    return false;
  };

  if (guard.owns_lock() == false)
    guard.lock();

  if (tmout_msec == 0)
  {
    cond_.wait(guard, pred);
    return true;
  }

  std::chrono::milliseconds timeout(tmout_msec);
  return cond_.wait_for(guard, timeout, pred);
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;사용 예제&lt;/span&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1734676878698&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;thread&amp;gt;
#include &quot;MSignal.h&quot;

int
main()
{

 MSignal signal;
 int value = 0;

 std::thread worker([&amp;amp;]()
 {
   while(value &amp;lt; 3)
   {
     signal.wait();
     std::cout &amp;lt;&amp;lt; &quot;received: &quot; &amp;lt;&amp;lt; value &amp;lt;&amp;lt; std::endl;
   }
 });

 for(int index = 1; index &amp;lt;= 3; ++index)
 {
   signal.notify_one([&amp;amp;]() { value = index; });

   std::this_thread::sleep_for(std::chrono::seconds(1));
 }

 worker.join();
 return 0;
}&lt;/code&gt;&lt;/pre&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;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;제약사항&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;1:N 통신 제한&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;MSignal은 1:1 통신에 최적화되어 있습니다. 다중 수신자가 필요한 경우 별도의 시그널 그룹핑 메커니즘 사용이 필요합니다. 저 같은 경우 1:1을 여러 개 묶어 N:N 또는 1:N을 구현합니다.&lt;/span&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;끝맺음&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot;&gt;Modern C++의 std::condition_variable을 기반으로 한 MSignal 클래스는 쓰레드 간 안전한 시그널링을 구현하는 도구입니다. Lost wakeup과 spurious wakeup 문제를 해결하면서도, 람다 함수를 통한 유연한 사용성을 확보해야 합니다. 이러한 구현은 멀티쓰레드 프로그래밍에서 자주 발생하는 동기화 문제를 해결할 수 있게 해 주며, 특히 생산자-소비자 패턴과 같은 일반적인 멀티쓰레딩 시나리오에서 매우 유용하게 활용될 수 있습니다.&lt;/span&gt;&lt;/p&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;pre id=&quot;code_1746934247024&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#pragma once

#include &amp;lt;mutex&amp;gt;
#include &amp;lt;condition_variable&amp;gt;
#include &amp;lt;chrono&amp;gt;
#include &amp;lt;functional&amp;gt;

class MSignal
{
public:
  MSignal () = default;
  ~MSignal() = default;

  void notify_one()
  {
    std::lock_guard&amp;lt;std::mutex&amp;gt; guard(lock_);
    notify_one_nolock();
  }

  // 기본 케이스 - 인자가 없는 람다, non-void 리턴
  template&amp;lt;typename F,
           typename = typename std::enable_if&amp;lt;!std::is_void&amp;lt;decltype(std::declval&amp;lt;F&amp;gt;()())&amp;gt;::value&amp;gt;::type&amp;gt;
  auto notify_one(F &amp;amp;&amp;amp;func) -&amp;gt; decltype(std::declval&amp;lt;F&amp;gt;()())
  {
      std::lock_guard&amp;lt;std::mutex&amp;gt; guard(lock_);
      auto result = std::forward&amp;lt;F&amp;gt;(func)();
      notify_one_nolock();
      return result;
  }

  // void 리턴 특수화 - 인자가 없는 람다
  template&amp;lt;typename F,
           typename = typename std::enable_if&amp;lt;std::is_void&amp;lt;decltype(std::declval&amp;lt;F&amp;gt;()())&amp;gt;::value&amp;gt;::type,
           typename = void&amp;gt;
  void notify_one(F &amp;amp;&amp;amp;func)
  {
      std::lock_guard&amp;lt;std::mutex&amp;gt; guard(lock_);
      std::forward&amp;lt;F&amp;gt;(func)();
      notify_one_nolock();
  }

  // unique_lock을 인자로 받는 람다 특수화, non-void 리턴
  template&amp;lt;typename F,
           typename = typename std::enable_if&amp;lt;!std::is_void&amp;lt;decltype(std::declval&amp;lt;F&amp;gt;()(std::declval&amp;lt;std::unique_lock&amp;lt;std::mutex&amp;gt;&amp;amp;&amp;gt;()))&amp;gt;::value&amp;gt;::type,
           typename = void,
           typename = void&amp;gt;
  auto notify_one(F &amp;amp;&amp;amp;func) -&amp;gt; decltype(std::declval&amp;lt;F&amp;gt;()(std::declval&amp;lt;std::unique_lock&amp;lt;std::mutex&amp;gt;&amp;amp;&amp;gt;()))
  {
      std::unique_lock&amp;lt;std::mutex&amp;gt; guard(lock_);
      auto result = std::forward&amp;lt;F&amp;gt;(func)(guard);
      notify_one_nolock();
      return result;
  }

  // void 리턴 특수화 - unique_lock을 인자로 받는 람다
  template&amp;lt;typename F,
           typename = typename std::enable_if&amp;lt;std::is_void&amp;lt;decltype(std::declval&amp;lt;F&amp;gt;()(std::declval&amp;lt;std::unique_lock&amp;lt;std::mutex&amp;gt;&amp;amp;&amp;gt;()))&amp;gt;::value&amp;gt;::type,
           typename = void, // 더미파라미터
           typename = void,
           typename = void&amp;gt;
  void notify_one(F &amp;amp;&amp;amp;func)
  {
      std::unique_lock&amp;lt;std::mutex&amp;gt; guard(lock_);
      std::forward&amp;lt;F&amp;gt;(func)(guard);
      notify_one_nolock();
  }

  std::unique_lock&amp;lt;std::mutex&amp;gt;
       acquire_scoped_lock() const
  {
    return std::unique_lock&amp;lt;std::mutex&amp;gt;(this-&amp;gt;lock_);
  }

  void notify_one(std::unique_lock&amp;lt;std::mutex&amp;gt; &amp;amp;guard)
  {
    if (guard.owns_lock() == false)
      guard.lock();

    notify_one_nolock();
  }

  // false : timeout, true : wakeup
  bool wait(uint32_t tmout_msec = 0) { return this-&amp;gt;wait(tmout_msec, nullptr); }

  // func가 true를 리턴하면 wait가 즉시 깨어남
  // 대기전 락을 걸고 func호출 false이면 대기-&amp;gt;깨어났을때 다시 func호출-&amp;gt;이때 fale이면 다시 대기...
  void wait(std::function&amp;lt;bool()&amp;gt; func) { this-&amp;gt;wait(0, func); }

  // false : timeout, true : wakeup
  bool wait(uint32_t tmout_msec, std::function&amp;lt;bool()&amp;gt; func);

  // lock은 자동 언락됨. 이때 lock는 acquire_scoped_lock의 락을 사용해야함.
  // false : timeout, true : wakeup
  bool wait(std::unique_lock&amp;lt;std::mutex&amp;gt; &amp;amp;guard, uint32_t tmout_msec = 0);

protected:
  void notify_one_nolock()
  {
    if (signaled_ == true)
      return;

    signaled_ = true;
    cond_.notify_one();
  }

private:
  mutable std::mutex      lock_;
  std::condition_variable cond_;
  bool                    signaled_ = false;
};

inline bool
MSignal::wait(uint32_t tmout_msec, std::function&amp;lt;bool()&amp;gt; func)
{
  size_t count = 0;
  std::function&amp;lt;bool()&amp;gt; pred = [&amp;amp;]()
  {
    const bool signaled = signaled_;

    if (signaled_ == true)
      signaled_ = false;

    if (func != nullptr)
    {
      if (count++  == 0   ) return func();
      if (signaled == true) return func();
    }

    return signaled;
  };

  std::unique_lock&amp;lt;std::mutex&amp;gt; guard(lock_);
  if (tmout_msec == 0)
  {
    cond_.wait(guard, pred);
    return true;
  }

  std::chrono::milliseconds timeout(tmout_msec);
  return cond_.wait_for(guard, timeout, pred);
}

inline bool
MSignal::wait(std::unique_lock&amp;lt;std::mutex&amp;gt; &amp;amp;guard, uint32_t tmout_msec)
{
  std::function&amp;lt;bool()&amp;gt; pred = [&amp;amp;]()
  {
    if (signaled_ == true)
    {
      signaled_ = false;
      return true;
    }

    return false;
  };

  if (guard.owns_lock() == false)
    guard.lock();

  if (tmout_msec == 0)
  {
    cond_.wait(guard, pred);
    return true;
  }

  std::chrono::milliseconds timeout(tmout_msec);
  return cond_.wait_for(guard, timeout, pred);
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1746934283847&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;std::thread의 실전적 랩핑 - thread 클래스 구현&quot; data-og-description=&quot;들어가며C++11에서 std::thread가 도입된 이후, 많은 개발자들이 멀티쓰레딩 프로그래밍을 더 쉽게 할 수 있게 되었습니다. 하지만 실제 프로덕션 환경에서는 std::thread를 직접 사용하는 것보다는 적&quot; data-og-host=&quot;cplusplus.tistory.com&quot; data-og-source-url=&quot;https://cplusplus.tistory.com/entry/stdthread%EC%9D%98-%EC%8B%A4%EC%A0%84%EC%A0%81-%EB%9E%A9%ED%95%91-thread-%ED%81%B4%EB%9E%98%EC%8A%A4-%EA%B5%AC%ED%98%84&quot; data-og-url=&quot;https://cplusplus.tistory.com/entry/stdthread%EC%9D%98-%EC%8B%A4%EC%A0%84%EC%A0%81-%EB%9E%A9%ED%95%91-thread-%ED%81%B4%EB%9E%98%EC%8A%A4-%EA%B5%AC%ED%98%84&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/OnurB/hyYRnPte5f/SfxJsGdhSZN8KthGMCKj7k/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/dtFzj7/hyYTlb0ToF/KWEI7kHhz4EkaU2NsvKjnK/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800&quot;&gt;&lt;a href=&quot;https://cplusplus.tistory.com/entry/stdthread%EC%9D%98-%EC%8B%A4%EC%A0%84%EC%A0%81-%EB%9E%A9%ED%95%91-thread-%ED%81%B4%EB%9E%98%EC%8A%A4-%EA%B5%AC%ED%98%84&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://cplusplus.tistory.com/entry/stdthread%EC%9D%98-%EC%8B%A4%EC%A0%84%EC%A0%81-%EB%9E%A9%ED%95%91-thread-%ED%81%B4%EB%9E%98%EC%8A%A4-%EA%B5%AC%ED%98%84&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/OnurB/hyYRnPte5f/SfxJsGdhSZN8KthGMCKj7k/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/dtFzj7/hyYTlb0ToF/KWEI7kHhz4EkaU2NsvKjnK/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800');&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;std::thread의 실전적 랩핑 - thread 클래스 구현&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;들어가며C++11에서 std::thread가 도입된 이후, 많은 개발자들이 멀티쓰레딩 프로그래밍을 더 쉽게 할 수 있게 되었습니다. 하지만 실제 프로덕션 환경에서는 std::thread를 직접 사용하는 것보다는 적&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;cplusplus.tistory.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;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;&lt;/span&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot;&gt;본 블로그의 소스코드는 자유롭게 사용하셔도 되나, 사용 시 발생하는 모든 문제에 대한 책임은 전적으로 사용자에게 있습니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;</description>
      <category>C++</category>
      <category>condition_variable</category>
      <category>cpp</category>
      <category>lostwakeup</category>
      <category>Modern</category>
      <category>pthread_cond_signal</category>
      <category>spuriouswakeup</category>
      <category>std::condition_variable</category>
      <author>tys</author>
      <guid isPermaLink="true">https://cplusplus.tistory.com/7</guid>
      <comments>https://cplusplus.tistory.com/entry/stdconditionvariable%EC%9D%98-%EC%95%88%EC%A0%84%ED%95%9C-%EC%8B%9C%EA%B7%B8%EB%84%90%EB%A7%81#entry7comment</comments>
      <pubDate>Fri, 20 Dec 2024 15:42:14 +0900</pubDate>
    </item>
    <item>
      <title>C 개발자는 모르는 자동 자원 해제 C++ RAII</title>
      <link>https://cplusplus.tistory.com/entry/C%EA%B0%9C%EB%B0%9C%EC%9E%90%EB%8A%94-%EB%AA%A8%EB%A5%B8%EB%8A%94-%EC%9E%90%EB%8F%99-%EC%9E%90%EC%9B%90-%ED%95%B4%EC%A0%9C-CPP-RAII</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot;&gt;RAII(Resource Acquisition Is Initialization)는&lt;/span&gt; 현대 언어에 와서는 없어서는 안 될 필수 개념으로 자리 잡았습니다. 그리고 현대 언어들에게 많은 영향을 끼쳤습니다. 하지만 이를 적극 활용하여 개발하는 C++개발자는 게임 이외의 산업에서는 드문 편입니다.&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;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;한국은 &lt;span style=&quot;text-align: start;&quot;&gt;젊은 &lt;/span&gt;C++개발자들이 없어서 일까요? 아니면 C개발자들이 마지못해 JAVA나 C++시장으로 밀려들어와서 일까요. C99 부터는&lt;span style=&quot;text-align: start;&quot;&gt; C++과&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;호환되지 않습니다. &lt;/span&gt;&lt;/span&gt;C는 C23표준에 와서야 자동해제를 지원하기 시작했습니다.)&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;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;C개발자들이 C++ 생태계에 참여하는 것은 매우 환영할 만한 일입니다. 하지만 이 과정에서 C++의 현대적 특성이 이해되지 않고, 단순히 'C의 확장' 정도로 취급되는 현실은 비판적으로 바라볼 필요가 있습니다.&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;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;이 RAII 패턴은 전반적으로 &lt;span style=&quot;color: #000000; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot;&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;&amp;nbsp;&lt;/p&gt;

            &lt;figure class=&quot;unsupported component-kakaotv&quot; contenteditable=&quot;false&quot; style=&quot;background:#000;margin:16px 0;min-height:72px;padding:10px 16px;display:flex;align-items:center;justify-content:center;text-align:center;box-sizing:border-box;width:100%;max-width:100%;&quot;&gt;
                &lt;p contenteditable=&quot;false&quot; style=&quot;margin:0;color:#8a8a8a;font-size:13px;line-height:1.6;user-select:none;pointer-events:none;&quot;&gt;동영상 서비스가 종료되어 해당 콘텐츠를 재생할 수 없습니다.&lt;/p&gt;
            &lt;/figure&gt;
        
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;h3-3&quot; style=&quot;color: #172f47; text-align: start;&quot; data-ke-size=&quot;size23&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;RAII 간단한 활용들&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;대표적으로 mutex를 사용할 때 아래와 같이 자동으로 unlock 시킬 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1734338083447&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;void func()
{
  std::lock_guard&amp;lt;std::mutex&amp;gt; guard(mutex);
  if (...) 
    return; ///&amp;lt; guard에 의해 unlock
  
  for (....)
    return; ///&amp;lt; guard에 의해 unlock
  ................
} ///&amp;lt; guard에 의해 unlock&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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;이 외에도 람다와 같이 사용할 수 있는 boost::scope_exit도 있죠. 저 또한 boost를 사용하지 않는 곳이 종종 있기에 scope exit를 만들어 사용하고 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1734338550179&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;void example() 
{
  int* p = new int(42);
 
  BOOST_SCOPE_EXIT(p)
  {
    delete p;
    std::cout &amp;lt;&amp;lt; &quot;Deleted\n&quot;;
  } BOOST_SCOPE_EXIT_END

  // p 사용
  *p = 100;
} ///&amp;lt; BOOST_SCOPE_EXIT &quot;Deleted\n&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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;일반적인 메모리 할당을 필요로 하는 클래스에서 사용하게 되면 클래스 자원해제 시 자동으로 해제되어 소멸자를 별도로 구현할 필요가 없을 정도입니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1734338628511&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class foo
{
public:
  foo() : value(std::make_shared&amp;lt;int&amp;gt;()) {}
  
private:
  std::shared_ptr&amp;lt;int&amp;gt; value;
}&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;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;특히나 foo 클래스가 어디로 도망가던 value가 Dangling Pointer를 만들어 주지 않으면서도 참조가 없으면 자동으로 자원 해제를 해주므로 안정성과 편리함 모두 확보할 수 있습니다. 물론 동시성이라던가, 순환 참조등을 감안하면 클래스를 설계하는 것이 쉽지만은 않지만, C에서 메모리 할당받고 난 후 이걸 어느 시점에 해제해야 하는지 코드에서 찾는 것보다는 설계에서 해결하는 것이 훨씬 이득입니다.&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;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;이 외에도 STL의 대부분의 클래스들(string, vector, deque, map, set, iostream...)은 RAII 패턴이 적용된 것들입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;h3-3&quot; style=&quot;color: #172f47; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;40년 된 RAII, 아직도 강력하고 유용한 패턴&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;이 RAII패턴을 구글링 해보면 아주 오래된 패턴입니다. 최근의 현대화된 언어에서 출발한 것도 아닌 그전부터 존재하던 개념이었습니다. C++의 창시자 Bjarne Stroustrup은 C++을 만들 때부터 이 기능을 생각하고 있었다고 이야기하고 있습니다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;1979년부터 시작된&amp;nbsp;&lt;span style=&quot;color: #000000; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot;&gt;C++ &lt;/span&gt;개발은 1985년, 처음 상업용으로 릴리즈 됩니다. 그리고 1998년에 들어서 첫 C++ ISO/IEC 표준인 C++98이 발표됩니다.&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;color: #000000; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot;&gt;이 RAII는 &lt;span style=&quot;background-color: #fafafa; color: #000000; text-align: start;&quot; data-darkreader-inline-bgcolor=&quot;&quot; data-darkreader-inline-color=&quot;&quot;&gt;C++언어가 얼마나 많은 다른 프로그래밍 언어(&lt;span style=&quot;color: #000000; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot;&gt;Rust, Python의 with문, Go의 defer 등)&lt;/span&gt; 현대화에 기여했는지 보여주는 사례중 하나일 것입니다.&lt;/span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot; data-darkreader-inline-color=&quot;&quot;&gt;그런데 이처럼 무척 오래되고 &lt;span style=&quot;color: #000000; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot;&gt;여전히 현대 프로그래밍 언어의 핵심 개념으로 자리 잡고 있는데도&lt;/span&gt; 불구하고&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;왜 유독 한국의 C++코드에서는 RAII를 찾아볼 수 없는 걸까요?&lt;/span&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;/p&gt;
&lt;figure id=&quot;og_1734784022042&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;RAII - cppreference.com&quot; data-og-description=&quot;Resource Acquisition Is Initialization or RAII, is a C++ programming technique[1][2] which binds the life cycle of a resource that must be acquired before use (allocated heap memory, thread of execution, open socket, open file, locked mutex, disk space, da&quot; data-og-host=&quot;en.cppreference.com&quot; data-og-source-url=&quot;https://en.cppreference.com/w/cpp/language/raii&quot; data-og-url=&quot;https://en.cppreference.com/w/cpp/language/raii&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://en.cppreference.com/w/cpp/language/raii&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://en.cppreference.com/w/cpp/language/raii&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&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;RAII - cppreference.com&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Resource Acquisition Is Initialization or RAII, is a C++ programming technique[1][2] which binds the life cycle of a resource that must be acquired before use (allocated heap memory, thread of execution, open socket, open file, locked mutex, disk space, da&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;en.cppreference.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;figure id=&quot;og_1739468968279&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;RAII (Resource Acquisition Is Initialization) In C++&quot; data-og-description=&quot;Welcome, dear reader! Today, we&amp;rsquo;re diving into the magical world of C++ and exploring a concept that&amp;rsquo;s as essential as your morning coffee: RAII, or Resource&quot; data-og-host=&quot;blog.heycoach.in&quot; data-og-source-url=&quot;https://blog.heycoach.in/raii-resource-acquisition-is-initialization-in-c/&quot; data-og-url=&quot;https://blog.heycoach.in/raii-resource-acquisition-is-initialization-in-c/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://blog.heycoach.in/raii-resource-acquisition-is-initialization-in-c/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://blog.heycoach.in/raii-resource-acquisition-is-initialization-in-c/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&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;RAII (Resource Acquisition Is Initialization) In C++&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Welcome, dear reader! Today, we&amp;rsquo;re diving into the magical world of C++ and exploring a concept that&amp;rsquo;s as essential as your morning coffee: RAII, or Resource&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;blog.heycoach.in&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div class=&quot;jfk-bubble gtx-bubble&quot; style=&quot;visibility: visible; left: 172px; top: 2289px; opacity: 1;&quot; role=&quot;alertdialog&quot; aria-describedby=&quot;bubble-5&quot;&gt;
&lt;div id=&quot;bubble-5&quot; class=&quot;jfk-bubble-content-id&quot;&gt;
&lt;div id=&quot;gtx-host&quot; style=&quot;min-width: 200px; max-width: 400px;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;jfk-bubble-closebtn-id jfk-bubble-closebtn&quot; tabindex=&quot;0&quot; role=&quot;button&quot; aria-label=&quot;Close&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;jfk-bubble-arrow-id jfk-bubble-arrow jfk-bubble-arrowdown&quot; style=&quot;left: 176.5px;&quot;&gt;
&lt;div class=&quot;jfk-bubble-arrowimplbefore&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;jfk-bubble-arrowimplafter&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
      <category>c</category>
      <category>C++</category>
      <category>cpp</category>
      <category>lock_guard</category>
      <category>mutex</category>
      <category>RAII</category>
      <category>shared_ptr</category>
      <category>unique_lock</category>
      <category>개발자</category>
      <author>tys</author>
      <guid isPermaLink="true">https://cplusplus.tistory.com/5</guid>
      <comments>https://cplusplus.tistory.com/entry/C%EA%B0%9C%EB%B0%9C%EC%9E%90%EB%8A%94-%EB%AA%A8%EB%A5%B8%EB%8A%94-%EC%9E%90%EB%8F%99-%EC%9E%90%EC%9B%90-%ED%95%B4%EC%A0%9C-CPP-RAII#entry5comment</comments>
      <pubDate>Mon, 16 Dec 2024 18:03:28 +0900</pubDate>
    </item>
    <item>
      <title>C++ Lockfree Queue 항상 좋은 선택일까?</title>
      <link>https://cplusplus.tistory.com/entry/C-Lockfree-Queue%EA%B0%80-%ED%95%AD%EC%83%81-%EC%A2%8B%EC%9D%80-%EC%84%A0%ED%83%9D%EC%9D%BC%EA%B9%8C</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;동시성 프로그래밍에서 락-프리(queue) 데이터 구조는 매력적으로 보일 수 있습니다. 블로킹이 없는 비블로킹 설계 덕분에 높은 성능과 낮은 지연 시간을 기대할 수 있기 때문입니다. 그러나 락-프리 큐가 항상 최선일지는 정말 고려할 점이 생각보다 많습니다.&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;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;장점 - 블로킹 없는 높은 성능, 지연시간 감소, 교착상태 없음&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; text-align: start; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;단점 - 구현시 복잡도(CAS경합, 메모리 관리, ABA문제), 대기 시 CPU소모량, 제약조건들 또는 외부 라이브러리 의존성.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;락프리 알고리즘을 구현하려면 하드웨어부터 운영체제, 컴파일러, 그리고 애플리케이션 레벨까지 모든 계층에서 세심한 주의가 필요합니다. C++ Core Guidelines에서는 락프리 프로그래밍을 정말 필요치 않으면 사용하지 말라고 권고하고 있으며 이런 구현상의 난이도 때문에 대부분 외부 라이브러리를 사용하게 됩니다.&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;a style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; href=&quot;https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#cp100-dont-use-lock-free-programming-unless-you-absolutely-have-to&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-darkreader-inline-color=&quot;&quot;&gt;Don&amp;rsquo;t use lock-free programming unless you absolutely have to&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1748090932931&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;C++ Core Guidelines&quot; data-og-description=&quot;&quot; data-og-host=&quot;isocpp.github.io&quot; data-og-source-url=&quot;https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#cp100-dont-use-lock-free-programming-unless-you-absolutely-have-to&quot; data-og-url=&quot;https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#cp100-dont-use-lock-free-programming-unless-you-absolutely-have-to&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/qkMQQ/hyYYG2tev1/VxzABsYQ7210jnTvXrWae1/img.png?width=1412&amp;amp;height=619&amp;amp;face=0_0_1412_619,https://scrap.kakaocdn.net/dn/btsGN0/hyYYFCtXP8/YWNki0Tm2Ckwnd0JwERx2k/img.png?width=1412&amp;amp;height=604&amp;amp;face=0_0_1412_604&quot;&gt;&lt;a href=&quot;https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#cp100-dont-use-lock-free-programming-unless-you-absolutely-have-to&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#cp100-dont-use-lock-free-programming-unless-you-absolutely-have-to&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/qkMQQ/hyYYG2tev1/VxzABsYQ7210jnTvXrWae1/img.png?width=1412&amp;amp;height=619&amp;amp;face=0_0_1412_619,https://scrap.kakaocdn.net/dn/btsGN0/hyYYFCtXP8/YWNki0Tm2Ckwnd0JwERx2k/img.png?width=1412&amp;amp;height=604&amp;amp;face=0_0_1412_604');&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;C++ Core Guidelines&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;isocpp.github.io&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;figure class=&quot;unsupported component-kakaotv&quot; contenteditable=&quot;false&quot; style=&quot;background:#000;margin:16px 0;min-height:72px;padding:10px 16px;display:flex;align-items:center;justify-content:center;text-align:center;box-sizing:border-box;width:100%;max-width:100%;&quot;&gt;
                &lt;p contenteditable=&quot;false&quot; style=&quot;margin:0;color:#8a8a8a;font-size:13px;line-height:1.6;user-select:none;pointer-events:none;&quot;&gt;동영상 서비스가 종료되어 해당 콘텐츠를 재생할 수 없습니다.&lt;/p&gt;
            &lt;/figure&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;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;경쟁&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;블로킹이 없을 뿐, 실제로는 CAS(Compare-And-Swap) 기반의 경합이 발생, Lockfree 단어에서 주는 기대만큼의 성능은 나오지 않을 수 있습니다. 락프리큐도 CAS실패와 재시도(Spin Loop)를 수행하며 경합이 발생하기 때문에 경합문제에 있어서는 자유롭지 않습니다. 즉, PUSH와 POP연산 경쟁이 없을 수 없으며 반복적인 CAS실패와 재시도로 인해 성능이 저하됩니다.&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;CPU소모량&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;이런 경쟁 때문에 락프리큐를 사용하다 보면 기본 구현에서는 CPU사용률이 100%입니다. 이는 CAS기반이기 때문에 계속해서 데이터가 있는지 확인해야 하기 때문입니다. 이런 CPU사용률을 낮추기 위해서는 다음과 같은 전략을 생각해 볼 수 있습니다.&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;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Yield, Backoff, 시그널(condition variable).&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;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;위 3가지 전략을 각각 테스트해 보면 Yield는 여전히 높은 CPU사용률, 시그널기반(반쪽짜리 락프리)은 &quot;Lockfree&quot;와 어울리지 않게 성능이 감소하는 현상이 두드러졌습니다. Backoff 방식을 사용하면 대기 시 10% 미만의 CPU사용률과 어느 정도의 성능은 유지하도록 만들 수 있습니다. 물론 대기 시 CPU사용율이 여전하다는 건 탐탁지 않습니다. 섞어 사용하면 더 괜찮을까요?&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;데이터 형식 제한&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;락프리큐는 구현의 복잡도와 난이도가 높고 검증이 오래 걸리기 때문에 대부분 외부 라이브러리를 사용하게 됩니다. 또 이러한 외부 라이브러리 들은 CAS를 위한 제약사항들이 있기 때문에 이를 해결하기 위해 기타 추가적인 처리 과정들이 생길 수도 있습니다.&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;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;특히 boost::lockfree::queue의 경우 데이터 형식에 있어 일반적인 객체를(std::string 등...)를 사용할 수 없습니다. 때문에 POD (Plain Old Data)등의 형식이거나 포인터를 사용해야 합니다. 이런 경우 RAII패턴을 사용할 수 없으므로 별도의 래퍼를 만들어 추거나, 직접 자원 해제등의 처리를 해주어야 합니다. 이러한 추가적인 작업들은 메모리 누수등의 문제가 발생하지 않도록 실수 없이 진행되어야 합니다. &lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;text-align: left;&quot;&gt;moodycamel::ConcurrentQueue, &lt;span style=&quot;text-align: left;&quot;&gt;Facebook &lt;/span&gt;Folly, Intel TBB는 데이터 형식에서 비교적 자유롭지만,&lt;br /&gt;자신이 사용할 조건에 제약이 있는지 꼭 살펴봐야 합니다.&lt;/span&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-ke-size=&quot;size23&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;std::vector(reserve와 swap) 기반 BlockingQueue&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;표준 라이브러리의 std::vector 기반의 큐를 구현해 봅니다. 물론 프로젝트의 특성에 따라 어떻게 해야 할지는 개발자의 선택입니다. 여기서는 boost:lockfree:queue보다 빠른 BlockingQueue를 구현합니다.&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;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;std::vector의 장점은 메모리 선할당(reserve)과 swap입니다. 메모리를 미리 할당받았으므로&lt;span style=&quot;text-align: start;&quot;&gt;&amp;nbsp;PUSH 오버헤드가 크게 줄어들며, swap연산은 1번의 Lock 시간 동안 짧은 연산(포인터 교환)으로&amp;nbsp;&lt;span style=&quot;text-align: start;&quot;&gt;더 많은 데이터를 한 번에 가져올 수 있습니다. 일반적으로 사용되는 std::deque는 reserve를 지원하지 않습니다.&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;std::condition_variable를 사용하기 때문에 대기 시 CPU 점유율을 0%로 만들 수 있습니다. std::&lt;span style=&quot;text-align: start;&quot;&gt;condition_variable는&lt;/span&gt;&lt;span style=&quot;text-align: start;&quot;&gt; 사용성능을 낮추기는 하지만 위에 언급한 요소들로 성능을 극적으로 올릴 수 있습니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;STL기반으로 하였기 때문에&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;text-align: start;&quot;&gt;외부 라이브러리 의존성이 없으며,&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;text-align: start;&quot;&gt;디버깅이 쉽고, 문제를 추적하기가 쉽습니다. 구현도 쉬우며, CPU 대기 사용율도 0%입니다. 따라서 코드베이스의 복잡도와 검증 오버헤드가 비교적 적은 수준으로, 엔지니어링 리소스의 효율적 운용이 가능합니다.&lt;/span&gt;&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;짧은 벤치마크&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;BlockingVector.Queue의 1대 1에 최적화되어 있습니다. 개인적으로 1대 N 또는 N대 N이 필요한 경우, 여러 개의 1대 1을 조합하는 방식으로 사용했습니다. 이런 경우 데이터 흐름을 예측할 수 있어 상황에 따라 다양한 분산 처리 방식의 수용이 쉽기 때문입니다.&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;color: #000000; text-align: start; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;AMD 7945HX 와 i9-9900K CPU @ 3.60 GHz에서 각각 테스트되었습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;boost::lockfree::queue와 BlockingVector는 각 블럭별로 100000개의 예비 버퍼.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;블럭사이즈별로 Producer-Consumer가 1:1로 주고받은 결과입니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;블럭은 8,32,64,128,512,1024,2048,4096,8192,10240,20480의 사이즈 별로 10,000,000번 전송하였습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;구현물은 src/main.cpp에 작성되어 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;벤치마크 소스 및 표는 Claude(sonnet 3.5)를 이용하여 작성하였습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;아래는 연속 5회 실행 후 6회째 결과입니다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;i9-9900K CPU @ 3.60GHz / 32G / Rocky Linux 9.4&lt;/span&gt;&lt;/h4&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;table style=&quot;width: 100%; border-collapse: collapse;&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style=&quot;font-weight: normal; border: 1px solid #bbbbbb; padding: 8px; background-color: #f5f5f5; text-align: center; width: 12.093%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-bgcolor: var(--darkreader-background-f5f5f5, #1e2021);&quot; rowspan=&quot;2&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot; data-darkreader-inline-bgcolor=&quot;&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Block Size&lt;/span&gt;&lt;/p&gt;
&lt;/th&gt;
&lt;th style=&quot;font-weight: normal; border: 1px solid #bbbbbb; padding: 8px; background-color: #f5f5f5; text-align: center; width: 36.7442%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-bgcolor: var(--darkreader-background-f5f5f5, #1e2021);&quot; colspan=&quot;2&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot; data-darkreader-inline-bgcolor=&quot;&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Execution Time (seconds)&lt;/span&gt;&lt;/p&gt;
&lt;/th&gt;
&lt;th style=&quot;font-weight: normal; border: 1px solid #bbbbbb; padding: 8px; background-color: #f5f5f5; text-align: center; width: 34.5349%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-bgcolor: var(--darkreader-background-f5f5f5, #1e2021);&quot; colspan=&quot;2&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot; data-darkreader-inline-bgcolor=&quot;&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Throughput (ops/sec)&lt;/span&gt;&lt;/p&gt;
&lt;/th&gt;
&lt;th style=&quot;font-weight: normal; border: 1px solid #bbbbbb; padding: 8px; background-color: #f5f5f5; text-align: center; width: 16.5116%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-bgcolor: var(--darkreader-background-f5f5f5, #1e2021);&quot; rowspan=&quot;2&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot; data-darkreader-inline-bgcolor=&quot;&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f5f5f5; color: #000000; text-align: center; --darkreader-inline-bgcolor: var(--darkreader-background-f5f5f5, #1e2021); --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-bgcolor=&quot;&quot; data-darkreader-inline-color=&quot;&quot;&gt;% Faster than&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f5f5f5; color: #000000; text-align: center; --darkreader-inline-bgcolor: var(--darkreader-background-f5f5f5, #1e2021); --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-bgcolor=&quot;&quot; data-darkreader-inline-color=&quot;&quot;&gt;BoostLockFree&lt;/span&gt;&lt;/p&gt;
&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th style=&quot;font-weight: normal; border: 1px solid #bbbbbb; padding: 8px; background-color: #f5f5f5; text-align: center; width: 18.4884%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-bgcolor: var(--darkreader-background-f5f5f5, #1e2021);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot; data-darkreader-inline-bgcolor=&quot;&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Boost&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;LockFree&lt;/span&gt;&lt;/p&gt;
&lt;/th&gt;
&lt;th style=&quot;font-weight: normal; border: 1px solid #bbbbbb; padding: 8px; background-color: #f5f5f5; text-align: center; width: 18.2558%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-bgcolor: var(--darkreader-background-f5f5f5, #1e2021);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot; data-darkreader-inline-bgcolor=&quot;&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Blocking&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Vector&lt;/span&gt;&lt;/p&gt;
&lt;/th&gt;
&lt;th style=&quot;font-weight: normal; border: 1px solid #bbbbbb; padding: 8px; background-color: #f5f5f5; text-align: center; width: 17.0931%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-bgcolor: var(--darkreader-background-f5f5f5, #1e2021);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot; data-darkreader-inline-bgcolor=&quot;&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Boost&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;LockFree&lt;/span&gt;&lt;/p&gt;
&lt;/th&gt;
&lt;th style=&quot;font-weight: normal; border: 1px solid #bbbbbb; padding: 8px; background-color: #f5f5f5; text-align: center; width: 17.4418%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-bgcolor: var(--darkreader-background-f5f5f5, #1e2021);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot; data-darkreader-inline-bgcolor=&quot;&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Blocking&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Vector&lt;/span&gt;&lt;/p&gt;
&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 12.093%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;8 bytes&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 18.4884%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;1.500&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 18.2558%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;0.402&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 17.0931%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;6.66M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 17.4418%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;24.89M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 16.5116%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;273.41%&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 12.093%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;32 bytes&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 18.4884%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;1.501&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 18.2558%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;0.402&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 17.0931%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;6.66M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 17.4418%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;24.85M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 16.5116%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;272.95%&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 12.093%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;64 bytes&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 18.4884%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;1.547&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 18.2558%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;0.417&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 17.0931%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;6.47M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 17.4418%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;24.00M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 16.5116%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;271.14%&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 12.093%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;128 bytes&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 18.4884%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;1.557&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 18.2558%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;0.452&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 17.0931%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;6.42M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 17.4418%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;22.12M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 16.5116%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;244.48%&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 12.093%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;512 bytes&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 18.4884%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;1.767&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 18.2558%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;0.945&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 17.0931%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;5.66M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 17.4418%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;10.58M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 16.5116%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;87.02%&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 12.093%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;1024 bytes&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 18.4884%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;2.049&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 18.2558%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;1.123&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 17.0931%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;4.88M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 17.4418%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;8.91M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 16.5116%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;82.51%&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 12.093%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;2048 bytes&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 18.4884%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;2.418&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 18.2558%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;1.898&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 17.0931%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;4.13M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 17.4418%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;5.27M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 16.5116%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;27.43%&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 12.093%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;4096 bytes&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 18.4884%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;3.195&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 18.2558%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;2.630&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 17.0931%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;3.13M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 17.4418%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;3.80M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 16.5116%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;21.45%&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 12.093%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;8192 bytes&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 18.4884%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;5.519&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 18.2558%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;3.499&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 17.0931%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;1.81M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 17.4418%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;2.86M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 16.5116%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;57.73%&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 12.093%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;10240 bytes&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 18.4884%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;6.463&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 18.2558%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;3.744&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 17.0931%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;1.55M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 17.4418%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;2.67M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 16.5116%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;72.64%&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 12.093%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;20480 bytes&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 18.4884%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;14.012&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 18.2558%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;7.395&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 17.0931%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;0.71M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 17.4418%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;1.35M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 16.5116%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;89.48%&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;AMD 7945HX / 32G / Linux Mint 21.3(Ubuntu 22.04), Last updated 05.23.2025&lt;/span&gt;&lt;/h4&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;table style=&quot;width: 100%; border-collapse: collapse;&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style=&quot;font-weight: normal; border: 1px solid #bbbbbb; padding: 8px; background-color: #f5f5f5; text-align: center; width: 12.093%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-bgcolor: var(--darkreader-background-f5f5f5, #1e2021);&quot; rowspan=&quot;2&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot; data-darkreader-inline-bgcolor=&quot;&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Block Size&lt;/span&gt;&lt;/p&gt;
&lt;/th&gt;
&lt;th style=&quot;font-weight: normal; border: 1px solid #bbbbbb; padding: 8px; background-color: #f5f5f5; text-align: center; width: 36.7442%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-bgcolor: var(--darkreader-background-f5f5f5, #1e2021);&quot; colspan=&quot;2&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot; data-darkreader-inline-bgcolor=&quot;&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Execution Time (seconds)&lt;/span&gt;&lt;/p&gt;
&lt;/th&gt;
&lt;th style=&quot;font-weight: normal; border: 1px solid #bbbbbb; padding: 8px; background-color: #f5f5f5; text-align: center; width: 34.4186%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-bgcolor: var(--darkreader-background-f5f5f5, #1e2021);&quot; colspan=&quot;2&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot; data-darkreader-inline-bgcolor=&quot;&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Throughput (ops/sec)&lt;/span&gt;&lt;/p&gt;
&lt;/th&gt;
&lt;th style=&quot;font-weight: normal; border: 1px solid #bbbbbb; padding: 8px; background-color: #f5f5f5; text-align: center; width: 16.6279%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-bgcolor: var(--darkreader-background-f5f5f5, #1e2021);&quot; rowspan=&quot;2&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot; data-darkreader-inline-bgcolor=&quot;&quot;&gt;
&lt;p style=&quot;color: #333333; text-align: center; --darkreader-inline-color: var(--darkreader-text-333333, #c8c3bc);&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;background-color: #f5f5f5; color: #000000; text-align: center; --darkreader-inline-bgcolor: var(--darkreader-background-f5f5f5, #1e2021); --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-bgcolor=&quot;&quot; data-darkreader-inline-color=&quot;&quot;&gt;% Faster than&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: center; --darkreader-inline-color: var(--darkreader-text-333333, #c8c3bc);&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;background-color: #f5f5f5; color: #000000; text-align: center; --darkreader-inline-bgcolor: var(--darkreader-background-f5f5f5, #1e2021); --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-bgcolor=&quot;&quot; data-darkreader-inline-color=&quot;&quot;&gt;BoostLockFree&lt;/span&gt;&lt;/p&gt;
&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th style=&quot;font-weight: normal; border: 1px solid #bbbbbb; padding: 8px; background-color: #f5f5f5; text-align: center; width: 18.4884%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-bgcolor: var(--darkreader-background-f5f5f5, #1e2021);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot; data-darkreader-inline-bgcolor=&quot;&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Boost&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;LockFree&lt;/span&gt;&lt;/p&gt;
&lt;/th&gt;
&lt;th style=&quot;font-weight: normal; border: 1px solid #bbbbbb; padding: 8px; background-color: #f5f5f5; text-align: center; width: 18.2558%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-bgcolor: var(--darkreader-background-f5f5f5, #1e2021);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot; data-darkreader-inline-bgcolor=&quot;&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Blocking&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Vector&lt;/span&gt;&lt;/p&gt;
&lt;/th&gt;
&lt;th style=&quot;font-weight: normal; border: 1px solid #bbbbbb; padding: 8px; background-color: #f5f5f5; text-align: center; width: 17.093%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-bgcolor: var(--darkreader-background-f5f5f5, #1e2021);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot; data-darkreader-inline-bgcolor=&quot;&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Boost&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;LockFree&lt;/span&gt;&lt;/p&gt;
&lt;/th&gt;
&lt;th style=&quot;font-weight: normal; border: 1px solid #bbbbbb; padding: 8px; background-color: #f5f5f5; text-align: center; width: 17.3256%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-bgcolor: var(--darkreader-background-f5f5f5, #1e2021);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot; data-darkreader-inline-bgcolor=&quot;&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Blocking&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Vector&lt;/span&gt;&lt;/p&gt;
&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 12.093%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;8 bytes&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 18.4884%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;3.071&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 18.2558%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;0.218&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 17.093%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;3.26M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 17.3256%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;45.90M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 16.6279%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;1309.45%&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 12.093%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;32 bytes&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 18.4884%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;3.045&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 18.2558%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;0.235&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 17.093%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;3.28M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 17.3256%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;42.52M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 16.6279%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;1194.73%&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 12.093%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;64 bytes&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 18.4884%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;3.105&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 18.2558%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;0.256&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 17.093%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;3.22M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 17.3256%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;39.06M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 16.6279%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;1112.90%&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 12.093%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;128 bytes&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 18.4884%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;3.112&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 18.2558%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;0.268&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 17.093%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;3.21M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 17.3256%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;37.34M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 16.6279%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;1062.03%&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 12.093%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;512 bytes&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 18.4884%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;3.509&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 18.2558%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;0.660&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 17.093%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;2.85M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 17.3256%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;15.14M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 16.6279%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;431.41%&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 12.093%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;1024 bytes&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 18.4884%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;3.800&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 18.2558%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;0.733&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 17.093%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;2.63M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 17.3256%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;13.65M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 16.6279%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;418.74%&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 12.093%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;2048 bytes&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 18.4884%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;5.165&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 18.2558%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;1.174&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 17.093%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;1.94M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 17.3256%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;8.51M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 16.6279%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;339.73%&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 12.093%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;4096 bytes&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 18.4884%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;8.417&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 18.2558%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;1.567&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 17.093%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;1.19M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 17.3256%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;6.38M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 16.6279%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;437.21%&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 12.093%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;8192 bytes&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 18.4884%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;12.478&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 18.2558%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;2.607&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 17.093%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;0.80M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 17.3256%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;3.84M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 16.6279%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;378.64%&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 12.093%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;10240 bytes&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 18.4884%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;13.921&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 18.2558%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;3.408&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 17.093%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;0.72M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 17.3256%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;2.93M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 16.6279%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;308.49%&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 12.093%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;20480 bytes&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 18.4884%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;22.029&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 18.2558%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;5.672&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 17.093%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;0.45M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 17.3256%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;1.76M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; width: 16.6279%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;288.36%&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;AMD 7945HX / 32G / Linux Mint 21.3(Ubuntu 22.04). include moodycamel::ConcurrentQueue&lt;/span&gt;&lt;/h4&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;table style=&quot;width: 100%; border-collapse: collapse;&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style=&quot;font-weight: normal; border: 1px solid #bbbbbb; padding: 8px; background-color: #f5f5f5; text-align: center; width: 12.093%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-bgcolor: var(--darkreader-background-f5f5f5, #1e2021);&quot; rowspan=&quot;2&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot; data-darkreader-inline-bgcolor=&quot;&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Block Size&lt;/span&gt;&lt;/p&gt;
&lt;/th&gt;
&lt;th style=&quot;font-weight: normal; border: 1px solid #bbbbbb; padding: 8px; background-color: #f5f5f5; text-align: center; width: 27.5582%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-bgcolor: var(--darkreader-background-f5f5f5, #1e2021);&quot; colspan=&quot;3&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot; data-darkreader-inline-bgcolor=&quot;&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Execution Time (seconds)&lt;/span&gt;&lt;/p&gt;
&lt;/th&gt;
&lt;th style=&quot;font-weight: normal; border: 1px solid #bbbbbb; padding: 8px; background-color: #f5f5f5; text-align: center; width: 32.7908%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-bgcolor: var(--darkreader-background-f5f5f5, #1e2021);&quot; colspan=&quot;3&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot; data-darkreader-inline-bgcolor=&quot;&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Throughput (ops/sec)&lt;/span&gt;&lt;/p&gt;
&lt;/th&gt;
&lt;th style=&quot;font-weight: normal; border: 1px solid #bbbbbb; padding: 8px; background-color: #f5f5f5; text-align: center; width: 27.4419%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-bgcolor: var(--darkreader-background-f5f5f5, #1e2021);&quot; colspan=&quot;2&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot; data-darkreader-inline-bgcolor=&quot;&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;% Faster than BoostLockFree&lt;/span&gt;&lt;/p&gt;
&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th style=&quot;font-weight: normal; border: 1px solid #bbbbbb; padding: 8px; background-color: #f5f5f5; text-align: center; width: 10.2326%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-bgcolor: var(--darkreader-background-f5f5f5, #1e2021);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot; data-darkreader-inline-bgcolor=&quot;&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Boost&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;LockFree&lt;/span&gt;&lt;/p&gt;
&lt;/th&gt;
&lt;th style=&quot;font-weight: normal; border: 1px solid #bbbbbb; padding: 8px; background-color: #f5f5f5; text-align: center; width: 7.90698%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-bgcolor: var(--darkreader-background-f5f5f5, #1e2021);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot; data-darkreader-inline-bgcolor=&quot;&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Moody&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Camel&lt;/span&gt;&lt;/p&gt;
&lt;/th&gt;
&lt;th style=&quot;font-weight: normal; border: 1px solid #bbbbbb; padding: 8px; background-color: #f5f5f5; text-align: center; width: 9.4186%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-bgcolor: var(--darkreader-background-f5f5f5, #1e2021);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot; data-darkreader-inline-bgcolor=&quot;&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Blocking&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Vector&lt;/span&gt;&lt;/p&gt;
&lt;/th&gt;
&lt;th style=&quot;font-weight: normal; border: 1px solid #bbbbbb; padding: 8px; background-color: #f5f5f5; text-align: center; width: 10.2326%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-bgcolor: var(--darkreader-background-f5f5f5, #1e2021);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot; data-darkreader-inline-bgcolor=&quot;&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Boost&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;LockFree&lt;/span&gt;&lt;/p&gt;
&lt;/th&gt;
&lt;th style=&quot;font-weight: normal; border: 1px solid #bbbbbb; padding: 8px; background-color: #f5f5f5; text-align: center; width: 11.2791%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-bgcolor: var(--darkreader-background-f5f5f5, #1e2021);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot; data-darkreader-inline-bgcolor=&quot;&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Moody&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Camel&lt;/span&gt;&lt;/p&gt;
&lt;/th&gt;
&lt;th style=&quot;font-weight: normal; border: 1px solid #bbbbbb; padding: 8px; background-color: #f5f5f5; text-align: center; width: 11.2791%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-bgcolor: var(--darkreader-background-f5f5f5, #1e2021);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot; data-darkreader-inline-bgcolor=&quot;&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Blocking&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Vector&lt;/span&gt;&lt;/p&gt;
&lt;/th&gt;
&lt;th style=&quot;font-weight: normal; border: 1px solid #bbbbbb; padding: 8px; background-color: #f5f5f5; text-align: center; width: 13.4884%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-bgcolor: var(--darkreader-background-f5f5f5, #1e2021);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot; data-darkreader-inline-bgcolor=&quot;&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Moody&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Camel&lt;/span&gt;&lt;/p&gt;
&lt;/th&gt;
&lt;th style=&quot;font-weight: normal; border: 1px solid #bbbbbb; padding: 8px; background-color: #f5f5f5; text-align: center; width: 13.9535%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-bgcolor: var(--darkreader-background-f5f5f5, #1e2021);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot; data-darkreader-inline-bgcolor=&quot;&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Blocking&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Vector&lt;/span&gt;&lt;/p&gt;
&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;8 bytes&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;2.912&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;0.222&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;0.175&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;3.43M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;45.12M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;57.23M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;1214.16%&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;1566.73%&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;16 bytes&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;2.898&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;0.245&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;0.222&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;3.45M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;40.86M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;44.97M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;1083.86%&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;1203.11%&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;32 bytes&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;3.074&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;0.300&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;0.220&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;3.25M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;33.38M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;45.38M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;926.21%&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;1295.12%&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;64 bytes&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;3.093&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;0.399&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;0.218&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;3.23M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;25.08M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;45.82M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;675.66%&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;1317.20%&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;128 bytes&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;3.056&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;0.664&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;0.232&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;3.27M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;15.07M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;43.08M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;360.58%&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;1216.81%&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;256 bytes&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;3.089&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;1.026&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;0.298&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;3.24M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;9.74M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;33.53M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;200.95%&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;935.59%&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;512 bytes&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;3.443&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;1.252&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;0.623&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;2.90M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;7.99M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;16.06M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;175.06%&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;453.10%&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;1024 bytes&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;3.687&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;2.415&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;0.796&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;2.71M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;4.14M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;12.56M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;52.68%&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;362.88%&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;2048 bytes&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;5.244&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;2.707&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;1.160&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;1.91M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;3.69M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;8.62M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;93.69%&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;352.11%&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;4096 bytes&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;7.872&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;5.138&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;1.598&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;1.27M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;1.95M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;6.26M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;53.19%&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;392.52%&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;8192 bytes&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;12.436&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;9.147&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;2.176&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;0.80M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;1.09M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;4.60M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;35.95%&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;471.46%&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;h4 style=&quot;color: #000000; text-align: start; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-ke-size=&quot;size20&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;AMD 8845HS / 16G / Linux Mint 22.1(Ubuntu 24.04). include&amp;nbsp;moodycamel::ConcurrentQueue&lt;/span&gt;&lt;/h4&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;table style=&quot;width: 100%; border-collapse: collapse;&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style=&quot;font-weight: normal; border: 1px solid #bbbbbb; padding: 8px; background-color: #f5f5f5; text-align: center; width: 12.093%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-bgcolor: var(--darkreader-background-f5f5f5, #1e2021);&quot; rowspan=&quot;2&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot; data-darkreader-inline-bgcolor=&quot;&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Block Size&lt;/span&gt;&lt;/p&gt;
&lt;/th&gt;
&lt;th style=&quot;font-weight: normal; border: 1px solid #bbbbbb; padding: 8px; background-color: #f5f5f5; text-align: center; width: 27.5582%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-bgcolor: var(--darkreader-background-f5f5f5, #1e2021);&quot; colspan=&quot;3&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot; data-darkreader-inline-bgcolor=&quot;&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Execution Time (seconds)&lt;/span&gt;&lt;/p&gt;
&lt;/th&gt;
&lt;th style=&quot;font-weight: normal; border: 1px solid #bbbbbb; padding: 8px; background-color: #f5f5f5; text-align: center; width: 32.7908%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-bgcolor: var(--darkreader-background-f5f5f5, #1e2021);&quot; colspan=&quot;3&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot; data-darkreader-inline-bgcolor=&quot;&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Throughput (ops/sec)&lt;/span&gt;&lt;/p&gt;
&lt;/th&gt;
&lt;th style=&quot;font-weight: normal; border: 1px solid #bbbbbb; padding: 8px; background-color: #f5f5f5; text-align: center; width: 27.4419%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-bgcolor: var(--darkreader-background-f5f5f5, #1e2021);&quot; colspan=&quot;2&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot; data-darkreader-inline-bgcolor=&quot;&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;% Faster than BoostLockFree&lt;/span&gt;&lt;/p&gt;
&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th style=&quot;font-weight: normal; border: 1px solid #bbbbbb; padding: 8px; background-color: #f5f5f5; text-align: center; width: 10.2326%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-bgcolor: var(--darkreader-background-f5f5f5, #1e2021);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot; data-darkreader-inline-bgcolor=&quot;&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Boost&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;LockFree&lt;/span&gt;&lt;/p&gt;
&lt;/th&gt;
&lt;th style=&quot;font-weight: normal; border: 1px solid #bbbbbb; padding: 8px; background-color: #f5f5f5; text-align: center; width: 7.90698%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-bgcolor: var(--darkreader-background-f5f5f5, #1e2021);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot; data-darkreader-inline-bgcolor=&quot;&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Moody&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Camel&lt;/span&gt;&lt;/p&gt;
&lt;/th&gt;
&lt;th style=&quot;font-weight: normal; border: 1px solid #bbbbbb; padding: 8px; background-color: #f5f5f5; text-align: center; width: 9.4186%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-bgcolor: var(--darkreader-background-f5f5f5, #1e2021);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot; data-darkreader-inline-bgcolor=&quot;&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Blocking&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Vector&lt;/span&gt;&lt;/p&gt;
&lt;/th&gt;
&lt;th style=&quot;font-weight: normal; border: 1px solid #bbbbbb; padding: 8px; background-color: #f5f5f5; text-align: center; width: 10.2326%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-bgcolor: var(--darkreader-background-f5f5f5, #1e2021);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot; data-darkreader-inline-bgcolor=&quot;&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Boost&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;LockFree&lt;/span&gt;&lt;/p&gt;
&lt;/th&gt;
&lt;th style=&quot;font-weight: normal; border: 1px solid #bbbbbb; padding: 8px; background-color: #f5f5f5; text-align: center; width: 11.2791%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-bgcolor: var(--darkreader-background-f5f5f5, #1e2021);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot; data-darkreader-inline-bgcolor=&quot;&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Moody&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Camel&lt;/span&gt;&lt;/p&gt;
&lt;/th&gt;
&lt;th style=&quot;font-weight: normal; border: 1px solid #bbbbbb; padding: 8px; background-color: #f5f5f5; text-align: center; width: 11.2791%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-bgcolor: var(--darkreader-background-f5f5f5, #1e2021);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot; data-darkreader-inline-bgcolor=&quot;&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Blocking&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Vector&lt;/span&gt;&lt;/p&gt;
&lt;/th&gt;
&lt;th style=&quot;font-weight: normal; border: 1px solid #bbbbbb; padding: 8px; background-color: #f5f5f5; text-align: center; width: 13.4884%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-bgcolor: var(--darkreader-background-f5f5f5, #1e2021);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot; data-darkreader-inline-bgcolor=&quot;&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Moody&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Camel&lt;/span&gt;&lt;/p&gt;
&lt;/th&gt;
&lt;th style=&quot;font-weight: normal; border: 1px solid #bbbbbb; padding: 8px; background-color: #f5f5f5; text-align: center; width: 13.9535%; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-bgcolor: var(--darkreader-background-f5f5f5, #1e2021);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot; data-darkreader-inline-bgcolor=&quot;&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Blocking&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;Vector&lt;/span&gt;&lt;/p&gt;
&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;8 bytes&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;1.390&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;0.211&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;0.197&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;7.19M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;47.50M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;50.88M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;560.41%&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;607.41%&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;16 bytes&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;1.372&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;0.218&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;0.192&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;7.29M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;45.86M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;52.18M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;529.18%&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;616.00%&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;32 bytes&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;1.330&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;0.285&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;0.214&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;7.52M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;35.08M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;46.83M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;366.57%&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;522.82%&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;64 bytes&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;1.372&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;0.387&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;0.172&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;7.29M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;25.82M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;58.05M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;254.33%&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;696.47%&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;128 bytes&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;1.360&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;0.653&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;0.202&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;7.35M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;15.31M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;49.40M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;108.19%&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;571.93%&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;256 bytes&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;1.432&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;0.892&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;0.249&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;6.98M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;11.21M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;40.10M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;60.60%&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;474.45%&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;512 bytes&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;1.481&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;1.073&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;0.490&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;6.75M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;9.32M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;20.42M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;38.00%&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;202.37%&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;1024 bytes&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;1.691&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;1.507&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;0.607&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;5.91M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;6.64M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;16.48M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;12.24%&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;178.83%&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;2048 bytes&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;2.337&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;2.341&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;0.794&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;4.28M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;4.27M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;12.59M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;-0.14%&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;194.30%&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;4096 bytes&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;5.286&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;3.565&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;1.080&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;1.89M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;2.81M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;9.26M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;48.29%&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;389.62%&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;8192 bytes&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;10.973&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;7.191&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;1.580&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;0.91M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;1.39M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;6.33M&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;52.59%&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #bbbbbb; padding: 8px; text-align: right; --darkreader-inline-border-top: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-right: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-bottom: var(--darkreader-border-bbbbbb, #43494c); --darkreader-inline-border-left: var(--darkreader-border-bbbbbb, #43494c);&quot; data-darkreader-inline-border-top=&quot;&quot; data-darkreader-inline-border-right=&quot;&quot; data-darkreader-inline-border-bottom=&quot;&quot; data-darkreader-inline-border-left=&quot;&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;594.30%&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;결론&lt;/span&gt;&lt;/h4&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;'락프리'라는 단어가 주는 매력적인 이미지와 실제 구현의 복잡성 사이에는 큰 간극이 있습니다. '락프리'라는 용어는 마치 모든 경합과 블로킹에서 자유로울 것 같은 인상을 줍니다. 하지만 실제로는 단지 경합의 형태가 바뀌었을 뿐입니다. 뮤텍스나 락을 사용할 때의 명시적인 대기 대신, CAS 연산의 실패와 재시도라는 형태로 경합이 나타나는 것이죠.&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;/div&gt;
&lt;div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&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;/div&gt;
&lt;div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;락프리에 열광하는 이유는 아마도 '이상'과 '현실'의 차이를 충분히 인식하지 못하기 때문일 것입니다. 앞서 본 벤치마크에서처럼, 때로는 더 단순한 블로킹 구현이 더 나은 성능을 보여주기도 합니다. 이는 소프트웨어 엔지니어링에서 자주 마주치는 '단순함 vs 복잡함'의 또 다른 예라고 볼 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR'; text-align: start; font-size: 1.12em; letter-spacing: 0px; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;때로는 단순한 해결책이 더 나은 선택일 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&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;&lt;span style=&quot;color: #000000; --darkreader-inline-color: var(--darkreader-text-000000, #e8e6e3);&quot; data-darkreader-inline-color=&quot;&quot;&gt;해당 코드는 아래 링크에서 볼 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;figure id=&quot;og_1748093414314&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 - ty7swkr/BlockingQueue-based-on-vector: High-performance BlockingQueue based on std::vector in c++11&quot; data-og-description=&quot;High-performance BlockingQueue based on std::vector in c++11 - ty7swkr/BlockingQueue-based-on-vector&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/ty7swkr/BlockingQueue-based-on-vector&quot; data-og-url=&quot;https://github.com/ty7swkr/BlockingQueue-based-on-vector&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/WCH6k/hyYYuOuuOl/PkeB97MdrVJMnJrJWrBAuK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/bFOcql/hyYYBmzbF9/PwvjgRXENWT2axKfKYufKK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/ty7swkr/BlockingQueue-based-on-vector&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/ty7swkr/BlockingQueue-based-on-vector&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/WCH6k/hyYYuOuuOl/PkeB97MdrVJMnJrJWrBAuK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/bFOcql/hyYYBmzbF9/PwvjgRXENWT2axKfKYufKK/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 - ty7swkr/BlockingQueue-based-on-vector: High-performance BlockingQueue based on std::vector in c++11&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;High-performance BlockingQueue based on std::vector in c++11 - ty7swkr/BlockingQueue-based-on-vector&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;/div&gt;
&lt;div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;</description>
      <category>blockingqueue</category>
      <category>boost</category>
      <category>C++</category>
      <category>cpp</category>
      <category>lockfree</category>
      <category>lockfreequeue</category>
      <category>producer-consumer</category>
      <category>STL</category>
      <author>tys</author>
      <guid isPermaLink="true">https://cplusplus.tistory.com/4</guid>
      <comments>https://cplusplus.tistory.com/entry/C-Lockfree-Queue%EA%B0%80-%ED%95%AD%EC%83%81-%EC%A2%8B%EC%9D%80-%EC%84%A0%ED%83%9D%EC%9D%BC%EA%B9%8C#entry4comment</comments>
      <pubDate>Sun, 15 Dec 2024 20:22:04 +0900</pubDate>
    </item>
  </channel>
</rss>