PageSourceSearch

https://sgs4ever.github.io/p/cppcon25pratical-data-oriented-design-in-c-/

html sgs4ever.github.io collected 2026-10-03 09:56:03 UTC 121,623 bytes, 1,333 lines download raw bytes

1<!DOCTYPE html>
2<html lang="en-us">
3    <head><meta charset='utf-8'>
4<meta name='viewport' content='width=device-width, initial-scale=1'><meta name='description' content='今天看的演讲是关于数据驱动设计( Data-oriented Design, DoD )的,真令我大开眼界。这个设计思路的性能收益很高,可读性也很好,实在应该找个机会狠狠落实到工作中。
5Vittorio的演讲风格挺干练的,talk is cheap,全程围绕一个小Demo来演绎如何通过DoD实现巨大的性能优化。
61 一个简单的Demo Demo是一个简单的场景模拟,要求在屏幕上飞过一些 可以喷射粒子 的 火箭 ,并为了更加贴近实际,附加了几点需求:
7运动模拟:火箭需要模拟出位置、速度、加速度等 效果:不同类型的粒子和发射器,支持自定义参数(透明度、大小、旋转角等) 实体:为了便于控制单个实体,理论上可以建模为例如火箭&#43;发射器 扩展性:便于增加新的实体或效果 一个简单的建模会是这样的(其中深蓝色为实体类,浅蓝色为抽象类):
8场景中所有的对象都是一个 实体 Entity 发射器和粒子可以有不同类型,如火焰发射器&amp;烟雾发射器,对应火焰粒子&amp;烟雾粒子 对象的实现可能是这样的(结合个人理解,对声明顺序进行了调整,以便呈现思路,其余代码细节均与演讲保持一致):
9struct Entity { // 1 首先实体在我们的设计中是一个抽象类,通常需要虚析构函数 virtual ~Entity() = default; // 2 实体需要遵循物理规律,因此提供一些物理属性(位置、速度、加速度) sf::Vector2f position; sf::Vector2f velocity; sf::Vector2f acceleration; // 3 实体需要更新和渲染 virtual void update(float dt) { position &#43;= velocity * dt; velocity &#43;= acceleration * dt; } virtual void draw(sf::RenderTarget&amp;); // 4 实体需要感知别的实体,因此会引用世界对象,同时,告诉世界自身是否还存活 World&amp; world; bool alive = true; }; 粒子的实现,在实体基础上增加了大小、透明度和旋转角,同时当透明度降为 0 时,会自动销毁:
10'><title>【cppcon25】Pratical Data-Oriented Design in C&#43;&#43;</title>
11
12<link rel='canonical' href='https://xrg.fj.cn/p/cppcon25pratical-data-oriented-design-in-c-/'>
13
14<link rel="stylesheet" href="/scss/style.min.744aa3042babb8291bcb36af694eb272f909f9b1e9c66387395b00d22da84726.css">
14<script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
14
15
16<script>
17    
18    $(window).scroll(function() {
19    if ($(this).scrollTop()) {
20        $('#back-to-top').fadeIn();
21    } else {
22        $('#back-to-top').fadeOut();
23    }
24    });
25
26    
27    $('#back-to-top').click(function() {
28    $('html, body').animate({scrollTop: 0}, 1000);
29    return false;
30    });
31</script>
31
32
33<meta property='og:title' content='【cppcon25】Pratical Data-Oriented Design in C&#43;&#43;'>
34<meta property='og:description' content='今天看的演讲是关于数据驱动设计( Data-oriented Design, DoD )的,真令我大开眼界。这个设计思路的性能收益很高,可读性也很好,实在应该找个机会狠狠落实到工作中。
35Vittorio的演讲风格挺干练的,talk is cheap,全程围绕一个小Demo来演绎如何通过DoD实现巨大的性能优化。
361 一个简单的Demo Demo是一个简单的场景模拟,要求在屏幕上飞过一些 可以喷射粒子 的 火箭 ,并为了更加贴近实际,附加了几点需求:
37运动模拟:火箭需要模拟出位置、速度、加速度等 效果:不同类型的粒子和发射器,支持自定义参数(透明度、大小、旋转角等) 实体:为了便于控制单个实体,理论上可以建模为例如火箭&#43;发射器 扩展性:便于增加新的实体或效果 一个简单的建模会是这样的(其中深蓝色为实体类,浅蓝色为抽象类):
38场景中所有的对象都是一个 实体 Entity 发射器和粒子可以有不同类型,如火焰发射器&amp;烟雾发射器,对应火焰粒子&amp;烟雾粒子 对象的实现可能是这样的(结合个人理解,对声明顺序进行了调整,以便呈现思路,其余代码细节均与演讲保持一致):
39struct Entity { // 1 首先实体在我们的设计中是一个抽象类,通常需要虚析构函数 virtual ~Entity() = default; // 2 实体需要遵循物理规律,因此提供一些物理属性(位置、速度、加速度) sf::Vector2f position; sf::Vector2f velocity; sf::Vector2f acceleration; // 3 实体需要更新和渲染 virtual void update(float dt) { position &#43;= velocity * dt; velocity &#43;= acceleration * dt; } virtual void draw(sf::RenderTarget&amp;); // 4 实体需要感知别的实体,因此会引用世界对象,同时,告诉世界自身是否还存活 World&amp; world; bool alive = true; }; 粒子的实现,在实体基础上增加了大小、透明度和旋转角,同时当透明度降为 0 时,会自动销毁:
40'>
41<meta property='og:url' content='https://xrg.fj.cn/p/cppcon25pratical-data-oriented-design-in-c-/'>
42<meta property='og:site_name' content='XR_G&#39;s Blog'>
43<meta property='og:type' content='article'><meta property='article:section' content='Post' /><meta property='article:tag' content='技术' /><meta property='article:published_time' content='2025-09-27T00:00:00&#43;00:00'/><meta property='article:modified_time' content='2025-09-27T00:00:00&#43;00:00'/>
44<meta name="twitter:title" content="【cppcon25】Pratical Data-Oriented Design in C&#43;&#43;">
45<meta name="twitter:description" content="今天看的演讲是关于数据驱动设计( Data-oriented Design, DoD )的,真令我大开眼界。这个设计思路的性能收益很高,可读性也很好,实在应该找个机会狠狠落实到工作中。
46Vittorio的演讲风格挺干练的,talk is cheap,全程围绕一个小Demo来演绎如何通过DoD实现巨大的性能优化。
471 一个简单的Demo Demo是一个简单的场景模拟,要求在屏幕上飞过一些 可以喷射粒子 的 火箭 ,并为了更加贴近实际,附加了几点需求:
48运动模拟:火箭需要模拟出位置、速度、加速度等 效果:不同类型的粒子和发射器,支持自定义参数(透明度、大小、旋转角等) 实体:为了便于控制单个实体,理论上可以建模为例如火箭&#43;发射器 扩展性:便于增加新的实体或效果 一个简单的建模会是这样的(其中深蓝色为实体类,浅蓝色为抽象类):
49场景中所有的对象都是一个 实体 Entity 发射器和粒子可以有不同类型,如火焰发射器&amp;烟雾发射器,对应火焰粒子&amp;烟雾粒子 对象的实现可能是这样的(结合个人理解,对声明顺序进行了调整,以便呈现思路,其余代码细节均与演讲保持一致):
50struct Entity { // 1 首先实体在我们的设计中是一个抽象类,通常需要虚析构函数 virtual ~Entity() = default; // 2 实体需要遵循物理规律,因此提供一些物理属性(位置、速度、加速度) sf::Vector2f position; sf::Vector2f velocity; sf::Vector2f acceleration; // 3 实体需要更新和渲染 virtual void update(float dt) { position &#43;= velocity * dt; velocity &#43;= acceleration * dt; } virtual void draw(sf::RenderTarget&amp;); // 4 实体需要感知别的实体,因此会引用世界对象,同时,告诉世界自身是否还存活 World&amp; world; bool alive = true; }; 粒子的实现,在实体基础上增加了大小、透明度和旋转角,同时当透明度降为 0 时,会自动销毁:
51">
52    <link rel="shortcut icon" href="/favicon.ico" />
53
54    </head>
55    <body class="
56    article-page has-toc
57">
58        <div id="loading-box">
59                
60                <div class="loading-left-bg"></div>
61                <div class="loading-right-bg"></div>
62                
63                <div class="spinner-box">
64                        <div class="configure-border-1">
65                                <div class="configure-core"></div>
66                        </div>
67                        <div class="configure-border-2">
68                                <div class="configure-core"></div>
69                        </div>
70                        <div class="loading-word">加载中...</div>
71                </div>
72      </div>
73      
74      
74<script>
75        $(document).ready(function () {
76                
77                document.getElementById('loading-box').classList.add("loaded")
78        })
79      </script>
79
80
81    
81<script>
82        (function() {
83            const colorSchemeKey = 'StackColorScheme';
84            if(!localStorage.getItem(colorSchemeKey)){
85                localStorage.setItem(colorSchemeKey, "auto");
86            }
87        })();
88    </script>
88<script>
89    (function() {
90        const colorSchemeKey = 'StackColorScheme';
91        const colorSchemeItem = localStorage.getItem(colorSchemeKey);
92        const supportDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches === true;
93
94        if (colorSchemeItem == 'dark' || colorSchemeItem === 'auto' && supportDarkMode) {
95            
96
97            document.documentElement.dataset.scheme = 'dark';
98        } else {
99            document.documentElement.dataset.scheme = 'light';
100        }
101    })();
102</script>
102
103<div class="container main-container flex 
104    
105        extended
106    
107">
108    
109        <div id="article-toolbar">
110            <a href="/" class="back-home">
111                <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-chevron-left" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
112  <path stroke="none" d="M0 0h24v24H0z"/>
113  <polyline points="15 6 9 12 15 18" />
114</svg>
115
116
117
118                <span>返回</span>
119            </a>
120        </div>
121    
122<main class="main full-width">
123    <article class="main-article">
124    <header class="article-header">
125
126    <div class="article-details">
127    
128
129    <h2 class="article-title">
130        <a href="/p/cppcon25pratical-data-oriented-design-in-c-/">【cppcon25】Pratical Data-Oriented Design in C&#43;&#43;</a>
131    </h2>
132
133    
134
135    
136    <footer class="article-time">
137        
138            <div>
139                <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-calendar-time" width="56" height="56" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
140  <path stroke="none" d="M0 0h24v24H0z"/>
141  <path d="M11.795 21h-6.795a2 2 0 0 1 -2 -2v-12a2 2 0 0 1 2 -2h12a2 2 0 0 1 2 2v4" />
142  <circle cx="18" cy="18" r="4" />
143  <path d="M15 3v4" />
144  <path d="M7 3v4" />
145  <path d="M3 11h16" />
146  <path d="M18 16.496v1.504l1 1" />
147</svg>
148                <time class="article-time--published">Sep 27, 2025</time>
149            </div>
150        
151            <div>
152                <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-clock" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
153  <path stroke="none" d="M0 0h24v24H0z"/>
154  <circle cx="12" cy="12" r="9" />
155  <polyline points="12 7 12 12 15 15" />
156</svg>
157
158
159
160                <time class="article-time--reading">
161                    阅读时长: 6 分钟
162                </time>
163            </div>
164        
165    </footer>
166    
167</div>
168</header>
169
170    <section class="article-content">
171    <p>今天看的演讲是关于数据驱动设计( <em>Data-oriented Design, DoD</em> )的,真令我大开眼界。这个设计思路的性能收益很高,可读性也很好,实在应该找个机会狠狠落实到工作中。</p>
172<p>Vittorio的演讲风格挺干练的,talk is cheap,全程围绕一个小Demo来演绎如何通过DoD实现巨大的性能优化。</p>
173<h3 id="1-一个简单的demo">1 一个简单的Demo</h3>
174<p>Demo是一个简单的场景模拟,要求在屏幕上飞过一些 <em>可以喷射粒子</em> 的 <em>火箭</em> ,并为了更加贴近实际,附加了几点需求:</p>
175<ul>
176<li>运动模拟:火箭需要模拟出位置、速度、加速度等</li>
177<li>效果:不同类型的粒子和发射器,支持自定义参数(透明度、大小、旋转角等)</li>
178<li>实体:为了便于控制单个实体,理论上可以建模为例如火箭+发射器</li>
179<li>扩展性:便于增加新的实体或效果</li>
180</ul>
181<p><figure 
182	
183		class="gallery-image" 
184		style="
185			flex-grow: 137; 
186			flex-basis: 329px"
187	>
188	<a href="/p/cppcon25pratical-data-oriented-design-in-c-/1.png" data-size="1067x778">
189		<img src="/p/cppcon25pratical-data-oriented-design-in-c-/1.png"
190			width="1067"
191			height="778"
192			srcset="/p/cppcon25pratical-data-oriented-design-in-c-/1_hu5410305169650136899.png 480w, /p/cppcon25pratical-data-oriented-design-in-c-/1_hu10257298369898371339.png 1024w"
193			loading="lazy"
194			>
195	</a>
196	
197</figure></p>
198<p>一个简单的建模会是这样的(其中深蓝色为实体类,浅蓝色为抽象类):</p>
199<p><figure 
200	
201		class="gallery-image" 
202		style="
203			flex-grow: 181; 
204			flex-basis: 436px"
205	>
206	<a href="/p/cppcon25pratical-data-oriented-design-in-c-/2.png" data-size="1502x826">
207		<img src="/p/cppcon25pratical-data-oriented-design-in-c-/2.png"
208			width="1502"
209			height="826"
210			srcset="/p/cppcon25pratical-data-oriented-design-in-c-/2_hu2386365465619707563.png 480w, /p/cppcon25pratical-data-oriented-design-in-c-/2_hu17582678550926521366.png 1024w"
211			loading="lazy"
212			>
213	</a>
214	
215</figure></p>
216<ul>
217<li>场景中所有的对象都是一个 <em>实体</em> <code>Entity</code></li>
218<li>发射器和粒子可以有不同类型,如火焰发射器&amp;烟雾发射器,对应火焰粒子&amp;烟雾粒子</li>
219</ul>
220<p>对象的实现可能是这样的(结合个人理解,对声明顺序进行了调整,以便呈现思路,其余代码细节均与演讲保持一致):</p>
221<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">Entity</span> <span class="p">{</span>
222</span></span><span class="line"><span class="cl">    <span class="c1">// 1 首先实体在我们的设计中是一个抽象类,通常需要虚析构函数
223</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">virtual</span> <span class="o">~</span><span class="n">Entity</span><span class="p">()</span> <span class="o">=</span> <span class="k">default</span><span class="p">;</span>
224</span></span><span class="line"><span class="cl">
225</span></span><span class="line"><span class="cl">    <span class="c1">// 2 实体需要遵循物理规律,因此提供一些物理属性(位置、速度、加速度)
226</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">sf</span><span class="o">::</span><span class="n">Vector2f</span> <span class="n">position</span><span class="p">;</span>
227</span></span><span class="line"><span class="cl">    <span class="n">sf</span><span class="o">::</span><span class="n">Vector2f</span> <span class="n">velocity</span><span class="p">;</span>
228</span></span><span class="line"><span class="cl">    <span class="n">sf</span><span class="o">::</span><span class="n">Vector2f</span> <span class="n">acceleration</span><span class="p">;</span>
229</span></span><span class="line"><span class="cl">
230</span></span><span class="line"><span class="cl">    <span class="c1">// 3 实体需要更新和渲染
231</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">virtual</span> <span class="kt">void</span> <span class="nf">update</span><span class="p">(</span><span class="kt">float</span> <span class="n">dt</span><span class="p">)</span>
232</span></span><span class="line"><span class="cl">    <span class="p">{</span>
233</span></span><span class="line"><span class="cl">
233        <span class="n">position</span> <span class="o">+=</span> <span class="n">velocity</span> <span class="o">*</span> <span class="n">dt</span><span class="p">;</span>
234</span></span><span class="line"><span class="cl">        <span class="n">velocity</span> <span class="o">+=</span> <span class="n">acceleration</span> <span class="o">*</span> <span class="n">dt</span><span class="p">;</span>
235</span></span><span class="line"><span class="cl">    <span class="p">}</span>
236</span></span><span class="line"><span class="cl">    <span class="k">virtual</span> <span class="kt">void</span> <span class="nf">draw</span><span class="p">(</span><span class="n">sf</span><span class="o">::</span><span class="n">RenderTarget</span><span class="o">&amp;</span><span class="p">);</span>
237</span></span><span class="line"><span class="cl">
238</span></span><span class="line"><span class="cl">    <span class="c1">// 4 实体需要感知别的实体,因此会引用世界对象,同时,告诉世界自身是否还存活
239</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">World</span><span class="o">&amp;</span> <span class="n">world</span><span class="p">;</span>
240</span></span><span class="line"><span class="cl">    <span class="kt">bool</span> <span class="n">alive</span> <span class="o">=</span> <span class="nb">true</span><span class="p">;</span>
241</span></span><span class="line"><span class="cl"><span class="p">};</span>
242</span></span></code></pre></div><p>粒子的实现,在实体基础上增加了大小、透明度和旋转角,同时当透明度降为 <code>0</code> 时,会自动销毁:</p>
243<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">Particle</span> <span class="o">:</span> <span class="n">Entity</span> <span class="p">{</span>
244</span></span><span class="line"><span class="cl">    <span class="kt">float</span> <span class="n">scale</span><span class="err">,</span> <span class="n">scaleRate</span><span class="p">;</span>
245</span></span><span class="line"><span class="cl">    <span class="kt">float</span> <span class="n">opacity</span><span class="p">,</span> <span class="n">opacityChange</span><span class="p">;</span>
246</span></span><span class="line"><span class="cl">    <span class="kt">float</span> <span class="n">rotation</span><span class="p">,</span> <span class="n">angularVelocity</span><span class="p">;</span>
247</span></span><span class="line"><span class="cl">
248</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="nf">update</span><span class="p">(</span><span class="kt">float</span> <span class="n">dt</span><span class="p">)</span> <span class="k">override</span>
249</span></span><span class="line"><span class="cl">    <span class="p">{</span>
250</span></span><span class="line"><span class="cl">        <span class="n">Entity</span><span class="o">::</span><span class="n">update</span><span class="p">(</span><span class="n">dt</span><span class="p">);</span>
251</span></span><span class="line"><span class="cl">        <span class="n">scale</span> <span class="o">+=</span> <span class="n">scaleRate</span> <span class="o">*</span> <span class="n">dt</span><span class="p">;</span>
252</span></span><span class="line"><span class="cl">        <span class="n">opacity</span> <span class="o">+=</span> <span class="n">opacityChange</span> <span class="o">*</span> <span class="n">dt</span><span class="p">;</span>
253</span></span><span class="line"><span class="cl">        <span class="n">rotation</span> <span class="o">+=</span> <span class="n">angularVelocity</span> <span class="o">*</span> <span class="n">dt</span><span class="p">;</span>
254</span></span><span class="line"><span class="cl">
255</span></span><span class="line"><span class="cl">        <span class="n">alive</span> <span class="o">=</span> <span class="n">opacity</span> <span class="o">&gt;</span> <span class="mf">0.0f</span><span class="p">;</span>
256</span></span><span class="line"><span class="cl">    <span class="p">}</span>
257</span></span><span class="line"><span class="cl"><span class="p">};</span>
258</span></span></code></pre></div><p>具体粒子的实现,如烟雾粒子,只是使用特定纹理实现了渲染能力,演
258讲中没有给出具体实现:</p>
259<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">SmokeParticle</span> <span class="o">:</span> <span class="n">Particle</span> <span class="p">{</span>
260</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="nf">draw</span><span class="p">(</span><span class="n">sf</span><span class="o">::</span><span class="n">RenderTarget</span><span class="o">&amp;</span> <span class="n">target</span><span class="p">)</span> <span class="k">override</span>
261</span></span><span class="line"><span class="cl">    <span class="p">{</span>
262</span></span><span class="line"><span class="cl">        <span class="c1">// ... draw using smoke texture ...
263</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="p">}</span>
264</span></span><span class="line"><span class="cl"><span class="p">};</span>
265</span></span></code></pre></div><p>所有的实体保存在世界中,世界对象会管理所有实体,并调用每个实体的更新和渲染方法:</p>
266<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">World</span> <span class="p">{</span>
267</span></span><span class="line"><span class="cl">    <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">unique_ptr</span><span class="o">&lt;</span><span class="n">Entity</span><span class="o">&gt;&gt;</span> <span class="n">entities</span><span class="p">;</span>
268</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="nf">update</span><span class="p">(</span><span class="kt">float</span> <span class="n">dt</span><span class="p">)</span>
269</span></span><span class="line"><span class="cl">    <span class="p">{</span>
270</span></span><span class="line"><span class="cl">        <span class="k">for</span> <span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">entity</span> <span class="p">:</span> <span class="n">entities</span><span class="p">)</span>
271</span></span><span class="line"><span class="cl">            <span class="n">entity</span><span class="o">-&gt;</span><span class="n">update</span><span class="p">(</span><span class="n">dt</span><span class="p">);</span>
272</span></span><span class="line"><span class="cl">    <span class="p">}</span>
273</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="nf">draw</span><span class="p">(</span><span class="n">sf</span><span class="o">::</span><span class="n">RenderTarget</span><span class="o">&amp;</span> <span class="n">target</span><span class="p">)</span>
274</span></span><span class="line"><span class="cl">    <span class="p">{</span>
275</span></span><span class="line"><span class="cl">        <span class="k">for</span> <span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">entity</span> <span class="p">:</span> <span class="n">entities</span><span class="p">)</span>
276</span></span><span class="line"><span class="cl">            <span class="n">entity</span><span class="o">-&gt;</span><span class="n">draw</span><span class="p">(</span><span class="n">target</span><span class="p">);</span>
277</span></span><span class="line"><span class="cl">    <span class="p">}</span>
278</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="nf">cleanup</span><span class="p">()</span>
279</span></span><span class="line"><span class="cl">    <span class="p">{</span>
280</span></span><span class="line"><span class="cl">        <span class="n">std</span><span class="o">::</span><span class="n">erase_if</span><span class="p">(</span><span class="n">entities</span><span class="p">,</span> <span class="p">[](</span><span class="k">const</span> <span class="k">auto</span><span class="o">&amp;</span> <span class="n">entity</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="o">!</span><span class="n">entity</span><span class="o">-&gt;</span><span class="n">
280alive</span><span class="p">;</span> <span class="p">});</span>
281</span></span><span class="line"><span class="cl">    <span class="p">}</span>
282</span></span><span class="line"><span class="cl"><span class="p">};</span>
283</span></span></code></pre></div><p>发射器的功能就是定期发射粒子,而具体发射器的实现,如烟雾发射器,只是生成一个粒子对象,并添加到世界中:</p>
284<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">Emitter</span> <span class="o">:</span> <span class="n">Entity</span> <span class="p">{</span>
285</span></span><span class="line"><span class="cl">    <span class="kt">float</span> <span class="n">spawnTimer</span><span class="p">,</span> <span class="n">spawnRate</span><span class="p">;</span>
286</span></span><span class="line"><span class="cl">
287</span></span><span class="line"><span class="cl">    <span class="k">virtual</span> <span class="kt">void</span> <span class="nf">spawnParticle</span><span class="p">()</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
288</span></span><span class="line"><span class="cl">
289</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="nf">update</span><span class="p">(</span><span class="kt">float</span> <span class="n">dt</span><span class="p">)</span> <span class="k">override</span>
290</span></span><span class="line"><span class="cl">    <span class="p">{</span>
291</span></span><span class="line"><span class="cl">        <span class="c1">// ... periodically call `spawnParticle()` ...
292</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="p">}</span>
293</span></span><span class="line"><span class="cl"><span class="p">};</span>
294</span></span><span class="line"><span class="cl">
295</span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">SmokeEmitter</span> <span class="o">:</span> <span class="n">Emitter</span> <span class="p">{</span>
296</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="nf">spawnParticle</span><span class="p">()</span> <span class="k">override</span>
297</span></span><span class="line"><span class="cl">    <span class="p">{</span>
298</span></span><span class="line"><span class="cl">        <span class="c1">// 这里的代码和演讲里的代码稍有不同,我认为这个实现才对
299</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="c1">// 演讲的代码里把 `world` 当指针用,但实际上在 Entity 的定义中, `world` 是一个引用
300</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="n">world</span><span class="p">.</span><span class="n">entities</span><span class="p">.</span><span class="n">emplace_back</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">make_unique</span><span class="o">&lt;</span><span class="n">SmokeParticle</span><span class="o">&gt;</span><span class="p">());</span>
301</span></span><span class="line"><span class="cl">    <span class="p">}</span>
302</span></span><span class="line"><span class="cl"><span class="p">};</span>
303</span></span></code></pre></div><p>最后一个实体是火箭,它和发射器的关系是组合关系,它的移动会带着发射器移动:</p>
304<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">Rocket</span> <span class="o">:</span> <span class="n">Entity</span> <span class="p">{</span>
305</span></span><span class="line"><span class="cl">    <span class="c1">// 因为能够保证生命周期,所以这里使用裸指针
306</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">SmokeEmitter</span> <span class="o">*</span><span class="n">smokeEmitter</span> <span class="o">=</span> <span class="k">nullptr</span><span class="p">;</span>
307</span></span><span class="line"><span class="cl">    <span class="n">FireEmitter</span> <span class="o">*</span><span class="n">fireEmitter</span> <span class="o">=</span> <span class="k">nullptr</span><span class="p">;</span>
308</span></span><span class="line"><span class="cl">
309</span></span><span class="line"><span class="cl">    <span class="n">Rocket</span><span class="p">()</span>
310</span></span><span class="line"><span class="cl">    <span class="p">{</span>
311</span></span><span class="line"><span class="cl">
311        <span class="c1">// ... create emitters ...
312</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="p">}</span>
313</span></span><span class="line"><span class="cl">
314</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="nf">update</span><span class="p">(</span><span class="kt">float</span> <span class="n">dt</span><span class="p">)</span> <span class="k">override</span>
315</span></span><span class="line"><span class="cl">    <span class="p">{</span>
316</span></span><span class="line"><span class="cl">        <span class="c1">// ... move emitters with rocket ...
317</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="p">}</span>
318</span></span><span class="line"><span class="cl"><span class="p">};</span>
319</span></span></code></pre></div><blockquote>
320<p>上述的实现有些地方也禁不起严格推敲,例如 <code>Entity::update</code> 实现了移动,但 <code>Emitter::update</code> 行为和它的基类完全不一样… whatever,这个实现还是比较OOP style的</p>
321</blockquote>
322<p>作者的benckmark测试结果如下,横轴为不同实体数量,纵轴为update消耗的时间:</p>
323<p><figure 
324	
325		class="gallery-image" 
326		style="
327			flex-grow: 179; 
328			flex-basis: 430px"
329	>
330	<a href="/p/cppcon25pratical-data-oriented-design-in-c-/3.png" data-size="1522x849">
331		<img src="/p/cppcon25pratical-data-oriented-design-in-c-/3.png"
332			width="1522"
333			height="849"
334			srcset="/p/cppcon25pratical-data-oriented-design-in-c-/3_hu6696551923100701951.png 480w, /p/cppcon25pratical-data-oriented-design-in-c-/3_hu4248200274122669967.png 1024w"
335			loading="lazy"
336			>
337	</a>
338	
339</figure></p>
340<p>看上去还可以的表现,但又考虑到两个数据:</p>
341<ul>
342<li><code>60FPS = ~16.67ms</code></li>
343<li><code>144FPS = ~6.94ms</code></li>
344</ul>
345<p>😲🥲!这样看来,为了流畅运行,每一帧留给开发者的时间真的非常短,常规实现的性能问题非常之显著。</p>
346<h3 id="2-题外话内存和cache">2 题外话:内存和Cache</h3>
347<p>这一节不打算跟着演讲详细记录,仅摘录一些重要的点。</p>
348<ul>
349<li><em>Cache line</em> 是最小的内存传输单元:即使我们只需要一个字节的数据,内存也是按照整行读入的;因此更宜使用连续内存,且按照利于CPU预测的方式进行数据访问;</li>
350</ul>
351<p><figure 
352	
353		class="gallery-image" 
354		style="
355			flex-grow: 182; 
356			flex-basis: 436px"
357	>
358	<a href="/p/cppcon25pratical-data-oriented-design-in-c-/4.png" data-size="1511x830">
359		<img src="/p/cppcon25pratical-data-oriented-design-in-c-/4.png"
360			width="1511"
361			height="830"
362			srcset="/p/cppcon25pratical-data-oriented-design-in-c-/4_hu3006604439347195105.png 480w, /p/cppcon25pratical-data-oriented-design-in-c-/4_hu11840952683576943485.png 1024w"
363			loading="lazy"
364			>
365	</a>
366	
367</figure></p>
368<h3 id="3-oop实现的性能问题分析">3 OOP实现的性能问题分析</h3>
369<ol>
370<li>世界对象中使用的是实体的指针,即,内存中实体的分布可能是分散的,这导致了几乎每次迭代都可能产生Cache Miss</li>
371<li>每次调用实体的 <code>update</code> 方法都需要进行虚函数的动态分发(运行时多态,依赖虚表查找真正的实现)</li>
372<li>较多的内存申请/释放</li>
373</ol>
374<p><figure 
375	
376		class="gallery-image" 
377		style="
378			flex-grow: 179; 
379			flex-basis: 430px"
380	>
381	<a href="/p/cppcon25pratical-data-oriented-design-in-c-/5.png" data-size="1518x846">
382		<img src="/p/cppcon25pratical-data-oriented-design-in-c-/5.png"
383			width="1518"
384			height="846"
385			srcset="/p/cppcon25pratical-data-oriented-design-in-c-/5_hu7351305689579101415.png 480w, /p/cppcon25pratical-data-oriented-design-in-c-/5_hu2237564057126370495.png 1024w"
386			loading="lazy"
387			>
388	</a>
389	
390</figure></p>
391<h3 id="4-设计哲学的对比">4 设计哲学的对比</h3>
392<p>从设计哲学来看,最初的实现虽然存在性能问题,但基本上已经符合OOP的设计哲学:</p>
393<p><figure 
394	
395		class="gallery-image" 
396		style="
397			flex-grow: 180; 
398			flex-basis: 432px"
399	>
400	<a href="/p/cppcon25pratical-data-oriented-design-in-c-/6.png" data-size="1507x836">
401		<img src="/p/cppcon25pratical-data-oriented-design-in-c-/6.png"
402			width="1507"
403			height="836"
404			srcset="/p/cppcon25pratical-data-oriented-design-in-c-/6_hu13626525986782113667.png 480w, /p/cppcon25pratical-data-oriented-design-in-c-/6_hu15220238138771443978.png 1024w"
405			loading="lazy"
406			>
407	</a>
408	
409</figure></p>
410<ul>
411<li>世界由对象组成</li>
412<li>通过消息传递进行交流</li>
413<li>暴露行为、隐藏数据</li>
414<li>规划未知的需求</li>
415</ul>
416<blockquote>
417<p>Vittorio说这个 <em>bet</em> (赌注)一词是他精心安排的,我一定程度上同意。OOP的基类,每一个方法都在赌自己对于世界的描述是正确的;后续扩展出来的子类,满足基类的设计则皆大欢喜,不满足基类的设计则拖泥带水地改动。这不是说OOP本身有问题,而是在实践中这太过于考验开发人员的设计水平,谁又能要求每个人都是大pro呢?</p>
418</blockquote>
419<p>相比之下,面向数据的设计哲学可能是这样的:</p>
420<p><figure 
421	
422		class="gallery-image" 
423		style="
424			flex-grow: 180; 
425			flex-basis: 432px"
426	>
427	<a href="/p/cppcon25pratical-data-oriented-design-in-c-/7.png" data-size="1503x835">
428		<img src="/p/cppcon25pratical-data-oriented-design-in-c-/7.png"
429			width="1503"
430			height="835"
431			srcset="/p/cppcon25pratical-data-oriented-design-in-c-/7_hu2318994575623631177.png 480w, /p/cppcon25pratical-data-oriented-design-in-c-/7_hu17420241732183230488.png 1024w"
432			loading="lazy"
433			>
434	</a>
435	
436</figure></p>
437<ul>
438<li>世界由数据组成,代码的作用是把数据从一个状态转换到另一个状态</li>
439<li>直接操作批量数据</li>
440<li>暴露数据,集中行为</li>
441<li>规划当下</li>
442</ul>
443<p>转变思路的关键:</p>
444<p><figure 
445	
446		class="gallery-image" 
447		style="
448			flex-grow: 176; 
449			flex-basis: 423px"
450	>
451	<a href="/p/cppcon25pratical-data-oriented-design-in-c-/8.png" data-size="1434x812">
452		<img src="/p/cppcon25pratical-data-oriented-design-in-c-/8.png"
453			width="1434"
454			height="812"
455			srcset="/p/cppcon25pratical-data-oriented-design-in-c-/8_hu10384310888831867967.png 480w, /p/cppcon25pratical-data-oriented-design-in-c-/8_hu10081852515287269042.png 1024w"
456			loading="lazy"
457			>
458	</a>
459	
460</figure></p>
461<ul>
462<li>认识到代码的唯一作用就是进行数据转换</li>
463<li>认识到数据是核心,而不是需要隐藏起来的东西</li>
464<li>认识到计算机更喜欢简单、可预测的工作,向计算机提供大段连续的数据</li>
465<li>认识到设计服务于机器,而非问题的建模</li>
466</ul>
467<blockquote>
468<p>的确比较振聋发聩,尤其是“Design for the machine”,完全是对习惯于OOP的人的一大冲击</p>
469</blockquote>
470<h3 id="5-对demo的优化">5 对Demo的优化</h3>
471<h4 id="51-优化1">5.1 优化1</h4>
472<ul>
473<li>避免单独的内存请求:干掉指针</li>
474<li>拉平架构:干掉继承</li>
475<li>数据和逻辑解耦:实体只是一些数据,世界决定行为</li>
476<li>将不同的类型存在各自的连续的数组中:从一个基类指针数组切换到若干实际对象的数组</li>
477</ul>
478<p>talk is cheap:</p>
479<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="c1">// 当前发射器和粒子需要区分烟雾和火焰
480</span></span></span><span class="line"><span class="cl"><span class="c1">// *暂且* 通过一个 `type` 属性来区分,后面会继续优化
481</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">enum</span> <span class="k">class</span> <span class="nc">ParticleType</span> <span class="p">{</span>
482</span></span><span class="line"><span class="cl">    <span class="n">Smoke</span><span class="p">,</span>
483</span></span><span class="line"><span class="cl">    <span class="n">Fire</span>
484</span></span><span class="line"><span class="cl"><span class="p">};</span>
485</span></span><span class="line"><span class="cl">
486</span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">Emitter</span> <span class="p">{</span>
487</span></span><span class="line"><span class="cl">    <span class="n">sf</span><span class="o">::</span><span class="n">Vector2f</span> <span class="n">position</span><span class="p">;</span>
488</span></span><span class="line"><span class="cl">    <span class="n">sf</span><span class="o">::</span><span class="n">Vector2f</span> <span class="n">velocity</span><span class="p">;</span>
489</span></span><span class="line"><span class="cl">    <span class="n">sf</span><span class="o">::</span><span class="n">Vector2f</span> <span class="n">acceleration</span><span class="p">;</span>
490</span></span><span class="line"><span class="cl">    <span class="kt">float</span> <span class="n">spawnTimer</span><span class="p">,</span> <span class="n">spawnRate</span><span class="p">;</span>
491</span></span><span class="line"><span class="cl">
492</span></span><span class="line"><span class="cl">    <span class="n">ParticleType</span> <span class="n">type</span><span class="p">;</span>
493</span></span><span class="line"><span class="cl"><span class="p">};</span>
494</span></span><span class="line"><span class="cl">
495</span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">Particle</span> <span class="p">{</span>
496</span></span><span class="line"><span class="cl">    <span class="c1">// 数据之间不再有继承关系,尽管这样会带来物理要素的重复,但没太大关系
497</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">sf</span><span class="o">::</span><span class="n">Vector2f</span> <span class="n">position</span><span class="p">;</span>
498</span></span><span class="line"><span class="cl">    <span class="n">sf</span><span class="o">::</span><span class="n">Vector2f</span> <span class="n">velocity</span><span class="p">;</span>
499</span></span><span class="line"><span class="cl">    <span class="n">sf</span><span class="o">::</span><span class="n">Vector2f</span> <span class="n">acceleration</span><span class="p">;</span>
500</span></span><span class="line"><span class="cl">
501</span></span><span class="line"><span class="cl">    <span class="kt">float</span> <span class="n">scale</span><span class="p">;</span>
502</span></span><span class="line"><span class="cl">    <span class="kt">float</span> <span class="n">opacity</span><span class="p">;</span>
503</span></span><span class="line"><span class="cl">    <span class="kt">float</span> <span class="n">rotation</span><span class="p">;</span>
504</span></span><span class="line"><span class="cl">
505</span></span><span class="line"><span class="cl">    <span class="kt">float</span> <span class="n">scaleRate</span><span class="p">;</span>
506</span></span><span class="line"><span class="cl">    <span class="kt">float</span> <span class="n">opacityChange</span><span class="p">;</span>
507</span></span><span class="line"><span class="cl">    <span class="kt">float</span> <span class="n">rotationVelocity</span><span class="p">;</span>
508</span></span><span class="line"><span class="cl">
509</span></span><span class="line"><span class="cl">    <span class="n">ParticleType</span> <span class="n">type</span><span class="p">;</span>
510</span></span><span class="line"><span class="cl"><span class="p">};</span>
511</span></span><span class="line"><span class="cl">
512</span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">Rocket</span> <span class="p">{</span>
513</span></span><span class="line"><span class="cl">    <span class="n">sf</span><span class="o">::</span><span class="n">Vector2f</span> <span class="n">position</span><span class="p">;</span>
514</span></span><span class="line"><span class="cl">    <span class="n">sf</span><span class="o">::</span><span class="n">Vector2f</span> <span class="n">velocity</span><span class="p">;</span>
515</span></span><span class="line"><span class="cl">    <span class="n">sf</span><span class="o">::</span><span class="n">Vector2f</span> <span class="n">acceleration</span><span class="p">;</span>
516</span></span><span class="line"><span class="cl">
517</span></span><span class="line"><span class="cl">    <span class="c1">// 火箭不再通过指针来管理发射器,而是通过索引来关联
518</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">std</span><span class="o">::</span><span class="n">
518size_t</span> <span class="n">smokeEmitterIndex</span><span class="p">;</span>
519</span></span><span class="line"><span class="cl">    <span class="n">std</span><span class="o">::</span><span class="n">size_t</span> <span class="n">fireEmitterIndex</span><span class="p">;</span>
520</span></span><span class="line"><span class="cl"><span class="p">};</span>
521</span></span></code></pre></div><p>世界对象现在管理数据:</p>
522<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">World</span> <span class="p">{</span>
523</span></span><span class="line"><span class="cl">    <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">Particle</span><span class="o">&gt;</span> <span class="n">particles</span><span class="p">;</span>
524</span></span><span class="line"><span class="cl">    <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">Rocket</span><span class="o">&gt;</span> <span class="n">rockets</span><span class="p">;</span>
525</span></span><span class="line"><span class="cl">    <span class="c1">// 要点:由于我们的rocket通过索引关联发射器
526</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="c1">// 当发射器移除,不应重排内存,而只是将对应槽位置为无效,因此这里使用optional
527</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">optional</span><span class="o">&lt;</span><span class="n">Emitter</span><span class="o">&gt;&gt;</span> <span class="n">emitters</span><span class="p">;</span>
528</span></span><span class="line"><span class="cl">
529</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="nf">addRocket</span><span class="p">(</span><span class="k">const</span> <span class="n">Rocket</span> <span class="o">&amp;</span><span class="n">rocket</span><span class="p">);</span>
530</span></span><span class="line"><span class="cl">    <span class="n">std</span><span class="o">::</span><span class="n">size_t</span> <span class="n">addEmitter</span><span class="p">(</span><span class="k">const</span> <span class="n">Emitter</span> <span class="o">&amp;</span><span class="n">emitter</span><span class="p">);</span>
531</span></span><span class="line"><span class="cl">
532</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="nf">update</span><span class="p">(</span><span class="kt">float</span> <span class="n">dt</span><span class="p">)</span><span class="err">;</span>
533</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="n">draw</span><span class="p">(</span><span class="n">sf</span><span class="o">::</span><span class="n">RenderTarget</span> <span class="o">&amp;</span><span class="n">target</span><span class="p">);</span>
534</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="nf">cleanup</span><span class="p">();</span>
535</span></span><span class="line"><span class="cl"><span class="p">};</span>
536</span></span></code></pre></div><p>世界对象的 <code>update</code> 方法:</p>
537<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">World</span><span class="o">::</span><span class="n">update</span><span class="p">(</span><span class="kt">float</span> <span class="n">dt</span><span class="p">)</span>
538</span></span><span class="line"><span class="cl"><span class="p">{</span>
539</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(</span><span class="n">Particle</span> <span class="o">&amp;</span><span class="nl">particle</span> <span class="p">:</span> <span class="n">particles</span><span class="p">)</span> <span class="p">{</span>
540</span></span><span class="line"><span class="cl">
540        <span class="n">particle</span><span class="p">.</span><span class="n">position</span> <span class="o">+=</span> <span class="n">particle</span><span class="p">.</span><span class="n">velocity</span> <span class="o">*</span> <span class="n">dt</span><span class="p">;</span>
541</span></span><span class="line"><span class="cl">        <span class="n">particle</span><span class="p">.</span><span class="n">velocity</span> <span class="o">+=</span> <span class="n">particle</span><span class="p">.</span><span class="n">acceleration</span> <span class="o">*</span> <span class="n">dt</span><span class="p">;</span>
542</span></span><span class="line"><span class="cl">        <span class="n">particle</span><span class="p">.</span><span class="n">scale</span> <span class="o">+=</span> <span class="n">particle</span><span class="p">.</span><span class="n">scaleRate</span> <span class="o">*</span> <span class="n">dt</span><span class="p">;</span>
543</span></span><span class="line"><span class="cl">        <span class="n">particle</span><span class="p">.</span><span class="n">opacity</span> <span class="o">+=</span> <span class="n">particle</span><span class="p">.</span><span class="n">opacityChange</span> <span class="o">*</span> <span class="n">dt</span><span class="p">;</span>
544</span></span><span class="line"><span class="cl">        <span class="n">particle</span><span class="p">.</span><span class="n">rotation</span> <span class="o">+=</span> <span class="n">particle</span><span class="p">.</span><span class="n">rotationVelocity</span> <span class="o">*</span> <span class="n">dt</span><span class="p">;</span>
545</span></span><span class="line"><span class="cl">    <span class="p">}</span>
546</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">optional</span><span class="o">&lt;</span><span class="n">Emitter</span><span class="o">&gt;</span> <span class="o">&amp;</span><span class="nl">e</span> <span class="p">:</span> <span class="n">emitters</span><span class="p">)</span> <span class="p">{</span>
547</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">e</span><span class="p">.</span><span class="n">has_value</span><span class="p">())</span>
548</span></span><span class="line"><span class="cl">            <span class="k">continue</span><span class="p">;</span>
549</span></span><span class="line"><span class="cl">        <span class="n">e</span><span class="o">-&gt;</span><span class="n">position</span> <span class="o">+=</span> <span class="n">e</span><span class="o">-&gt;</span><span class="n">velocity</span> <span class="o">*</span> <span class="n">dt</span><span class="p">;</span>
550</span></span><span class="line"><span class="cl">        <span class="n">e</span><span class="o">-&gt;</span><span class="n">velocity</span> <span class="o">+=</span> <span class="n">e</span><span class="o">-&gt;</span><span class="n">acceleration</span> <span class="o">*</span> <span class="n">dt</span><span class="p">;</span>
551</span></span><span class="line"><span class="cl">        <span class="n">e</span><span class="o">-&gt;</span><span class="n">spawnTimer</span> <span class="o">+=</span> <span class="n">e</span><span class="o">-&gt;</span><span class="n">spawnRate</span> <span class="o">*</span> <span class="n">dt</span><span class="p">;</span>
552</span></span><span class="line"><span class="cl">
553</span></span><span class="line"><span class="cl">        <span class="k">for</span> <span class="p">(;</span> <span class="n">e</span><span class="o">-&gt;</span><span class="n">spawnTimer</span> <span class="o">&gt;=</span> <span class="mf">1.f</span><span class="p">;</span> <span class="n">e</span><span class="o">-&gt;</span><span class="n">spawnTimer</span> <span class="o">-=</span> <span class="mf">1.f</span><span class="p">)</span>
554</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="p">(</span><span class="n">e</span><span class="o">-&gt;</span><span class="n">type</span> <span class="o">==</span> <span class="n">ParticleType</span><span class="o">::</span><span class="n">Smoke</span><span class="p">)</span>
555</span></span><span class="line"><span class="cl">
555                <span class="n">particles</span><span class="p">.</span><span class="n">push_back</span><span class="p">({...});</span>
556</span></span><span class="line"><span class="cl">            <span class="k">else</span> <span class="nf">if</span> <span class="p">(</span><span class="n">e</span><span class="o">-&gt;</span><span class="n">type</span> <span class="o">==</span> <span class="n">ParticleType</span><span class="o">::</span><span class="n">Fire</span><span class="p">)</span>
557</span></span><span class="line"><span class="cl">                <span class="n">particles</span><span class="p">.</span><span class="n">push_back</span><span class="p">({...});</span>
558</span></span><span class="line"><span class="cl">
559</span></span><span class="line"><span class="cl">    <span class="p">}</span>
560</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(</span><span class="n">Rocket</span> <span class="o">&amp;</span><span class="nl">rocket</span> <span class="p">:</span> <span class="n">rockets</span><span class="p">)</span> <span class="p">{</span>
561</span></span><span class="line"><span class="cl">        <span class="n">rocket</span><span class="p">.</span><span class="n">position</span> <span class="o">+=</span> <span class="n">rocket</span><span class="p">.</span><span class="n">velocity</span> <span class="o">*</span> <span class="n">dt</span><span class="p">;</span>
562</span></span><span class="line"><span class="cl">        <span class="n">rocket</span><span class="p">.</span><span class="n">velocity</span> <span class="o">+=</span> <span class="n">rocket</span><span class="p">.</span><span class="n">acceleration</span> <span class="o">*</span> <span class="n">dt</span><span class="p">;</span>
563</span></span><span class="line"><span class="cl">        
564</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">optional</span><span class="o">&lt;</span><span class="n">Emitter</span><span class="o">&gt;</span> <span class="o">&amp;</span><span class="n">se</span> <span class="o">=</span> <span class="n">emitters</span><span class="p">[</span><span class="n">rocket</span><span class="p">.</span><span class="n">smokeEmitterIndex</span><span class="p">])</span>
565</span></span><span class="line"><span class="cl">            <span class="n">se</span><span class="o">-&gt;</span><span class="n">position</span> <span class="o">=</span> <span class="n">rocket</span><span class="p">.</span><span class="n">position</span> <span class="o">-</span> <span class="n">sf</span><span class="o">::</span><span class="n">Vector2f</span><span class="p">(</span><span class="mf">12.f</span><span class="p">,</span> <span class="mf">0.f</span><span class="p">);</span>
566</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">optional</span><span class="o">&lt;</span><span class="n">Emitter</span><span class="o">&gt;</span> <span class="o">&amp;</span><span class="n">fe</span> <span class="o">=</span> <span class="n">emitters</span><span class="p">[</span><span class="n">rocket</span><span class="p">.</span><span class="n">fireEmitterIndex</span><span class="p">])</span>
567</span></span><span class="line"><span class="cl">            <span class="n">fe</span><span class="o">-&gt;</span><span class="n">position</span> <span class="o">=</span> <span class="n">rocket</span><span class="p">.</span><span class="n">position</span> <span class="o">-</span> <span class="n">sf</span><span class="o">::</span><span class="n">Vector2f</span><span class="p">(</span><span class="mf">12.f</span><span class="p">,</span> <span class="mf">0.f</span><span class="p">);</span>
568</span></span><span class="line"><span class="cl">    <span class="p">}</span>
569</span></span><span class="line"><span class="cl"><span class="p">}</span>
570</span></span></code></pre></div><p>添加发射器:</p>
571<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">std</span><span class="o">::</span><span class="n">
571size_t</span> <span class="n">World</span><span class="o">::</span><span class="n">addEmitter</span><span class="p">(</span><span class="k">const</span> <span class="n">Emitter</span> <span class="o">&amp;</span><span class="n">emitter</span><span class="p">)</span>
572</span></span><span class="line"><span class="cl"><span class="p">{</span>
573</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">size_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">emitters</span><span class="p">.</span><span class="n">size</span><span class="p">();</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span>
574</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">emitters</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">has_value</span><span class="p">())</span> <span class="p">{</span>
575</span></span><span class="line"><span class="cl">            <span class="n">emitters</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">emitter</span><span class="p">;</span>
576</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="n">i</span><span class="p">;</span>
577</span></span><span class="line"><span class="cl">        <span class="p">}</span>
578</span></span><span class="line"><span class="cl">    <span class="n">emitters</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">emitter</span><span class="p">);</span>
579</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">emitters</span><span class="p">.</span><span class="n">size</span><span class="p">()</span> <span class="o">-</span> <span class="mi">1</span><span class="p">;</span>
580</span></span><span class="line"><span class="cl"><span class="p">}</span>
581</span></span></code></pre></div><p>清理:</p>
582<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">World</span><span class="o">::</span><span class="n">cleanup</span><span class="p">()</span>
583</span></span><span class="line"><span class="cl"><span class="p">{</span>
584</span></span><span class="line"><span class="cl">    <span class="c1">// 清理粒子
585</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">std</span><span class="o">::</span><span class="n">erase_if</span><span class="p">(</span><span class="n">particles</span><span class="p">,</span> <span class="p">[](</span><span class="k">const</span> <span class="n">Particle</span> <span class="o">&amp;</span><span class="n">p</span><span class="p">)</span> <span class="p">{</span>
586</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">p</span><span class="p">.</span><span class="n">opacity</span> <span class="o">&lt;=</span> <span class="mf">0.f</span><span class="p">;</span>
587</span></span><span class="line"><span class="cl">    <span class="p">});</span>
588</span></span><span class="line"><span class="cl">    <span class="c1">// 清理火箭,发射器的生命周期跟着火箭
589</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">std</span><span class="o">::</span><span class="n">erase_if</span><span class="p">(</span><span class="n">rockets</span><span class="p">,</span> <span class="p">[](</span><span class="k">const</span> <span class="n">Rocket</span> <span class="o">&amp;</span><span class="n">r</span><span class="p">)</span> <span class="p">{</span>
590</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">r</span><span class="p">.</span><span class="n">position</span><span class="p">.</span><span class="n">x</span> <span class="o">&lt;</span> <span class="n">bounds</span><span class="p">.</span><span class="n">x</span><span class="p">)</span>
591</span></span><span class="line"><span class="cl">
591            <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
592</span></span><span class="line"><span class="cl">
593</span></span><span class="line"><span class="cl">        <span class="c1">// 清理对应的发射器
594</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="n">emitters</span><span class="p">[</span><span class="n">r</span><span class="p">.</span><span class="n">smokeEmitterIndex</span><span class="p">].</span><span class="n">reset</span><span class="p">();</span>
595</span></span><span class="line"><span class="cl">        <span class="n">emitters</span><span class="p">[</span><span class="n">r</span><span class="p">.</span><span class="n">fireEmitterIndex</span><span class="p">].</span><span class="n">reset</span><span class="p">();</span>
596</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nb">true</span><span class="p">;</span> <span class="c1">// 超出屏幕范围
597</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="p">})</span><span class="err">;</span>
598</span></span><span class="line"><span class="cl"><span class="p">}</span>
599</span></span></code></pre></div><p>可怕的性能提升😲🫡:</p>
600<p><figure 
601	
602		class="gallery-image" 
603		style="
604			flex-grow: 179; 
605			flex-basis: 430px"
606	>
607	<a href="/p/cppcon25pratical-data-oriented-design-in-c-/9.png" data-size="1518x847">
608		<img src="/p/cppcon25pratical-data-oriented-design-in-c-/9.png"
609			width="1518"
610			height="847"
611			srcset="/p/cppcon25pratical-data-oriented-design-in-c-/9_hu18302114455711385202.png 480w, /p/cppcon25pratical-data-oriented-design-in-c-/9_hu10654747247858193818.png 1024w"
612			loading="lazy"
613			>
614	</a>
615	
616</figure></p>
617<hr>
618<p><strong>扩展性</strong></p>
619<p>在某些扩展需求场景下,DoD还有显著的优势。</p>
620<p>例如,当我们需要跟踪火箭的数量时,OOP不是太慢、就是会打破各种面向对象的原则,而DoD一行秒了:</p>
621<p><figure 
622	
623		class="gallery-image" 
624		style="
625			flex-grow: 178; 
626			flex-basis: 427px"
627	>
628	<a href="/p/cppcon25pratical-data-oriented-design-in-c-/10.png" data-size="1511x848">
629		<img src="/p/cppcon25pratical-data-oriented-design-in-c-/10.png"
630			width="1511"
631			height="848"
632			srcset="/p/cppcon25pratical-data-oriented-design-in-c-/10_hu839920638594494017.png 480w, /p/cppcon25pratical-data-oriented-design-in-c-/10_hu7936819700054189616.png 1024w"
633			loading="lazy"
634			>
635	</a>
636	
637</figure> <figure 
638	
639		class="gallery-image" 
640		style="
641			flex-grow: 180; 
642			flex-basis: 432px"
643	>
644	<a href="/p/cppcon25pratical-data-oriented-design-in-c-/11.png" data-size="1513x840">
645		<img src="/p/cppcon25pratical-data-oriented-design-in-c-/11.png"
646			width="1513"
647			height="840"
648			srcset="/p/cppcon25pratical-data-oriented-design-in-c-/11_hu10294139693594287059.png 480w, /p/cppcon25pratical-data-oriented-design-in-c-/11_hu3443738303422378384.png 1024w"
649			loading="lazy"
650			>
651	</a>
652	
653</figure></p>
654<p>例如,对多线程的支持。OOP的实现实际上很难支持并发,毕竟每个对象都对全局的对象产生影响,而DoD的朴素循环天然支持并发操作:</p>
655<p><figure 
656	
657		class="gallery-image" 
658		style="
659			flex-grow: 178; 
660			flex-basis: 429px"
661	>
662	<a href="/p/cppcon25pratical-data-oriented-design-in-c-/14.png" data-size="1520x850">
663		<img src="/p/cppcon25pratical-data-oriented-design-in-c-/14.png"
664			width="1520"
665			height="850"
666			srcset="/p/cppcon25pratical-data-oriented-design-in-c-/14_hu12858367930504213327.png 480w, /p/cppcon25pratical-data-oriented-design-in-c-/14_hu13471575707855015029.png 1024w"
667			loading="lazy"
668			>
669	</a>
670	
671</figure> <figure 
672	
673		class="gallery-image" 
674		style="
675			flex-grow: 178; 
676			flex-basis: 429px"
677	>
678	<a href="/p/cppcon25pratical-data-oriented-design-in-c-/15.png" data-size="1516x847">
679		<img src="/p/cppcon25pratical-data-oriented-design-in-c-/15.png"
680			width="1516"
681			height="847"
682			srcset="/p/cppcon25pratical-data-oriented-design-in-c-/15_hu958766615921814844.png 480w, /p/cppcon25pratical-data-oriented-design-in-c-/15_hu16196223889278836658.png 1024w"
683			loading="lazy"
684			>
685	</a>
686	
687</figure></p>
688<p><strong>可读性</strong></p>
689<p>至于代码的可读性,DoD的代码比OOP更直白,更松耦合,更易维护。</p>
690<p><figure 
691	
692		class="gallery-image" 
693		style="
694			flex-grow: 179; 
695			flex-basis: 430px"
696	>
697	<a href="/p/cppcon25pratical-data-oriented-design-in-c-/12.png" data-size="1516x845">
698		<img src="/p/cppcon25pratical-data-oriented-design-in-c-/12.png"
699			width="1516"
700			height="845"
701			srcset="/p/cppcon25pratical-data-oriented-design-in-c-/12_hu10419330792162832265.png 480w, /p/cppcon25pratical-data-oriented-design-in-c-/12_hu17981198533867461022.png 1024w"
702			loading="lazy"
703			>
704	</a>
705	
706</figure> <figure 
707	
708		class="gallery-image" 
709		style="
710			flex-grow: 179; 
711			flex-basis: 429px"
712	>
713	<a href="/p/cppcon25pratical-data-oriented-design-in-c-/13.png" data-size="1522x850">
714		<img src="/p/cppcon25pratical-data-oriented-design-in-c-/13.png"
715			width="1522"
716			height="850"
717			srcset="/p/cppcon25pratical-data-oriented-design-in-c-/13_hu11801942938935829101.png 480w, /p/cppcon25pratical-data-oriented-design-in-c-/13_hu17883041461787745195.png 1024w"
718			loading="lazy"
719			>
720	</a>
721	
722</figure></p>
723<blockquote>
724<p>✔️感悟:在工作中的确发现OOP的代码在复杂的继承关系出现时会变成一坨。接触新功能模块时点出一个抽象类的所有子类一个个看过去,还要在一边时刻记着调用点的上下文,不知道带走了我多少个脑细胞… 😶‍🌫️</p>
725</blockquote>
726<h4 id="52-优化2">5.2 优化2</h4>
727<ul>
728<li>在高频循环中避免分支,以免缓存命中率下降</li>
729<li>减小公共类型的体积,使得缓存可以塞下更多的对象</li>
730</ul>
731<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="c1">// 【5.2】相比于5.1,不再需要发射器/粒子类型,将由世界对象来分类
732</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">struct</span> <span class="nc">Emitter</span> <span class="p">{</span>
733</span></span><span class="line"><span class="cl">    <span class="n">sf</span><span class="o">::</span><span class="n">Vector2f</span> <span class="n">position</span><span class="p">;</span>
734</span></span><span class="line"><span class="cl">    <span class="n">sf</span><span class="o">::</span><span class="n">Vector2f</span> <span class="n">velocity</span><span class="p">;</span>
735</span></span><span class="line"><span class="cl">    <span class="n">sf</span><span class="o">::</span><span class="n">Vector2f</span> <span class="n">acceleration</span><span class="p">;</span>
736</span></span><span class="line"><span class="cl">    <span class="kt">float</span> <span class="n">spawnTimer</span><span class="p">,</span> <span class="n">spawnRate</span><span class="p">;</span>
737</span></span><span class="line"><span class="cl"><span class="p">};</span>
738</span></span><span class="line"><span class="cl">
739</span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">Particle</span> <span class="p">{</span>
740</span></span><span class="line"><span class="cl">    <span class="c1">// 数据之间不再有继承关系,尽管这样会带来字段的重复,但没太大关系
741</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">sf</span><span class="o">::</span><span class="n">Vector2f</span> <span class="n">position</span><span class="p">;</span>
742</span></span><span class="line"><span class="cl">    <span class="n">sf</span><span class="o">::</span><span class="n">Vector2f</span> <span class="n">velocity</span><span class="p">;</span>
743</span></span><span class="line"><span class="cl">    <span class="n">sf</span><span class="o">::</span><span class="n">Vector2f</span> <span class="n">acceleration</span><span class="p">;</span>
744</span></span><span class="line"><span class="cl">
745</span></span><span class="line"><span class="cl">    <span class="kt">float</span> <span class="n">scale</span><span class="p">;</span>
746</span></span><span class="line"><span class="cl">    <span class="kt">float</span> <span class="n">opacity</span><span class="p">;</span>
747</span></span><span class="line"><span class="cl">    <span class="kt">float</span> <span class="n">rotation</span><span class="p">;</span>
748</span></span><span class="line"><span class="cl">
749</span></span><span class="line"><span class="cl">    <span class="kt">float</span> <span class="n">scaleRate</span><span class="p">;</span>
750</span></span><span class="line"><span class="cl">    <span class="kt">float</span> <span class="n">opacityChange</span><span class="p">;</span>
751</span></span><span class="line"><span class="cl">    <span class="kt">float</span> <span class="n">rotationVelocity</span><span class="p">;</span>
752</span></span><span class="line"><span class="cl"><span class="p">};</span>
753</span></span><span class="line"><span class="cl">
754</span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">Rocket</span> <span class="p">{</span>
755</span></span><span class="line"><span class="cl">    <span class="n">sf</span><span class="o">::</span><span class="n">Vector2f</span> <span class="n">position</span><span class="p">;</span>
756</span></span><span class="line"><span class="cl">    <span class="n">sf</span><span class="o">::</span><span class="n">Vector2f</span> <span class="n">velocity</span><span class="p">;</span>
757</span></span><span class="line"><span class="cl">    <span class="n">sf</span><span class="o">::</span><span class="n">Vector2f</span> <span class="n">acceleration</span><span class="p">;</span>
758</span></span><span class="line"><span class="cl">
759</span></span><span class="line"><span class="cl">    <span class="c1">// 【5.2】作者指出5.1的 size_t 占用的大小太大(64比特),实际只需要 u16
760</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="c1">// 说实话,我感觉65536个发射器好像不满足benchmark上百万个火箭的需要,
761</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="c1">// 可能是因为一个屏没办法同时存在这么多数据,内存是循环利用的,才能做这种改造?
762</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">std</span><span class="o">::</span><span class="kt">uint16_t</span> <span class="n">smokeEmitterIndex</span><span class="p">;</span>
763</span></span><span class="line"><span class="cl">    <span class="n">std</span><span class="o">::</span><span class="kt">uint16_t</span> <span class="n">fireEmitterIndex</span><span class="p">;</span>
764</span></span><span class="line"><span class="cl"><span class="p">};</span>
765</span></span></code></pre></div><p>由于我们不再进行发射器/粒子的分类,世界对象的数据管理会变得精细:</p>
766<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">World</span> <span class="p">{</span>
767</span></span><span class="line"><span class="cl">    <span class="c1">// 【5.2】相比于5.1,这里对数组的管理更细
768</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">Particle</span><span class="o">&gt;</span> <span class="n">smokeParticles</span><span class="p">;</span>
769</span></span><span class="line"><span class="cl">    <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">Particle</span><span class="o">&gt;</span> <span class="n">fireParticles</span><span class="p">;</span>
770</span></span><span class="line"><span class="cl">    <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">Rocket</span><span class="o">&gt;</span> <span class="n">rockets</span><span class="p">;</span>
771</span></span><span class="line"><span class="cl">    <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">optional</span><span class="o">&lt;</span><span class="n">Emitter</span><span class="o">&gt;&gt;</span> <span class="n">smokeEmitters</span><span class="p">;</span>
772</span></span><span class="line"><span class="cl">    <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">optional</span><span class="o">&lt;</span><span class="n">Emitter</span><span class="o">&gt;&gt;</span> <span class="n">fireEmitters</span><span class="p">;</span>
773</span></span><span class="line"><span class="cl">
774</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="nf">addRocket</span><span class="p">(</span><span class="k">const</span> <span class="n">Rocket</span> <span class="o">&amp;</span><span class="n">rocket</span><span class="p">);</span>
775</span></span><span class="line"><span class="cl">    <span class="c1">// // 【5.2】相比于5.1,这里支持指定要添加的数组
776</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">std</span><span class="o">::</span><span class="n">
776size_t</span> <span class="n">addEmitter</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="n">targetVec</span><span class="p">,</span> <span class="k">const</span> <span class="n">Emitter</span> <span class="o">&amp;</span><span class="n">emitter</span><span class="p">);</span>
777</span></span><span class="line"><span class="cl">
778</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="nf">update</span><span class="p">(</span><span class="kt">float</span> <span class="n">dt</span><span class="p">)</span><span class="err">;</span>
779</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="n">draw</span><span class="p">(</span><span class="n">sf</span><span class="o">::</span><span class="n">RenderTarget</span> <span class="o">&amp;</span><span class="n">target</span><span class="p">);</span>
780</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="nf">cleanup</span><span class="p">();</span>
781</span></span><span class="line"><span class="cl"><span class="p">};</span>
782</span></span></code></pre></div><p>优化后的更新函数:</p>
783<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">World</span><span class="o">::</span><span class="n">update</span><span class="p">(</span><span class="kt">float</span> <span class="n">dt</span><span class="p">)</span>
784</span></span><span class="line"><span class="cl"><span class="p">{</span>
785</span></span><span class="line"><span class="cl">    <span class="k">auto</span> <span class="n">updateParticles</span> <span class="o">=</span> <span class="p">[</span><span class="o">&amp;</span><span class="p">](</span><span class="n">Particle</span><span class="o">&amp;</span> <span class="n">particles</span><span class="p">)</span> <span class="p">{</span>
786</span></span><span class="line"><span class="cl">        <span class="n">particle</span><span class="p">.</span><span class="n">position</span> <span class="o">+=</span> <span class="n">particle</span><span class="p">.</span><span class="n">velocity</span> <span class="o">*</span> <span class="n">dt</span><span class="p">;</span>
787</span></span><span class="line"><span class="cl">        <span class="n">particle</span><span class="p">.</span><span class="n">velocity</span> <span class="o">+=</span> <span class="n">particle</span><span class="p">.</span><span class="n">acceleration</span> <span class="o">*</span> <span class="n">dt</span><span class="p">;</span>
788</span></span><span class="line"><span class="cl">        <span class="n">particle</span><span class="p">.</span><span class="n">scale</span> <span class="o">+=</span> <span class="n">particle</span><span class="p">.</span><span class="n">scaleRate</span> <span class="o">*</span> <span class="n">dt</span><span class="p">;</span>
789</span></span><span class="line"><span class="cl">        <span class="n">particle</span><span class="p">.</span><span class="n">opacity</span> <span class="o">+=</span> <span class="n">particle</span><span class="p">.</span><span class="n">opacityChange</span> <span class="o">*</span> <span class="n">dt</span><span class="p">;</span>
790</span></span><span class="line"><span class="cl">        <span class="n">particle</span><span class="p">.</span><span class="n">rotation</span> <span class="o">+=</span> <span class="n">particle</span><span class="p">.</span><span class="n">rotationVelocity</span> <span class="o">*</span> <span class="n">dt</span><span class="p">;</span>
791</span></span><span class="line"><span class="cl">    <span class="p">};</span>
792</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(</span><span class="n">Particle</span> <span class="o">&amp;</span><span class="nl">particle</span> <span class="p">:</span> <span class="n">smokeParticles</span><span class="p">)</span> <span class="n">updateParticles</span><span class="p">(</span><span class="n">particle</span><span class="p">);</span>
793</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(</span><span class="n">Particle</span> <span class="o">&amp;</span><span class="nl">particle</span> <span class="p">:</span> <span class="n">fireParticles</span><span class="p">)</span> <span class="n">updateParticles</span><span class="p">(</span><span class="n">particle</span><span class="p">);</span>
794</span></span><span class="line"><span class="cl">
795</span></span><span class="line"><span class="cl">    <span class="k">auto</span> <span class="n">updateEmitter</span> <span class="o">=</span> <span class="p">[</span><span class="o">&amp;</span><span class="p">](</span><span class="n">std</span><span class="o">::</span><span class="n">optional</span><span class="o">&lt;</span><span class="n">Emitter</span><span class="o">&gt;&amp;</span> <span class="n">e</span><span class="p">,</span> <span class="k">auto</span><span class="o">&amp;&amp;</span> <span class="n">fSpawn</span><span class="p">)</span>
796</span></span><span class="line"><span class="cl">    <span class="p">{</span>
797</span></span><span class="line"><span class="cl">
797        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">e</span><span class="p">.</span><span class="n">has_value</span><span class="p">())</span>
798</span></span><span class="line"><span class="cl">            <span class="k">return</span><span class="p">;</span>
799</span></span><span class="line"><span class="cl">        <span class="n">e</span><span class="o">-&gt;</span><span class="n">position</span> <span class="o">+=</span> <span class="n">e</span><span class="o">-&gt;</span><span class="n">velocity</span> <span class="o">*</span> <span class="n">dt</span><span class="p">;</span>
800</span></span><span class="line"><span class="cl">        <span class="n">e</span><span class="o">-&gt;</span><span class="n">velocity</span> <span class="o">+=</span> <span class="n">e</span><span class="o">-&gt;</span><span class="n">acceleration</span> <span class="o">*</span> <span class="n">dt</span><span class="p">;</span>
801</span></span><span class="line"><span class="cl">        <span class="n">e</span><span class="o">-&gt;</span><span class="n">spawnTimer</span> <span class="o">+=</span> <span class="n">e</span><span class="o">-&gt;</span><span class="n">spawnRate</span> <span class="o">*</span> <span class="n">dt</span><span class="p">;</span>
802</span></span><span class="line"><span class="cl">
803</span></span><span class="line"><span class="cl">        <span class="k">for</span> <span class="p">(;</span> <span class="n">e</span><span class="o">-&gt;</span><span class="n">spawnTimer</span> <span class="o">&gt;=</span> <span class="mf">1.f</span><span class="p">;</span> <span class="n">e</span><span class="o">-&gt;</span><span class="n">spawnTimer</span> <span class="o">-=</span> <span class="mf">1.f</span><span class="p">)</span>
804</span></span><span class="line"><span class="cl">            <span class="n">fSpawn</span><span class="p">();</span>
805</span></span><span class="line"><span class="cl">    <span class="p">};</span>
806</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">optional</span><span class="o">&lt;</span><span class="n">Emitter</span><span class="o">&gt;&amp;</span> <span class="nl">e</span> <span class="p">:</span> <span class="n">smokeEmitters</span><span class="p">)</span>
807</span></span><span class="line"><span class="cl">        <span class="n">updateEmitter</span><span class="p">(</span><span class="n">e</span><span class="p">,</span> <span class="p">[</span><span class="o">&amp;</span><span class="p">]</span> <span class="p">{</span> <span class="n">smokeParticles</span><span class="p">.</span><span class="n">emplace_back</span><span class="p">(...);</span> <span class="p">});</span>
808</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">optional</span><span class="o">&lt;</span><span class="n">Emitter</span><span class="o">&gt;&amp;</span> <span class="nl">e</span> <span class="p">:</span> <span class="n">fireEmitters</span><span class="p">)</span>
809</span></span><span class="line"><span class="cl">        <span class="n">updateEmitter</span><span class="p">(</span><span class="n">e</span><span class="p">,</span> <span class="p">[</span><span class="o">&amp;</span><span class="p">]</span> <span class="p">{</span> <span class="n">fireParticles</span><span class="p">.</span><span class="n">emplace_back</span><span class="p">(...);</span> <span class="p">});</span>
810</span></span><span class="line"><span class="cl"><span class="p">}</span>
811</span></span></code></pre></div><p>性能提升并不很大,原因是对于作者的benchmark测试来说,性能瓶颈已经不在于数据的安排,而在于更新particle。作者自陈,优化后的数据结构在渲染时带来了12%的性能提升。</p>
812<p><figure 
813	
814		class="gallery-image" 
815		style="
816			flex-grow: 178; 
817			flex-basis: 428px"
818	>
819	<a href="/p/cppcon25pratical-data-oriented-design-in-c-/16.png" data-size="1518x850">
820		<img src="/p/cppcon25pratical-data-oriented-design-in-c-/16.png"
821			width="1518"
822			height="850"
823			srcset="/p/cppcon25pratical-data-oriented-design-in-c-/16_hu18069446886534235613.png 480w, /p/cppcon25pratical-data-oriented-design-in-c-/16_hu233018878266016890.png 1024w"
824			loading="lazy"
825			>
826	</a>
827	
828</figure></p>
829<h4 id="53-优化3">5.3 优化3</h4>
830<ul>
831<li>从 <em>Array of Structs</em> (对象数组)改成 <em>Struct of Arrays</em> (数组对象)</li>
832</ul>
833<p>不敲代码了:</p>
834<p><figure 
835	
836		class="gallery-image" 
837		style="
838			flex-grow: 179; 
839			flex-basis: 431px"
840	>
841	<a href="/p/cppcon25pratical-data-oriented-design-in-c-/17.png" data-size="1511x841">
842		<img src="/p/cppcon25pratical-data-oriented-design-in-c-/17.png"
843			width="1511"
844			height="841"
845			srcset="/p/cppcon25pratical-data-oriented-design-in-c-/17_hu2444119559110335323.png 480w, /p/cppcon25pratical-data-oriented-design-in-c-/17_hu6560399056473588303.png 1024w"
846			loading="lazy"
847			>
848	</a>
849	
850</figure></p>
851<p>相比于AoS,SoA存在如下方面的提升:</p>
852<p><figure 
853	
854		class="gallery-image" 
855		style="
856			flex-grow: 178; 
857			flex-basis: 429px"
858	>
859	<a href="/p/cppcon25pratical-data-oriented-design-in-c-/18.png" data-size="1513x846">
860		<img src="/p/cppcon25pratical-data-oriented-design-in-c-/18.png"
861			width="1513"
862			height="846"
863			srcset="/p/cppcon25pratical-data-oriented-design-in-c-/18_hu17402534095562776053.png 480w, /p/cppcon25pratical-data-oriented-design-in-c-/18_hu15862703896513886581.png 1024w"
864			loading="lazy"
865			>
866	</a>
867	
868</figure></p>
869<ul>
870<li>没有对象内部的 <em>padding</em> 。我们知道一个数据结构内部的字段不等长,因此AoS单元之间会由于每个对象内部的对齐,存在一些空内存</li>
871<li>更灵活的字段加载。当我们只需要访问一个对象的一个字段时,AoS会需要逐个加载整个对象,而SoA能够批量加载所有对象的这个字段,不需要加载其余无关字段。(注、我的理解,这实际上需要和数据处理逻辑部分深度联系,即,可能需要实现字段级别的批处理时,这种特性的优势会比较明显,反之则是劣势)</li>
872<li>更好地支持向量化操作。这里主要也是指便于进行字段级别的批处理,在这种应用场景下SIMD(单指令多数据)会进一步带来性能提升</li>
873</ul>
874<blockquote>
875<p>拓展阅读:</p>
876<p>[1] 理解SIMD技术,高效并行计算的利器,https://blog.csdn.net/qq_34068440/article/details/139636268</p>
877</blockquote>
878<p>改造为SoA的实现如下:</p>
879<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">ParticleSoA</span> <span class="p">{</span>
880</span></span><span class="line"><span class="cl">    <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">sf</span><span class="o">::</span><span class="n">Vector2f</span><span class="o">&gt;</span> <span class="n">positions</span><span class="p">;</span>
881</span></span><span class="line"><span class="cl">    <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">sf</span><span class="o">::</span><span class="n">Vector2f</span><span class="o">&gt;</span> <span class="n">velocitys</span><span class="p">;</span>
882</span></span><span class="line"><span class="cl">    <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">sf</span><span class="o">::</span><span class="n">Vector2f</span><span class="o">&gt;</span> <span class="n">accelerations</span><span class="p">;</span>
883</span></span><span class="line"><span class="cl">
884</span></span><span class="line"><span class="cl">    <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">float</span><span class="o">&gt;</span> <span class="n">scales</span><span class="p">;</span>
885</span></span><span class="line"><span class="cl">    <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">float</span><span class="o">&gt;</span> <span class="n">opacities</span><span class="p">;</span>
886</span></span><span class="line"><span class="cl">    <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">float</span><span class="o">&gt;</span> <span class="n">rotations</span><span class="p">;</span>
887</span></span><span class="line"><span class="cl">
888</span></span><span class="line"><span class="cl">    <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">float</span><span class="o">&gt;</span> <span class="n">scaleRates</span><span class="p">;</span>
889</span></span><span class="line"><span class="cl">    <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">float</span><span class="o">&gt;</span> <span class="n">opacityChanges</span><span class="p">;</span>
890</span></span><span class="line"><span class="cl">    <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">float</span><span class="o">&gt;</span> <span class="n">rotationVelocities</span><span class="p">;</span>
891</span></span><span class="line"><span class="cl">
892</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="nf">forAllVectors</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;&amp;</span> <span class="n">f</span><span class="p">)</span> <span class="p">{</span>
893</span></span><span class="line"><span class="cl">
893        <span class="n">f</span><span class="p">(</span><span class="n">positions</span><span class="p">);</span>
894</span></span><span class="line"><span class="cl">        <span class="n">f</span><span class="p">(</span><span class="n">velocitys</span><span class="p">);</span>
895</span></span><span class="line"><span class="cl">        <span class="n">f</span><span class="p">(</span><span class="n">accelerations</span><span class="p">);</span>
896</span></span><span class="line"><span class="cl">        <span class="n">f</span><span class="p">(</span><span class="n">scales</span><span class="p">);</span>
897</span></span><span class="line"><span class="cl">        <span class="n">f</span><span class="p">(</span><span class="n">opacities</span><span class="p">);</span>
898</span></span><span class="line"><span class="cl">        <span class="n">f</span><span class="p">(</span><span class="n">rotations</span><span class="p">);</span>
899</span></span><span class="line"><span class="cl">        <span class="n">f</span><span class="p">(</span><span class="n">scaleRates</span><span class="p">);</span>
900</span></span><span class="line"><span class="cl">        <span class="n">f</span><span class="p">(</span><span class="n">opacityChanges</span><span class="p">);</span>
901</span></span><span class="line"><span class="cl">        <span class="n">f</span><span class="p">(</span><span class="n">rotationVelocities</span><span class="p">);</span>
902</span></span><span class="line"><span class="cl">    <span class="p">}</span>
903</span></span><span class="line"><span class="cl"><span class="p">};</span>
904</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">World</span> <span class="p">{</span>
905</span></span><span class="line"><span class="cl">    <span class="c1">// 【5.3】相比于5.2,这里粒子类型使用了SoA
906</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">ParticleSoA</span> <span class="n">smokeParticles</span><span class="p">;</span>
907</span></span><span class="line"><span class="cl">    <span class="n">ParticleSoA</span> <span class="n">fireParticles</span><span class="p">;</span>
908</span></span><span class="line"><span class="cl">
909</span></span><span class="line"><span class="cl">    <span class="c1">// 【5.3】这里不做改造的原因是性能瓶颈不在这里
910</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">Rocket</span><span class="o">&gt;</span> <span class="n">rockets</span><span class="p">;</span>
911</span></span><span class="line"><span class="cl">    <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">optional</span><span class="o">&lt;</span><span class="n">Emitter</span><span class="o">&gt;&gt;</span> <span class="n">smokeEmitters</span><span class="p">;</span>
912</span></span><span class="line"><span class="cl">    <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">optional</span><span class="o">&lt;</span><span class="n">Emitter</span><span class="o">&gt;&gt;</span> <span class="n">fireEmitters</span><span class="p">;</span>
913</span></span><span class="line"><span class="cl">
914</span></span><span class="line"><span class="cl">    <span class="c1">// ...
915</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">};</span>
916</span></span></code></pre></div><p>改造后的更新函数:</p>
917<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">World</span><span class="o">::</span><span class="n">update</span><span class="p">(</span><span class="kt">float</span> <span class="n">dt</span><span class="p">)</span>
918</span></span><span class="line"><span class="cl"><span class="p">{</span>
919</span></span><span class="line"><span class="cl">    <span class="c1">// 【5.3】相比于5.2,这里使用了SoA
920</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">auto</span> <span class="n">updateParticles</span> <span class="o">=</span> <span class="p">[</span><span class="o">&amp;</span><span class="p">](</span><span class="n">ParticleSoA</span><span class="o">&amp;</span> <span class="n">soa</span><span class="p">)</span> <span class="p">{</span>
921</span></span><span class="line"><span class="cl">
921        <span class="k">const</span> <span class="k">auto</span> <span class="n">nParticles</span> <span class="o">=</span> <span class="n">soa</span><span class="p">.</span><span class="n">positions</span><span class="p">.</span><span class="n">size</span><span class="p">();</span>
922</span></span><span class="line"><span class="cl">        <span class="c1">// 迭代里面的操作是混合的,主要是Vittorio做的实验发现这里混合迭代性能较好
923</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="k">for</span> <span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">size_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">nParticles</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span> <span class="p">{</span> 
924</span></span><span class="line"><span class="cl">            <span class="n">soa</span><span class="p">.</span><span class="n">velocitys</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">+=</span> <span class="n">soa</span><span class="p">.</span><span class="n">accelerations</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">*</span> <span class="n">dt</span><span class="p">;</span>
925</span></span><span class="line"><span class="cl">            <span class="n">soa</span><span class="p">.</span><span class="n">positions</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">+=</span> <span class="n">soa</span><span class="p">.</span><span class="n">velocitys</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">*</span> <span class="n">dt</span><span class="p">;</span>
926</span></span><span class="line"><span class="cl">            <span class="n">soa</span><span class="p">.</span><span class="n">scales</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">+=</span> <span class="n">soa</span><span class="p">.</span><span class="n">scaleRates</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">*</span> <span class="n">dt</span><span class="p">;</span>
927</span></span><span class="line"><span class="cl">            <span class="n">soa</span><span class="p">.</span><span class="n">opacities</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">+=</span> <span class="n">soa</span><span class="p">.</span><span class="n">opacityChanges</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">*</span> <span class="n">dt</span><span class="p">;</span>
928</span></span><span class="line"><span class="cl">            <span class="n">soa</span><span class="p">.</span><span class="n">rotations</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">+=</span> <span class="n">soa</span><span class="p">.</span><span class="n">rotationVelocities</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">*</span> <span class="n">dt</span><span class="p">;</span>
929</span></span><span class="line"><span class="cl">        <span class="p">}</span>
930</span></span><span class="line"><span class="cl">    <span class="p">};</span>
931</span></span><span class="line"><span class="cl">    <span class="n">updateParticles</span><span class="p">(</span><span class="n">smokeParticles</span><span class="p">);</span>
932</span></span><span class="line"><span class="cl">    <span class="n">updateParticles</span><span class="p">(</span><span class="n">fireParticles</span><span class="p">);</span>
933</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">optional</span><span class="o">&lt;</span><span class="n">Emitter</span><span class="o">&gt;&amp;</span> <span class="nl">e</span> <span class="p">:</span> <span class="n">smokeEmitters</span><span class="p">)</span> <span class="p">{</span> 
934</span></span><span class="line"><span class="cl">
934        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">e</span><span class="p">.</span><span class="n">has_value</span><span class="p">())</span>
935</span></span><span class="line"><span class="cl">            <span class="k">continue</span><span class="p">;</span>
936</span></span><span class="line"><span class="cl">        <span class="n">e</span><span class="o">-&gt;</span><span class="n">position</span> <span class="o">+=</span> <span class="n">e</span><span class="o">-&gt;</span><span class="n">velocity</span> <span class="o">*</span> <span class="n">dt</span><span class="p">;</span>
937</span></span><span class="line"><span class="cl">        <span class="n">e</span><span class="o">-&gt;</span><span class="n">velocity</span> <span class="o">+=</span> <span class="n">e</span><span class="o">-&gt;</span><span class="n">acceleration</span> <span class="o">*</span> <span class="n">dt</span><span class="p">;</span>
938</span></span><span class="line"><span class="cl">        <span class="n">e</span><span class="o">-&gt;</span><span class="n">spawnTimer</span> <span class="o">+=</span> <span class="n">e</span><span class="o">-&gt;</span><span class="n">spawnRate</span> <span class="o">*</span> <span class="n">dt</span><span class="p">;</span>
939</span></span><span class="line"><span class="cl">        <span class="k">for</span> <span class="p">(;</span> <span class="n">e</span><span class="o">-&gt;</span><span class="n">spawnTimer</span> <span class="o">&gt;=</span> <span class="mf">1.f</span><span class="p">;</span> <span class="n">e</span><span class="o">-&gt;</span><span class="n">spawnTimer</span> <span class="o">-=</span> <span class="mf">1.f</span><span class="p">)</span> <span class="p">{</span>
940</span></span><span class="line"><span class="cl">            <span class="c1">// 【5.3】这里不得不分别对每个字段单独添加,比较恶心但没办法 🫥
941</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="c1">// 也许SoA对象自行封装一个emplace函数,来完成这个操作会更好,不重要~
942</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="n">smokeParticles</span><span class="p">.</span><span class="n">positions</span><span class="p">.</span><span class="n">emplace_back</span><span class="p">(...);</span>
943</span></span><span class="line"><span class="cl">            <span class="n">smokeParticles</span><span class="p">.</span><span class="n">velocitys</span><span class="p">.</span><span class="n">emplace_back</span><span class="p">(...);</span>
944</span></span><span class="line"><span class="cl">            <span class="c1">// ...
945</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="n">smokeParticles</span><span class="p">.</span><span class="n">rotationVelocities</span><span class="p">.</span><span class="n">emplace_back</span><span class="p">(...);</span>
946</span></span><span class="line"><span class="cl">        <span class="p">}</span>
947</span></span><span class="line"><span class="cl">    <span class="p">}</span>
948</span></span><span class="line"><span class="cl"><span class="p">}</span>
949</span></span></code></pre></div><p>清理函数也是带有SoA风格的:</p>
950<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="c1">// 首先定义一个简单的帮助方法
951</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">soaEraseIf</span><span class="p">(</span><span class="n">ParticleSoA</span><span class="o">&amp;</span> <span class="n">soa</span><span class="p">,</span> <span class="k">auto</span><span class="o">&amp;&amp;</span> <span class="n">predicate</span><span class="p">)</span>
952</span></span><span class="line"><span class="cl"><span class="p">{</span>
953</span></span><span class="line"><span class="cl">    <span class="n">std</span><span class="o">::</span><span class="n">
953size_t</span> <span class="n">n</span> <span class="o">=</span> <span class="n">soa</span><span class="p">.</span><span class="n">positions</span><span class="p">.</span><span class="n">size</span><span class="p">();</span>
954</span></span><span class="line"><span class="cl">    <span class="n">std</span><span class="o">::</span><span class="n">size_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0u</span><span class="p">;</span>
955</span></span><span class="line"><span class="cl">    <span class="k">while</span> <span class="p">(</span><span class="n">i</span> <span class="o">&lt;</span> <span class="n">n</span><span class="p">)</span> <span class="p">{</span>
956</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">predicate</span><span class="p">(</span><span class="n">soa</span><span class="p">,</span> <span class="n">i</span><span class="p">))</span>
957</span></span><span class="line"><span class="cl">        <span class="p">{</span>
958</span></span><span class="line"><span class="cl">            <span class="o">++</span><span class="n">i</span><span class="p">;</span>
959</span></span><span class="line"><span class="cl">            <span class="k">continue</span><span class="p">;</span>
960</span></span><span class="line"><span class="cl">        <span class="p">}</span>
961</span></span><span class="line"><span class="cl">        <span class="c1">// 🔖这里很妙,需细品
962</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="o">--</span><span class="n">n</span><span class="p">;</span>
963</span></span><span class="line"><span class="cl">        <span class="n">soa</span><span class="p">.</span><span class="n">forAllVectors</span><span class="p">([</span><span class="o">&amp;</span><span class="p">](</span><span class="k">auto</span><span class="o">&amp;</span> <span class="n">vec</span><span class="p">)</span> <span class="p">{</span> <span class="n">vec</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">vec</span><span class="p">[</span><span class="n">n</span><span class="p">];</span> <span class="p">});</span>
964</span></span><span class="line"><span class="cl">    <span class="p">}</span>
965</span></span><span class="line"><span class="cl">    <span class="n">soa</span><span class="p">.</span><span class="n">forAllVectors</span><span class="p">([</span><span class="o">&amp;</span><span class="p">](</span><span class="k">auto</span><span class="o">&amp;</span> <span class="n">vec</span><span class="p">)</span> <span class="p">{</span> <span class="n">vec</span><span class="p">.</span><span class="n">resize</span><span class="p">(</span><span class="n">n</span><span class="p">);</span> <span class="p">});</span>
966</span></span><span class="line"><span class="cl"><span class="p">}</span>
967</span></span><span class="line"><span class="cl">
968</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">World</span><span class="o">::</span><span class="n">cleanup</span><span class="p">()</span>
969</span></span><span class="line"><span class="cl"><span class="p">{</span>
970</span></span><span class="line"><span class="cl">    <span class="k">auto</span> <span class="n">hasNegativeOpacity</span> <span class="o">=</span> <span class="p">[](</span><span class="k">const</span> <span class="n">ParticleSoA</span><span class="o">&amp;</span> <span class="n">soa</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">size_t</span> <span class="n">i</span><span class="p">)</span> <span class="p">{</span>
971</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">soa</span><span class="p">.</span><span class="n">opacities</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">&lt;=</span> <span class="mf">0.f</span><span class="p">;</span>
972</span></span><span class="line"><span class="cl">    <span class="p">};</span>
973</span></span><span class="line"><span class="cl">    <span class="n">soaEraseIf</span><span class="p">(</span><span class="n">smokeParticles</span><span class="p">,</span> <span class="n">hasNegativeOpacity</span><span class="p">);</span>
974</span></span><span class="line"><span class="cl">    <span class="n">soaEraseIf</span><span class="p">(</span><span class="n">fireParticles</span><span class="p">,</span> <span class="n">hasNegativeOpacity</span><span class="p">);</span>
975</span></span><span class="line"><span class="cl">    <span class="c1">// ...
976</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
977</span></span></code></pre></div><p>收益也是非常的amazing:</p>
978<p><figure 
979	
980		class="gallery-image" 
981		style="
982			flex-grow: 178; 
983			flex-basis: 429px"
984	>
985	<a href="/p/cppcon25pratical-data-oriented-design-in-c-/19.png" data-size="1516x847">
986		<img src="/p/cppcon25pratical-data-oriented-design-in-c-/19.png"
987			width="1516"
988			height="847"
989			srcset="/p/cppcon25pratical-data-oriented-design-in-c-/19_hu2484940084883594255.png 480w, /p/cppcon25pratical-data-oriented-design-in-c-/19_hu10252215922456562818.png 1024w"
990			loading="lazy"
991			>
992	</a>
993	
994</figure></p>
995<p>对于SoA分为多个数组字段所带来的不易维护问题,Vittorio提供了一些反射实现:</p>
996<p><figure 
997	
998		class="gallery-image" 
999		style="
1000			flex-grow: 178; 
1001			flex-basis: 429px"
1002	>
1003	<a href="/p/cppcon25pratical-data-oriented-design-in-c-/20.png" data-size="1512x845">
1004		<img src="/p/cppcon25pratical-data-oriented-design-in-c-/20.png"
1005			width="1512"
1006			height="845"
1007			srcset="/p/cppcon25pratical-data-oriented-design-in-c-/20_hu4569994164053250784.png 480w, /p/cppcon25pratical-data-oriented-design-in-c-/20_hu12051097503537907757.png 1024w"
1008			loading="lazy"
1009			>
1010	</a>
1011	
1012</figure></p>
1013<p>
1013并提及这些实现将在C++26有真正的官方支持~😲</p>
1014<h4 id="54-one-more-thing">5.4 one more thing</h4>
1015<ul>
1016<li>DoD和SoA并没有直接联系,SoA只是一种优化策略而非终极答案</li>
1017</ul>
1018<p><figure 
1019	
1020		class="gallery-image" 
1021		style="
1022			flex-grow: 187; 
1023			flex-basis: 450px"
1024	>
1025	<a href="/p/cppcon25pratical-data-oriented-design-in-c-/21.png" data-size="1477x787">
1026		<img src="/p/cppcon25pratical-data-oriented-design-in-c-/21.png"
1027			width="1477"
1028			height="787"
1029			srcset="/p/cppcon25pratical-data-oriented-design-in-c-/21_hu8230816338949601031.png 480w, /p/cppcon25pratical-data-oriented-design-in-c-/21_hu9981513575824479441.png 1024w"
1030			loading="lazy"
1031			>
1032	</a>
1033	
1034</figure></p>
1035<ul>
1036<li>DoD也并非终极答案,OOP在更上层的业务中仍起到优秀的抽象作用(注、OOP是外壳,DoD是引擎,这句话个人觉得非常好地描述了两者的合适的关系)</li>
1037</ul>
1038<p><figure 
1039	
1040		class="gallery-image" 
1041		style="
1042			flex-grow: 182; 
1043			flex-basis: 436px"
1044	>
1045	<a href="/p/cppcon25pratical-data-oriented-design-in-c-/22.png" data-size="1504x826">
1046		<img src="/p/cppcon25pratical-data-oriented-design-in-c-/22.png"
1047			width="1504"
1048			height="826"
1049			srcset="/p/cppcon25pratical-data-oriented-design-in-c-/22_hu6524415594779468322.png 480w, /p/cppcon25pratical-data-oriented-design-in-c-/22_hu5508765578805549557.png 1024w"
1050			loading="lazy"
1051			>
1052	</a>
1053	
1054</figure></p>
1055<h3 id="6-总结">6 总结</h3>
1056<ul>
1057<li>考量性能于最初</li>
1058<li>扁平、精简的数据结构</li>
1059<li>妥当的分类而非各种类型标识</li>
1060<li>在需要批处理时考虑SoA</li>
1061</ul>
1062<p><figure 
1063	
1064		class="gallery-image" 
1065		style="
1066			flex-grow: 182; 
1067			flex-basis: 438px"
1068	>
1069	<a href="/p/cppcon25pratical-data-oriented-design-in-c-/23.png" data-size="1506x825">
1070		<img src="/p/cppcon25pratical-data-oriented-design-in-c-/23.png"
1071			width="1506"
1072			height="825"
1073			srcset="/p/cppcon25pratical-data-oriented-design-in-c-/23_hu11643206164514503739.png 480w, /p/cppcon25pratical-data-oriented-design-in-c-/23_hu11282351607161604186.png 1024w"
1074			loading="lazy"
1075			>
1076	</a>
1077	
1078</figure></p>
1079<p><em>Data drives design</em></p>
1080<ul>
1081<li>数据是设计的核心驱动力。这意味着在进行系统或代码设计时,应以实际的数据结构、数据流和使用场景为依据,而不是凭空想象或遵循固定模式。</li>
1082</ul>
1083<p><em>Target the machine</em></p>
1084<ul>
1085<li>编程的目标是让机器高效运行。强调要关注底层硬件和执行效率,编写能够被机器高效执行的代码,避免过度抽象或不必要的开销。</li>
1086</ul>
1087<p><em>It’s a spectrum, not dogma</em></p>
1088<ul>
1089<li>软件开发中的各种方法和技术是一个连续谱系,而非非黑即白的教条。鼓励灵活选择适合当前场景的技术方案,不盲目追随某种“最佳实践”。</li>
1090</ul>
1091<p><em>Embrace Modern C++</em></p>
1092<ul>
1093<li>积极采用现代C++(如C++11及以上版本)的新特性,例如智能指针、lambda表达式、范围for循环等,以提升代码的安全性、可读性和性能。</li>
1094</ul>
1095<p><em>Pragmatism wins</em></p>
1096<ul>
1097<li>实用主义胜出。最终决定因素是实际效果:是否解决了问题、是否易于维护、是否高效稳定。不要为了追求“优雅”而牺牲实用性。</li>
1098</ul>
1099<h3 id="7-qa">7 Q&amp;A</h3>
1100<blockquote>
1101<p>Q&amp;A环节,一些问题也颇有启发,浅做记录(记录我理解到的意思而非忠实地翻译问答对话)。</p>
1102</blockquote>
1103<p>Q:我认为一些习于面向对象的程序员会认为DoD丢失了一些对象间的联系,例如在OOP中 <code>Rocket</code> 和 <code>Emitter</code> 有着清晰的联系,而DoD中这种联系似乎丢失了。对此你怎么看?</p>
1104<p>A:仅仅观察数据本身的话,我能get到这个问题点。我想指出虽然我们在示例中使用整型索引进行关联性的表达,但实际也可以使用一些 <code>handle</code> 或强类型定义来使得关联性的表达更清晰。另一方面,我觉得当我们操作数据时,这种联系实际上是变清晰而非减
1104弱,当我们阅读 <code>update</code> 中的循环时,我们不止知道 <code>Emitter</code> 和 <code>Rocket</code> 之间存在联系,还知道这种联系是为了什么、产生了什么作用。因此,在这种视角下我们得大于失。</p>
1105<hr>
1106<p>Q:你如何比较DoD和OOP二者之间的可测试性( <em>testability</em> )?</p>
1107<p>A:我认为DoD(给可测试性)带来了一些很好的收益,例如由于我们的一切都只是数据,我们可以很简单地构造、存储、加载不同的测试数据,而OOP相对来说就没有那么简单地能对对象进行序列化/反序列化的操作。然而OOP仍在一些地方表现更好,例如mocking或依赖注入。因此需要将OOP应用在合适的层级(即前面提到的较高的层级)。</p>
1108<hr>
1109<p>Q:我感觉到似乎批量处理型的任务更能取得DoD的所有益处,你如何看待非批量处理型的任务,例如随机访问或仅处理少数铺展开的实体?</p>
1110<p>A:依然取决于你的需求,正如演讲中所说,也有很多非常成功的游戏或应用不必关注这些东西(指DoD)。在运用DoD时,如果你的数据天然是一种图或类似的结构,处理起来就需要更巧妙一些,因为它们不像我们的demo那么简单且符合直觉,但DoD仍然有地方施展身手。例如当你在层序遍历一棵树的时候,节点可能可以组织为一个数组(以得到性能收益),因为遍历的顺序是明确的。我的建议仍然是当性能是一项关键需求,且你希望确保你的架构是高效的,此时应考虑数据访问的方式,并把数据组织为使得缓存命中的几率最大化的形式。</p>
1111<hr>
1112<p>Q:当你把数据组织为扁平形式,实际上是产生了一些重复数据,而似乎不如简单地继承自基类。此种做法是否有弊端?毕竟后者使得数据没有重复,内存只有一份。</p>
1113<p>A:这会取决于你希望表达到多清晰。如果我直接看一个结构,我希望能够直接看到这个东西就有这么多字段,而如果你继承了,阅读代码时就需要再往上翻一层,虽问题不大,毕竟也是一个额外步骤。另一方面是,一旦引入继承关系,就有人可能开始依赖这层关系,使得你对继承体系中的代码修改变得困难,引入了一定的耦合。</p>
1114
1115</section>
1116
1117
1118
1119    <footer class="article-footer">
1120    
1121    <section class="article-tags">
1122        
1123            <a href="/tags/%E6%8A%80%E6%9C%AF/">技术</a>
1124        
1125    </section>
1126
1127
1128    </footer>
1129
1130
1131
1132    
1133</article>
1134
1135    
1136
1137    <aside class="related-contents--wrapper">
1138    
1139    
1140</aside>
1141
1142     
1143    
1144        
1145    <div id="gitalk-container"></div>
1146
1147<link rel="stylesheet" href="/resources/gitalk.css">
1148<script src="/resources/gitalk.min.js"></script>
1148
1149
1150<script>
1151    const gitalk = new Gitalk({
1152        clientID: "60cf432a89bc92763c12",
1153        clientSecret: "6725615388bdb06c4050a0f6f3fa47d017fcc8d9",
1154        repo: "BlogComments",
1155        owner: "SGS4ever",
1156        admin: ["SGS4ever"],
1157        distractionFreeMode: false, 
1158        id: '', 
1159    });
1160    (function () {
1161        if (
1162            ["localhost", "127.0.0.1"].indexOf(window.location.hostname) != -1
1163        ) {
1164            document.getElementById("gitalk-container").innerHTML =
1165                "Gitalk comments not available by default when the website is previewed locally.";
1166            return;
1167        }
1168        gitalk.render("gitalk-container");
1169    })();
1170</script>
1170
1171
1172
1173
1174    
1175
1176    <footer class="site-footer">
1177    <section class="copyright">
1178        &copy; 
1179        
1180            2020 - 
1181        
1182        2025 XR_G&#39;s Blog
1183    </section>
1184    
1185    <section class="powerby">
1186        
1187            有朋自远方来,不亦说乎? <br/>
1188         <br />
1189        
1190    </section>
1191</footer>
1192
1193
1194    
1195<div class="pswp" tabindex="-1" role="dialog" aria-hidden="true">
1196
1197    
1198    <div class="pswp__bg"></div>
1199
1200    
1201    <div class="pswp__scroll-wrap">
1202
1203        
1204        <div class="pswp__container">
1205            <div class="pswp__item"></div>
1206            <div class="pswp__item"></div>
1207            <div class="pswp__item"></div>
1208        </div>
1209
1210        
1211        <div class="pswp__ui pswp__ui--hidden">
1212
1213            <div class="pswp__top-bar">
1214
1215                
1216
1217                <div class="pswp__counter"></div>
1218
1219                <button class="pswp__button pswp__button--close" title="Close (Esc)"></button>
1220
1221                <button class="pswp__button pswp__button--share" title="Share"></button>
1222
1223                <button class="pswp__button pswp__button--fs" title="Toggle fullscreen"></button>
1224
1225                <button class="pswp__button pswp__button--zoom" title="Zoom in/out"></button>
1226
1227                
1228                
1229                <div class="pswp__preloader">
1230                    <div class="pswp__preloader__icn">
1231                        <div class="pswp__preloader__cut">
1232                            <div class="pswp__preloader__donut"></div>
1233                        </div>
1234                    </div>
1235                </div>
1236            </div>
1237
1238            <div class="pswp__share-modal pswp__share-modal--hidden pswp__single-tap">
1239                <div class="pswp__share-tooltip"></div>
1240            </div>
1241
1242            <button class="pswp__button pswp__button--arrow--left" title="Previous (arrow left)">
1243            </button>
1244
1245            <button class="pswp__button pswp__button--arrow--right" title="Next (arrow right)">
1246            </button>
1247
1248            <div class="pswp__caption">
1249                <div class="pswp__caption__center"></div>
1250            </div>
1251
1252        </div>
1253
1254    </div>
1255
1256</div>
1256<script 
1257                src="https://cdn.jsdelivr.net/npm/[email protected]/dist/photoswipe.min.js"integrity="sha256-ePwmChbbvXbsO02lbM3HoHbSHTHFAeChekF1xKJdleo="crossorigin="anonymous"
1258                defer="true"
1259                >
1260            </script>
1260<script 
1261                src="https://cdn.jsdelivr.net/npm/[email protected]/dist/photoswipe-ui-default.min.js"integrity="sha256-UKkzOn/w1mBxRmLLGrSeyB4e1xbrp4xylgAWb3M42pU="crossorigin="anonymous"
1262                defer="true"
1263                >
1264            </script>
1264<link 
1265                rel="stylesheet" 
1266                href="https://cdn.jsdelivr.net/npm/[email protected]/dist/default-skin/default-skin.css"integrity="sha256-c0uckgykQ9v5k&#43;IqViZOZKc47Jn7KQil4/MP3ySA3F8="crossorigin="anonymous"
1267            ><link 
1268                rel="stylesheet" 
1269                href="https://cdn.jsdelivr.net/npm/[email protected]/dist/photoswipe.css"integrity="sha256-SBLU4vv6CA6lHsZ1XyTdhyjJxCjPif/TRkjnsyGAGnE="crossorigin="anonymous"
1270            >
1271
1272            </main>
1273    
1274        <aside class="sidebar right-sidebar sticky">
1275            <section class="widget archives">
1276                <div class="widget-icon">
1277                    <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-hash" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
1278  <path stroke="none" d="M0 0h24v24H0z"/>
1279  <line x1="5" y1="9" x2="19" y2="9" />
1280  <line x1="5" y1="15" x2="19" y2="15" />
1281  <line x1="11" y1="4" x2="7" y2="20" />
1282  <line x1="17" y1="4" x2="13" y2="20" />
1283</svg>
1284
1285
1286
1287                </div>
1288                <h2 class="widget-title section-title">目录</h2>
1289                
1290                <div class="widget--toc">
1291                    <nav id="TableOfContents">
1292  <ul>
1293    <li><a href="#1-一个简单的demo">1 一个简单的Demo</a></li>
1294    <li><a href="#2-题外话内存和cache">2 题外话:内存和Cache</a></li>
1295    <li><a href="#3-oop实现的性能问题分析">3 OOP实现的性能问题分析</a></li>
1296    <li><a href="#4-设计哲学的对比">4 设计哲学的对比</a></li>
1297    <li><a href="#5-对demo的优化">5 对Demo的优化</a>
1298      <ul>
1299        <li><a href="#51-优化1">5.1 优化1</a></li>
1300        <li><a href="#52-优化2">5.2 优化2</a></li>
1301        <li><a href="#53-优化3">5.3 优化3</a></li>
1302        <li><a href="#54-one-more-thing">5.4 one more thing</a></li>
1303      </ul>
1304    </li>
1305    <li><a href="#6-总结">6 总结</a></li>
1306    <li><a href="#7-qa">7 Q&amp;A</a></li>
1307  </ul>
1308</nav>
1309                </div>
1310            </section>
1311
1312            
1313            
1314            <a id="back-to-top" href="#">
1315                <img src="/img/top_hu10958439986251666570.png" />
1316            </a>
1317        </aside>
1318    
1319
1320        </div>
1321        
1321<script 
1322                src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vibrant.min.js"integrity="sha256-5NovOZc4iwiAWTYIFiIM7DxKUXKWvpVEuMEPLzcm5/g="crossorigin="anonymous"
1323                defer="false"
1324                >
1325            </script>
1325<script type="text/javascript" src="/ts/main.js" defer></script>
1325
1326
1327
1328<link rel="preconnect" href="https://fonts.googleapis.com">
1329<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
1330<link href="https://fonts.googleapis.com/css2?family=Long+Cang&family=Ma+Shan+Zheng&family=Noto+Sans+SC:wght@300&family=Noto+Serif+SC:wght@300&family=Zhi+Mang+Xing&display=swap" rel="stylesheet">
1331
1332    </body>
1333</html>

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.