<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Cs on Hammer's Blog</title><link>https://lkic1625.github.io/blog/cs/</link><description>Recent content in Cs on Hammer's Blog</description><generator>Hugo</generator><language>ko-kr</language><copyright>© 조원빈 - All rights reserved</copyright><lastBuildDate>Fri, 02 Oct 2026 11:51:54 +0900</lastBuildDate><atom:link href="https://lkic1625.github.io/blog/cs/index.xml" rel="self" type="application/rss+xml"/><item><title>EEVDF Scheduling</title><link>https://lkic1625.github.io/eevdf-scheduling/</link><pubDate>Fri, 02 Oct 2026 11:51:54 +0900</pubDate><guid>https://lkic1625.github.io/eevdf-scheduling/</guid><description>&lt;h2 id="개요">개요&lt;/h2>
&lt;p>이건 단순 공부용 정리이다. EEVDF는 &lt;strong>Earliest Eligible Virtual Deadline First&lt;/strong>의 약자이다. 이름 그대로, 실행 자격이 있는 스레드 중에서 virtual deadline이 가장 이른 스레드를 선택한다.
&lt;a href="https://lkic1625.github.io/os-process/">4. The Process&lt;/a>에서 CPU별 rq와 cfs_rq의 내부구조를 살펴봤었다. 여기서는 그중 Fairness 수준에서 경쟁하는 스레드들을 어떤 기준으로 선택하는지 알아보자. 설명은 하나의 CPU에서 같은 작업 큐를 두고 경쟁하는 스레드들을 기준으로 단순화한다. 다만, 이는 실제 Linux에서 실행 중인 스레드의 짧은 실행 구간을 보호하는 처리 등도 있으므로, 완벽한 로직이라고 판단하면 안된다.&lt;/p></description></item><item><title>Go Runtime Internals — 작성 중</title><link>https://lkic1625.github.io/go-runtime-internals/</link><pubDate>Wed, 30 Sep 2026 17:41:37 +0900</pubDate><guid>https://lkic1625.github.io/go-runtime-internals/</guid><description>&lt;blockquote>
&lt;p>&lt;strong>작성 중인 글입니다.&lt;/strong> Goroutine Stack까지 정리했으며, M·P·schedt·netpoller·sysmon 설명은 이어서 작성할 예정입니다.&lt;/p>&lt;/blockquote>
&lt;h2 id="개요">개요&lt;/h2>
&lt;p>고루틴을 설명하라고 하면, 반드시 나오는 이야기가 있다. GMP 구조이다. 고루틴의 위대한 업적 중 하나를 뽑자면, 어려운 경량 스레드 모델링에 대해 가장 직관적이고 명료한 모델링이라고 생각한다. 다만, GMP 모델을 아는 것과 이를 완전히 이해하는 것은 서로 다른 문제이다. 아래 질문에 대해서 답변할 수 있는가?&lt;/p>
&lt;ul>
&lt;li>왜 &lt;code>G → OS Thread&lt;/code>가 아니라 &lt;strong>G-M-P&lt;/strong>일지&lt;/li>
&lt;li>왜 &lt;code>GOMAXPROCS&lt;/code>를 통해 &lt;strong>P 개수&lt;/strong>를 제한해야하는지, 그리고 그 개수는 왜 코어 개수인지.&lt;/li>
&lt;li>go runtime은 어떻게 &lt;strong>필요한 M만 유지하면서 runnable G를 P에 공급하는지&lt;/strong>&lt;/li>
&lt;li>network I/O 시에 고루틴은 어떻게 동작하는지
이 글은 이 질문에 답변하기 위한 글이다.&lt;/li>
&lt;/ul>
&lt;h2 id="topology">Topology&lt;/h2>
&lt;h3 id="g---goroutine">G - Goroutine&lt;/h3>
&lt;h4 id="goroutine-stack">Goroutine Stack&lt;/h4>
&lt;p>우리가 가장 잘 아는 고루틴이다. stackful한 구조를 통해, 자신이 언제 어디서 멈췄는지를 기억하는, 독립적인 Call Stack을 가지고 있다. 참고로, 보통 코루틴을 나누는 구조 중에 하나는 stackful, stackless이다. stackless는 간단하게 설명하자면, 명시적인 구절을 통해 실제 컴파일 단에서 suspension 지점을 지정하고 상태 기계를 통해 관리하는 방식이다.(물론, 호출자의 스택을 이용해 최소한의 정보만을 저장을 한다는 의미이다.)
코루틴의 대한 다양한 논의와 코루틴 이론에 대해서는 몇 가지 글을 참고할 수 있겠으나, 이 글에서는 다루지 않겠다. [1], [2]&lt;/p></description></item><item><title>SIMD와 Vectorized Execution</title><link>https://lkic1625.github.io/simd-vectorized-execution/</link><pubDate>Fri, 18 Sep 2026 13:41:12 +0900</pubDate><guid>https://lkic1625.github.io/simd-vectorized-execution/</guid><description>&lt;h2 id="introduction">Introduction&lt;/h2>
&lt;p>SIMD란 &lt;strong>Single Instruction, Multiple Data&lt;/strong>의 줄임말이다. 하나의 연산을 동시에 작업하는 방식이다. 이러한 연산 방식은 현재 데이터베이스 시스템에서도 매우 유용하게 사용될 수 있다. 이에 대해서 알아보자.&lt;/p>
&lt;h2 id="simd">SIMD&lt;/h2>
&lt;p>SIMD 연산은 아주 기본적인 출발점부터 시작한다. 예를 들어 간단한 스칼라 연산을 여러번 반복하는 loop가 있다면 해당 loop는 당연하게도 해당 데이터를 가져오고, 비교하는 연산을 수행한다. 하지만 만약, AVX2의 256-bit register에 &lt;code>int32&lt;/code>를 넣을 시에는 8개의 데이터를 동시에 연산할 수 있을 것이다. 이것이 SIMD 연산의 개괄이다. 물론, 실제로는 8배로 빨라지진 않는다. 데이터를 DRAM에서 가져오고 처리하는 데에 드는 추가적인 병목이 있기 때문이다.&lt;/p></description></item><item><title>Cache Affinity와 Seastar</title><link>https://lkic1625.github.io/cache-affinity/</link><pubDate>Fri, 18 Sep 2026 13:40:59 +0900</pubDate><guid>https://lkic1625.github.io/cache-affinity/</guid><description>&lt;h2 id="intro">Intro&lt;/h2>
&lt;p>Cache Affinity란, 기본적으로 스케쥴링 영역에 해당하는 동작성이다. 이에 대한 정리를 practical 한 예시와 함께 정리해본다.&lt;/p>
&lt;h2 id="cache-affinity">Cache Affinity&lt;/h2>
&lt;p>CPU는 대략적으로 L1, L2, L3, DRAM으로 이루어지며 각 레벨에서의 연산은 기본적으로 단계를 넘어갈수록 매우커진다. 특히, DRAM에 있는 값을 가져오는 연산은 수백배 차이날 수 있다. 그렇기에 이러한 하드웨어 동작에서 중요한 것은 CPU cache에 실제로 해당 값이 있는지를 기반으로 동작의 판가름이 난다. 대략적인 지연시간(latency)는 아래와 같다.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-text" data-lang="text">&lt;span style="display:flex;">&lt;span>Register ~ 1 cycle
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>L1 ~ 4 cycles
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>L2 ~ 10~20 cycles
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>L3 ~ 수십 cycles
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>DRAM ~ 수백 cycles
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>기본적으로 이러한 캐시 친화성은 CPU 스케쥴링의 영역지만, 더 나아가 현대의 소프트웨어 설계 기법에서도 볼 수 있다.&lt;/p></description></item></channel></rss>