-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsearch.xml
3442 lines (1657 loc) · 972 KB
/
search.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title>【Graphics-2022】Texture 纹理</title>
<link href="/posts/2022-texture/"/>
<url>/posts/2022-texture/</url>
<content type="html"><![CDATA[<h2 id="定义">定义</h2><p>图形学中有两个与图片相关的概念,分别是 Image(贴图)和 Texture(纹理),可以这样区分它们:</p><ul><li>Image:内存意义上的图片,例如 <code>tga, png, jpg</code></li><li>Texture:GPU意义上的图片,例如 <code>RGBA8</code> 格式的一对像素组合</li></ul><p>因此 Texture 一定是以 GPU像素格式来申明的,并且需要规定 <code>Sampler</code> 的规则。</p><h2 id="Color-Space">Color Space</h2><p>参考阅读:</p><ul><li><a href="https://luhao.wiki/posts/rtr-5/#%E6%98%BE%E7%A4%BA%E7%BC%96%E7%A0%81">【RealtimeRendering】5. Shading Basic > ColorSpace | Luhao’s Blog</a></li><li><a href="https://zhuanlan.zhihu.com/p/66558476">Gamma、Linear、sRGB 和Unity Color Space,你真懂了吗? - 知乎</a></li></ul><h2 id="Texture-Mapping">Texture Mapping</h2><p>将贴图的坐标映射到模型表面,通常采用一种 $u, v$ 坐标的形式,取值为 $[0, 1]$ 的浮点数。<br><img src="../../images/Graphics2022-Texture_18326.png" alt=""><br>贴图一般都是规规矩矩的方形,但模型的表面却差异很大,这套映射关系是如何选择的?下面展示三种基本的 texture mapping:</p><ul><li>Planar:只考虑两个维度的<strong>平面映射</strong></li><li>Cubic:考虑三个维度的<strong>立方体映射</strong></li><li>Cylindrical:<strong>柱状映射</strong><br><img src="../../images/Graphics2022-Texture_45479.png" alt=""></li></ul><p>此时,一个<strong>从贴图到模型</strong>的完整映射流程是:<br><img src="../../images/Graphics2022-Texture_26581.png" alt=""></p><h3 id="Texture-Tiling">Texture Tiling</h3><p>上面讨论的是 uv 映射 最完美的情况(即 texture Sampler 每个采样点与 Image <strong>一一对应</strong>),然而实际应用中,这两者很难对上。</p><p><strong>Q:当采样点落在多个 Image 像素之间怎么办?</strong></p><ol><li><p>如果粗暴地选取最近的一个像素,那么会导致严重的走样,不可取!称为 <a href="https://en.wikipedia.org/wiki/Nearest-neighbor_interpolation"><strong>Nearest-neighbor Filtering</strong></a></p></li><li><p>传统的方案是增加到四次采样,并在这四个像素之间作插值,称为 <a href="https://en.wikipedia.org/wiki/Bilinear_interpolation"><strong>Bilinear interpolation</strong></a><br><img src="../../images/Graphics2022-Texture_48684.png" alt=""></p></li><li><p>考虑到当图片与屏幕呈倾斜角度时,我们需要<strong>更多的采样点</strong>来铺满该区域!这是硬件层面实现的 <a href="https://en.wikipedia.org/wiki/Anisotropic_filtering"><strong>Anisotropic Filtering</strong></a>。其中 <code>4X AF</code> 表示需要多采样4倍的pixel;<code>16X AF</code> 同理<br><img src="../../images/anisotropic.png" alt=""></p></li></ol><hr><p>参考阅读:<a href="https://en.wikipedia.org/wiki/Texture_filtering">Texture filtering - Wikipedia</a><br>越多的采样数往往意味着性能开销更大,下面是各种方案的采样数消耗:</p><table><thead><tr><th style="text-align:center">Filering</th><th style="text-align:center">Samples</th></tr></thead><tbody><tr><td style="text-align:center">nearest-neighbor</td><td style="text-align:center">1</td></tr><tr><td style="text-align:center">bilinear</td><td style="text-align:center">4</td></tr><tr><td style="text-align:center">trilinear</td><td style="text-align:center">8</td></tr><tr><td style="text-align:center">AF 4X</td><td style="text-align:center">32</td></tr><tr><td style="text-align:center">AF 16X</td><td style="text-align:center">128</td></tr></tbody></table><h3 id="Texture-Tiling-2">Texture Tiling</h3><p>当你翻出一张陈年老照片(分辨率很低),想将它作为 4k 显示屏的壁纸时,问题就出现了:<strong>待采样的贴图不足以铺满整个屏幕</strong>。<br>此时就需要 <code>Texture Tiling</code>,即考虑怎么将贴图放大、堆叠,传统的做法有如下几种:</p><ul><li>下图忽略了 <code>Repeat</code> 模式<br><img src="../../images/Graphics2022-Texture_31282.png" alt=""></li></ul><h3 id="Interpolation">Interpolation</h3><p>考虑下面这种情形,我们需要将半张图片渲染到 (下左图)经过透视变换的区域内。而 Vertex Shading 阶段<strong>只会接受三个顶点(及其UV属性)</strong>,那么实际采样中应该如何确定区域内每个像素的 uv 取值呢?<br><img src="../../images/Graphics2022-Texture_47237.png" alt=""></p><p>显然这需要选取合适的插值方法!我们可以将这个问题规纳为一般情形(上右图):</p><ul><li>已知三个顶点 $A, B, C$ 及其 $uv$ 属性</li><li>求三角形内任意顶点的 $uv$ 取值</li></ul><p>考虑最简单的线性插值方案:<br>$$<br>P(x, y) = \alpha A + \beta B + \gamma C \\<br>\alpha + \beta + \gamma = 1<br>$$<br>通常会获得如下的效果(下图中),显然是错误的。<br><img src="../../images/Graphics2022-Texture_12104.png" alt=""></p><p>上图的原因用一句话概括为:<strong>ViewSpace 的线性变换不等价于 ScreenSpace的线性变换。</strong><br>解决方案可阅读如下链接,暂时没理解公式:</p><ul><li><a href="https://lxjk.github.io/2017/06/10/Conversion-between-View-Space-Linear-and-Screen-Space-Linear.html">Conversion between View Space Linear and Screen Space Linear - Eric’s Blog</a></li><li><a href="https://www.comp.nus.edu.sg/~lowkl/publications/lowk_persp_interp_techrep.pdf">comp.nus.edu.sg/~lowkl/publications/lowk_persp_interp_techrep.pdf</a></li></ul>]]></content>
<categories>
<category> Graphics </category>
<category> Graphics </category>
</categories>
<tags>
<tag> Graphics </tag>
</tags>
</entry>
<entry>
<title>【RealtimeRendering】5. Shading Basic</title>
<link href="/posts/rtr-5/"/>
<url>/posts/rtr-5/</url>
<content type="html"><![CDATA[<blockquote><p><code>Pixel Shading</code> 阶段会决定每个像素最终的颜色和透明度,而决定这些颜色的公式,就是所谓的 <code>Shading Model</code></p></blockquote><h3 id="前言">前言</h3><p>这一部分其实更多是面对 TA 向的内容,掌握 Shading 基础,是各种 真实感渲染、卡通渲染… 的技术基础。虽然说大多商业引擎已经实现了非常少成熟的 PBR、NBR … 渲染框架,网上各种解决方案、优化技术也非常普遍,但是作为引擎(图形)开发程序,掌握这些基础的 Shading 知识仍然是非常必要的。</p><p>23年底时,曾面试 <code>Garena</code> 的图形引擎岗位,其中一个基础的问题没答完整:“请介绍 Phong Shading 的渲染公式”。这个经历提醒自己,一定要夯实基础,万丈高楼平地起,积沙成塔,只有底部足够扎实,才能爬得更高。</p><h2 id="Shading-Models">Shading Models</h2><p><code>RealTimeRendering</code> 作者选择将这个章节放在 <code>Textures</code> 纹理之前,我觉得是有失偏颇的。应该是先有纹理贴图,继而发现朴素的贴图缺少光照,表现非常虚假、平淡,继而引入一系列逐渐复杂的 Shading Models,其目的是 <strong>优化纹理在不同光照、视角、及法线下的视觉表现</strong>,只不过通过一些 <strong>计算调参</strong> 的手段罢了。</p><p>我们从 <strong>第一性原理</strong> 出发,先考虑最简单的原则:</p><h4 id="1">1.</h4><p>将采样的 <code>Texture</code> 简单粗暴的渲染出来,即 Shading Model 是:<br>$$C_{shading}=C_{tex}$$</p><p>参考 <a href="https://learnopengl.com/Getting-started/Camera">LearnOpenGL</a>的示例,得到的效果是<strong>明显不真实</strong>的:<br><img src="../../images/RTR-5-Shading_45121.png" alt=""></p><h4 id="2">2.</h4><p>为什么这个最简单的 Shading Model 渲染出的结果具有强烈的不真实感?想象真实世界的物体,最主要的特征是(太阳)光线带来的强烈视觉感,而这个视觉感与远近没有明显的关系,影响最大的因素是<strong>与光线的夹角</strong>。</p><p>如果难以理解,想象一束光照射在镜子上:</p><ul><li>当垂直入射时,是刺眼的白色(假设白光)</li><li>当(接近)水平入射时,是接近物体本身的颜色</li></ul><p>根据这个从物理世界观察到的规律,我们进一步优化 Shading Model:<br>$$<br>C_{Shading} =<br>\begin{cases}<br>C_{Light} & if \ angle \geq N^\circ \\<br>C_{Tex} & else\\<br>\end{cases}<br>$$<br>实际计算可以作一些插值,以太阳光照射水面为例,效果大概如下所示:<br><img src="../../images/RTR-5-Shading_19391.png" alt=""></p><h4 id="3-Gooch-Shading">3. Gooch Shading</h4><p>上面都是自己的瞎扯,这里尝试进入正题。<code>Gooch Shading</code> 是一个足够简单但经典的着色模型,它将光照颜色分为两部分区域:</p><ul><li>法线越接近光照:使用暖色调</li><li>法线越远离光照:使用冷色调</li></ul><p>具体公式如下,其中 <code>2(n*l)n - l</code> 是用来计算 <code>l</code> 相对于法线的反射向量,在 shader 可以使用 <code>reflect</code> 函数代替:<br><img src="../../images/RTR-5-Shading_53237.png" alt=""><br><img src="../../images/RTR-5-Shading_12110.png" alt=""><br><img src="../../images/RTR-5-Shading_52010.png" alt=""></p><div class="admonition note"><p class="admonition-title">如何区分 冷色调 和 暖色调?</p><ul><li><code>Cool</code>:偏蓝色的</li><li><code>Warm</code>:偏红色、橘色的</li></ul></div><hr><p>鉴于这个特征,<code>Gooch Shading</code> 又被称为 <code>Cool to Warm Shading</code>,实际效果如图所示:<br><img src="../../images/RTR-5-Shading_57107.png" alt=""></p><h4 id="4-Lambertian-Shading">4. Lambertian Shading</h4><p><a href="https://luhao.wiki/posts/shading/#font-color-750000-Lambertian-Reflection-%E2%AD%90-font">【GAMES101】Shading - Lambertian Shading | Luhao’s Blog</a></p><h4 id="5-Phong-Shading">5. Phong Shading</h4><p><a href="https://luhao.wiki/posts/shading/#font-color-750000-Blinn-Phong-Reflection-%E2%AD%90-font">【GAMES101】Shading - Phong Shading | Luhao’s Blog</a></p><h2 id="Light-Sources">Light Sources</h2><p>想要更好地描述光源对于物体表面渲染的影响,我们需要对光照这个行为进行定量地分析,这里列出 RTR 书中的分析思路:</p><h4 id="1-光-射线">1. 光 == 射线</h4><p>先将 光照对表面的影响,可视化为 <strong>一组平行的射线</strong>,同时射线的密度代表光照的强度。</p><ul><li>对于一个固定光源,不同射线的间距是固定的 $d$</li><li>垂直入射时,到达表面的长度是 $d$</li><li>倾斜入射时,到达表面的长度是 $d / cos\theta$</li><li>背面入射时($\theta \geq 90$),到达表面的长度是 $0$</li></ul><p>因为 $n \cdot l = cos\theta$,所以(单位)光照的影响长度为 $d / (n \cdot l)$<br><img src="../../images/RTR-5-Shading_20955.png" alt=""></p><p>考虑到光照随面积的分布是均匀的,当单位光照影响的长度越大,其所受光的影响也就越弱。<br>假设 $d$ 是一个单位为1的值,那么(单位)光照的影响强度为 $max(0, n \cdot l)$</p><h4 id="2-有光-无光">2. 有光 & 无光</h4><p>PS:这里有一点困惑,假设自然界完全无光的情况下,那么物体表现应该也是纯黑色?<br>本章将物体表面区分为两种状态:无光、有光的环境,而最终的呈现是这两种结果的组合。</p><ul><li>无光:$f_{unlit}$,阴影中死黑的部分</li><li>有光:$f_{lit}$,取决于光照公式的选取,如 <code>Lambert、Phong...</code></li><li>光源颜色:$c_{lit}$,通过缩放还可以表示光照的强度</li></ul><p>此时,对于一个光源的光照公式可以表示为:<br>$$C_{shading} = f_{unlit} + c_{light} f_{lit}$$<br>如果扩展到多个光源,那么有:<br>$$C_{shading} = f_{unlit} + (\sum_{i=1}^{n}c_{light} f_{lit})$$<br>结合前面 <code>Shading Model</code> 介绍的 <code>Gooch Shading</code> 模型,我们可以为上面公式套上:</p><ul><li>$f_{unlit} = (0, 0, 0)$</li><li>$f_{lit} = f_{Gooch\ Shading}$</li></ul><p>此时就得到一个完整的光照模型啦~ 但是考虑到自然界存在着无时无刻不发挥作用的间接光,这里将 $f_{unlit}$ 取为全死黑,显然是不科学的,后续可以继续改进。</p><h4 id="2-1-方向光">2.1 方向光</h4><p><code>Directional Light</code> 是一个最简单的光源模型,象征自然界的太阳。它具有如下特征:</p><ul><li>方向 $l$ 恒定,因此又称为 平行光</li><li>光源颜色(强度)$c_{light}$ 固定,不考虑任何衰减(伟大的太阳!)</li><li>没有位置的概念<br><img src="../../images/RTR-5-Shading_14188.png" alt=""></li></ul><p>因此在 <code>shading</code> 中可以考虑如下定义:</p><pre><code class="language-c++">struct DirectionalLight { vec3 direction; vec3 ambient; vec3 diffuse; vec3 specular;};void main(){ vec3 lightDir = normalize(-light.direction); [...]}</code></pre><h4 id="2-2-点光">2.2 点光</h4><p><code>Point Light</code> 象征自然界的电灯泡,它的特征如下:</p><ul><li>方向 $l$ 向所有方向均匀发射光线</li><li>强度 $c_{light}$ 随距离衰减</li><li>有明确的位置概念</li></ul><p>RTR 书中使用下图解释 <strong>强度随距离衰减</strong>。考虑到单位光线影响的范围,随距离 $r$ 的增大而平方增长,因此:光线强度与 $1 / r^{2}$ 成正比。<br>注意,这个衰减并不是因为 <strong>能量随传播的衰减</strong>。<br><img src="../../images/RTR-5-Shading_44438.png" alt=""></p><p>因此,对于<strong>距离为 $r$ 处的光源强度</strong>可以表示为:(这里选取了一个参考值 $c_{light_{0}}$,表示光源在距离为 $r_{0}$时的光照参数,你可以通过度量等方式定义它)<br>$$c_{light_{r}} = c_{light_{0}} (\frac{r_{0}}{r})^{2}$$<br>这个公式称为 <a href="https://en.wikipedia.org/wiki/Inverse-square_law">Inverse-square law - Wikipedia</a>,即平方反比定律,物理学中存在非常多类似的例子,例如:万有引力定律、库仑定律…<br>在实际的工程使用中,它存在一个非常明显的问题,即当 <strong>$r$ 无穷趋近于0时</strong>(或者干脆取值为0),那么光源强度会是一个趋向无穷大的取值,这显然是无法接受的。对此,商业引擎有几种优化手段:</p><ul><li><strong>Unreal:距离叠加一个极小的数,实际取值是 $1 cm$</strong><br>$$c_{light_r}=C_{light_0}{\frac{r_{0}^{2}}{r^{2}+\epsilon}}.$$</li><li><strong>CryEngine:限定 $r$ 的最小值</strong><br>$$c_{light_{r}}=c_{light_{0}} (\frac{r_{0}} {max(r,r_{m i n})})^{2}$$</li></ul><div class="admonition note"><p class="admonition-title">物理学解释</p><ul><li>从物理学角度解释,<code>CryEngine</code> 的做法更科学。因为$r_{min}$在物理中表示发光物体的物理半径,比它还小的距离,对应着光源内部的着色表面,这在现实中是不可能发生的。</li></ul></div><p>实际开发中,为了性能考虑,我们希望 <strong>光照强度在某个有限的距离处,能够乖乖地衰减到0</strong>,因此会引入一些距离衰减函数来实现这一目的,其中甚至包括指数衰减,这里不详细介绍。</p><p>另一方面,为了<strong>让点光的效果更贴近现实</strong>,<code>OGRE Engine</code> 引入一些复杂的衰减函数来实现点光,参考阅读:</p><ul><li><a href="https://wiki.ogre3d.org/tiki-index.php?page=-Point+Light+Attenuation">-Point Light Attenuation | Ogre Wiki</a></li></ul><h4 id="2-3-聚光灯">2.3 聚光灯</h4><p>TODO: <code>SpotLight</code></p><div class="admonition note"><p class="admonition-title">多种光源对比</p><ul><li>从左到右依次为:平行光、点光(无衰减)、聚光灯(有衰减)</li><li><img src="../../images/RTR-5-Shading_47228.png" alt=""></li></ul></div><h2 id="Anti-Aliasing">Anti-Aliasing</h2><p>抗锯齿的部分,看 GAMES101 时有作总结,这里不详细展开。不过有机会还是希望实践落地 SMAA、TAA …</p><ul><li><a href="https://luhao.wiki/posts/games101-aa/">【GAMES101】Anti-Aliasing | Luhao’s Blog</a></li><li><a href="https://www.highperformancegraphics.org/wp-content/uploads/2017/Retrospective/HPG2017_Reshetov_MLAARetrospective.pdf">[HPG 2017] MLAA from 2009 to 2017.pdf</a></li></ul><h2 id="半透明">半透明</h2><p>TODO:这部分讨论的是,光线穿过半透明物体的效果。</p><h2 id="显示编码">显示编码</h2><p>推荐阅读:</p><ul><li><a href="https://zhuanlan.zhihu.com/p/66558476">Gamma、Linear、sRGB 和Unity Color Space,你真懂了吗? - 知乎</a></li><li><a href="https://gwb.tencent.com/community/detail/120396">伽马空间与线性空间详解-腾讯游戏学堂</a></li><li><a href="https://kinematicsoup.com/news/2016/6/15/gamma-and-linear-space-what-they-are-how-they-differ#:~:text=non%2Dlinear%20space-,Gamma%20Space,shades%20better%20than%20lighter%20shades.">Gamma and Linear Space - What They Are and How They Differ</a></li></ul><h4 id="Gamma-Space">Gamma Space</h4><p><code>Gamma Space</code> 将颜色输出为 2.2 次幂,所谓伽马矫正是指如下公式:<br>$$C_{gamma} = (C_{linear})^{2.2}$$<br>为什么会引入伽马矫正?一般有两个原因:</p><ul><li>传统 CRT 显示器的设计原因</li><li>人眼对暗部辨识度高于明部</li></ul><p>以第2点为例,我们可以理解为:如果在 0% 和 50% 明暗处分别增加 10% 的亮度,那么<strong>人眼对前者的感知更加明显</strong>,这也意味着人眼对暗处更敏感。换言之,我们应该给暗处更大的存储、展示细节。<br><img src="../../images/RTR-5-Shading_50654.png" alt=""></p><hr><p>如下图,中间是标准的线性空间,右侧是 <code>Gamma-2.2</code> 空间,可以这么理解:</p><ul><li>左一:给亮部更大精度</li><li>右一:给暗部更大精度</li></ul><p>显然<strong>右一更符合人眼的观感</strong>,这正解释了为什么要将图像转化到 Gamma 空间!另外,我们购买的显示器一般也是 Gamma2.2 空间(又称为 sRGB)。<br><img src="../../images/RTR-5-Shading_57381.png" alt=""></p><hr><p>虽然 Gamma Space 更符合人眼的观感,但是它不利于 Shading 的计算,因此一个正常的渲染流程是:</p><ul><li>png/jpg:sRGB</li><li>shading:linear</li><li>display:sRGB(取决于硬件,一般是 sRGB 空间)<br><img src="../../images/RTR-5-Shading_54553.png" alt=""></li></ul><div class="admonition note"><p class="admonition-title">sRGB</p><ul><li><code>sRGB</code> 又称为 Gamma-0.45空间,即会给亮部更多的精度和细节。</li><li>当 <code>sRGB</code> 叠加一次 Gamma矫正 之后,就会得到一个正确的 Linear空间。</li></ul></div><h2 id="参考资料">参考资料</h2><ul><li><a href="https://www.logiconsole.com/real-time-rendering-4/">Real-Time Rendering 4th Edition学习笔记(四) | Logiconsole</a></li><li><a href="https://ciel1012.github.io/2019/07/07/rtr5/">Shading Basics - 就决定是你了 | Ciel’s Blog</a></li></ul>]]></content>
<categories>
<category> RealtimeRendering </category>
<category> RealtimeRendering </category>
</categories>
<tags>
<tag> Graphics </tag>
<tag> C++ </tag>
<tag> OpenGL </tag>
</tags>
</entry>
<entry>
<title>【Graphics-2022】图形API</title>
<link href="/posts/18QJ9Y1/"/>
<url>/posts/18QJ9Y1/</url>
<content type="html"><![CDATA[<div class="admonition note"><p class="admonition-title">推荐阅读:</p><ul><li><a href="https://v.netease.com/evideo/video_course/show?course_id=18956">2022图形引擎-内部资料</a></li></ul></div><h2 id="概要">概要</h2><p>为什么需要图形API?</p><ul><li>暴露图形硬件的功能(GPU),并抽象出高->低维度的接口</li><li>用作 realtime rendering</li></ul><p>图形API发展历史:(主要是15年左右诞生 Metal、DX12、Vulkan)</p><ul><li><img src="../../images/rtr-graphics-api.png" alt=""></li></ul><p>现代图形API的发展方向:</p><ul><li>降低 CPU 性能瓶颈</li><li>多线程</li><li>优越的开发能力</li></ul><p>从 High-Level -> Low-Level 角度来看:</p><ul><li>High-Level:意味着封装层次更高,性能较差</li><li>Low-Level:以为这封装较低,学习成本陡峭<br><img src="../../images/2022-graphics-API_24124.png" alt=""></li></ul><p>整个完整的图形引擎调用栈:</p><h2 id=""><img src="../../images/2022-graphics-API_09984.png" alt=""></h2><h3 id="图形API-vs-GPU">图形API vs GPU</h3><ul><li>图形API = Resource Manager + Commnad Producer</li><li>GPU = Commnad Consumer + Execute async</li></ul><p>从 <strong>生产者消费者模型</strong> 理解:</p><ul><li>图形API:从CPU端 创建资源 + 产生一系列 DrawCalls</li><li>GPU:指令的消耗 和 异步执行。</li></ul><blockquote><p><em><strong>GPU = Async Execute Engine</strong></em> 一个异步执行的引擎<br>CPU 永远领先 GPU 1~3 帧<br><img src="../../images/2022-graphics-API_24172.png" alt=""></p></blockquote><hr><h3 id="Single-Commnad">Single Commnad</h3><p>一个基本的 CPU 渲染指令,应该包含如下要素:</p><ul><li>Command ID</li><li>操作数(注意有大小限制,后面会说)</li><li>CPU地址(GPU可访问的)</li><li>GPU地址</li></ul><p><img src="../../images/2022-graphics-API_09197.png" alt=""></p><hr><div class="admonition warning"><p class="admonition-title">问题</p><ul><li><strong>如何向 Commnad 传递一波数据?</strong></li></ul></div><ul><li>Method 1:直接传递操作数 <code>(<64 Bytes)</code>,一般只有特定的API支持这么做,例如 <a href="https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkCmdPushConstants.html"><code>vkCmdPushConstants</code></a><ul><li><img src="../../images/2022-graphics-API_45028.png" alt=""></li></ul></li><li>Method 2:拷贝到显存(GPU可访问的),然后传地址进去。注意避免 CPU写 + GPU读 的情形发生<ul><li><img src="../../images/2022-graphics-API_13901.png" alt=""></li></ul></li><li>Method 3:在Method 2的基础上,通过 <code>Blit</code> 将数据拷到 GPU内存,然后传GPU地址<ul><li>如果 <code>Blit</code> 一次,但是 <code>Read</code> 多次,那么收益比较高</li><li><img src="../../images/2022-graphics-API_20940.png" alt=""></li></ul></li></ul><h2 id="API结构">API结构</h2><p>前面说过,图形API分为两大类:Resource Manager + Command Producer:<br><img src="../../images/2022-graphics-API_50546.png" alt=""></p><p>以具体的API为例:<br><img src="../../images/2022-graphics-API_20155.png" alt=""></p><h2 id="API-一帧的调用">API 一帧的调用</h2><ol><li>Create Resource<ul><li>Texture / VertexBuffer / IndexBuffer …</li></ul></li><li>Set RenderPass</li><li>Set PipelineState<ul><li>Shader / BlendState / DepthState</li></ul></li><li>Bind Shader Resources<ul><li>Uniform / Buffer …</li></ul></li><li>DrawCall</li><li>Present</li></ol><h2 id="1-Resources">1. Resources</h2><p>第一步是资源管理,对应 <code>Create Resources</code> 的部分。根据资源的类型还可以细分如下:<br><img src="../../images/2022-graphics-API_13785.png" alt=""></p><h3 id="Resource-Memory">Resource Memory</h3><p>上图框出来的部分,需要注意**内存的开销<em>j</em>( Buffer、Image)。对于图形API中的内存分配方式,一共分为两种:</p><ul><li>自动分配:DX11/OpenGL/Metal</li><li>手动分配:DX12/Vulkan/Metal</li></ul><p>对于手动分配的方式,有一个好处是 <code>resouce aliasing</code>,多个资源可以共用一块内存(真节省呀!),参考阅读:</p><ul><li><a href="https://gpuopen-librariesandsdks.github.io/VulkanMemoryAllocator/html/resource_aliasing.html">Vulkan Memory Allocator: Resource aliasing (overlap)</a></li><li><a href="https://gpuopen-librariesandsdks.github.io/D3D12MemoryAllocator/html/resource_aliasing.html">Direct3D 12 Memory Allocator: Resource aliasing (overlap)</a></li></ul><hr><h3 id="内存架构">内存架构</h3><p><strong>对于PC端</strong>,CPU 和 GPU 都有独立的内存,有如下特点:</p><ul><li>GPU 内存传输快于 CPU (主要是带宽高,数据bus设计原因)</li><li>GPU/CPU 之间传输很慢</li></ul><p><img src="../../images/2022-graphics-API_29146.png" alt=""><br><br></p><p><strong>对于移动端</strong>,CPU 和 GPU 共用一张内存,有如下特点:</p><ul><li>考虑到低功耗(带宽变小),内存传输非常慢</li><li>GPU 部分有 <code>Tiled Memory</code> 的架构优化</li></ul><p><img src="../../images/2022-graphics-API_38013.png" alt=""></p><hr><h3 id="Memory-Types">Memory Types</h3><p>图形API中有不同的内存类型,区分如下:</p><ul><li><code>Default</code>:默认是 GPU 内存,不支持 CPU访问<ul><li>大多数资源的选择:buffers、textures、rt</li></ul></li><li><code>Dynamic</code>:指 CPU只写、GPU只读 的内存<ul><li>需要CPU每帧更新的资源( todo:举个栗子)</li></ul></li><li><code>Readback</code>:指 CPU只读、GPU只写 的内存(使用情况比较少)</li><li><code>Memoryless</code>:适用于 TBR 架构</li></ul><p><img src="../../images/2022-graphics-API_05483.png" alt=""></p><h2 id="2-Render-Pass">2. Render Pass</h2><p>对于 <code>Render Pass</code> 的定义:不切换 FrameBuffer 的连续 Drawcalls。<br><code>Render Pass</code> 的性能需要关注两个操作:</p><ul><li><code>Load Action</code>:注意 DontCare/Clear 是没有带宽开销的,只有 Load 需要注意</li><li><code>Store Action</code>:将 FrameBuffer 写回到 主存,开销大头</li></ul><h2 id="3-Pipeline-State">3. Pipeline State</h2><p>通俗说 <code>Pipeline State</code> 作用是控制渲染状态,现代API通常将所有状态打包为一个大的 <code>PSO</code> (Pipeline State Object)。<br>如下是 <code>DX11</code> 的示例,需要手动设置 Shaders、Blend、DS、Raster 等所有状态。<br><img src="../../images/2022-graphics-API_25420.png" alt=""></p><h2 id="Shader-Compilation">Shader Compilation</h2><ul><li><ol><li>将 shader source 编译成 跨平台的 bytecode/glsl</li></ol></li><li><ol start="2"><li>将 bytecode/glsl 编译成 machine code(ISA)</li></ol></li><li><ol start="3"><li>Patch the Shader (Todo):例如为 Binning Pass 产生仅包含 pos 的 vertex buffer</li></ol></li></ul><p><img src="../../images/2022-graphics-API_46759.png" alt=""></p><hr><h3 id="重要点">重要点</h3><ul><li>Pipeline State 创建非常慢</li><li>Shader 直到 Pipeline State 创建完,才能明确所有属性</li><li>尽可能早地创建 <code>PSO</code></li></ul><h2 id="4-Shader-Resource-Binding">4. Shader Resource Binding</h2><p>这阶段是为了给 shader 设置参数,以 <code>DX11</code> 为例:</p><ul><li>每帧重复设置</li><li>逐个参数设置</li></ul><p><img src="../../images/2022-graphics-API_17849.png" alt=""></p><p>反思一下,<code>DX11</code> 的设置方式太落后了,作为现代API,可以分配一块GPU内存专门用于 shader 参数传递(核心思想是 cache):</p><p><img src="../../images/2022-graphics-API_43538.png" alt=""></p><h2 id="5-Draw-Calls">5. Draw Calls</h2><p>核心是如下三个参数:<br><img src="../../images/2022-graphics-API_40089.png" alt=""></p><ul><li>Indexed:需要绘制的 vertex、index 的 下标</li><li>Instanced:一个 drawcall 绘制多个物体,CPU端 需要 buffer 存储不同的信息(如 vertex,pos)</li><li>Indirect:<code>GPU-driven rendering</code>,同样是一个 drawcall 绘制多个物体,区别于前者地方在于,是 GPU端 填充 buffer 信息</li></ul><h2 id="6-Swapchain">6. Swapchain</h2><p><img src="../../images/2022-graphics-API_36591.png" alt=""></p><p>Swapchain 会持有如下资源,呈现最终的画面(Presentation)需要如下步骤:</p><ol><li>从 Swanchain 取一张 image</li><li>rendering 整个渲染流程</li><li>设置到 composition(因为有多个窗口)</li></ol><p><img src="../../images/2022-graphics-API_06244.png" alt=""></p><div class="admonition warning"><p class="admonition-title">TODO</p><ul><li>SwapChain、Presentation 这几个部分没听懂</li></ul></div><h2 id="优化">优化</h2><p>应该同时对 GPU、CPU 进行 Profile,如果不是性能瓶颈,请不要 <strong>过度优化</strong>。</p><p>基本的优化手段:</p><ul><li>降低 DrawCalls:Instanced、Indirect</li><li>规划绘制顺序:eg. 通过 state、distance 等排序</li><li>降低 带宽:合图、mipmap …</li></ul>]]></content>
<categories>
<category> Graphics </category>
<category> Graphics </category>
</categories>
<tags>
<tag> Graphics </tag>
<tag> OpenGL </tag>
</tags>
</entry>
<entry>
<title>【Graphics-2022】渲染管线</title>
<link href="/posts/2GFAABV/"/>
<url>/posts/2GFAABV/</url>
<content type="html"><![CDATA[<p>IMR、TBR、Pipeline…<span id="more"></span></p><div class="admonition note"><p class="admonition-title">推荐阅读:</p><ul><li><a href="https://v.netease.com/evideo/video_course/show?course_id=18829">2022图形引擎-内部资料</a></li><li><a href="https://www.cnblogs.com/timlly/p/15546797.html#1246-hidden-surface-removal">剖析虚幻渲染体系(12)- 移动端专题Part 2(GPU架构和机制) - 0向往0 - 博客园</a></li><li>Todo: 与 <a href="https://luhao.wiki/posts/gpu/">【硬件】GPU架构 | Luhao's Blog</a> 这篇文章合并</li></ul></div><div class="markmap-container" style="height:250px"> <svg data="{"t":"root","d":0,"v":"","c":[{"t":"heading","d":1,"p":{"lines":[0,1]},"v":"硬件架构","c":[{"t":"heading","d":2,"p":{"lines":[1,2]},"v":"<code>Discrete</code>"},{"t":"heading","d":2,"p":{"lines":[2,3]},"v":"<code>Coupled</code>"}]},{"t":"heading","d":1,"p":{"lines":[3,4]},"v":"IMR","c":[{"t":"heading","d":2,"p":{"lines":[4,5]},"v":"Warp"}]},{"t":"heading","d":1,"p":{"lines":[5,6]},"v":"TBR","c":[{"t":"heading","d":2,"p":{"lines":[6,7]},"v":"Mali: Index-Driven Vertex Shading"},{"t":"heading","d":2,"p":{"lines":[7,8]},"v":"Adreno: Binning Pass"},{"t":"heading","d":2,"p":{"lines":[8,9]},"v":"PowerVR: Hidden Sureface Removal"}]},{"t":"heading","d":1,"p":{"lines":[9,10]},"v":"Pipeline","c":[{"t":"heading","d":2,"p":{"lines":[10,11]},"v":"Render Pass"}]},{"t":"heading","d":1,"p":{"lines":[11,12]},"v":"Frame Graph"},{"t":"heading","d":1,"p":{"lines":[12,13]},"v":"Compute Pipeline"}],"p":{}}"></svg></div><h2 id="硬件架构">硬件架构</h2><p>如果仅从 CPU、GPU、Memory 三者的角度考虑硬件架构,那么可以分为如下两类:</p><ul><li>(左)<strong>分离式架构</strong>,CPU和GPU有各自独立的内存和 Cache,通过 <code>PCI-e</code> 总线通讯。其特点是:高带宽、高延迟,性能瓶颈是数据传输。主要应用于 PC 和 手机。</li><li>(右)<strong>耦合式架构</strong>,CPU和GPU共享内存和 Cache。主要应用于 PS4 等游戏主机。</li></ul><p><img src="../../images/2022-rendering-pipeline_35740.png" alt=""></p><h2 id="IMR">IMR</h2><p><code>Immediate Mode Rendering</code>,通常指 PC 端的GPU渲染架构,其特点是:所有渲染管线中的读写操作,都直接由 GPU(紫色) 和 显存(深灰色) 之间完成,如图所示:<br><img src="../../images/2022-rendering-pipeline_23750.png" alt=""></p><p>其中 ↑ 表示读取显存,如 Vertex 阶段需要读取 几何(顶点)信息,而 ↓ 表示写回显存,如 Visibility 测试阶段需要写 Depth-Buffer。这些会带来巨大的带宽开销,IMR结构通过引入 L1、L2 Cache 之类的结构来尝试优化这部分带宽。</p><div class="admonition warning"><p class="admonition-title">移动端</p><ul><li>由于 高带宽 导致的 高功耗,IMR架构对 移动端 的 <strong>性能</strong> 是致命打击!</li><li>因此 移动端 大都选择 性能友好 的TBR架构</li></ul></div><hr><p>如何理解 GPU 的 <strong>高度并行化</strong>?</p><ul><li>参考阅读: <a href="/posts/gpu/#GPU-%E6%9E%B6%E6%9E%84%E5%8F%91%E5%B1%95%E5%8E%86%E5%8F%B2">##GPU 架构发展历史</a></li><li>假设 GPU 每个 core 一次只处理一个 vertex,(NVIDIA架构)每个 SM 中包含 32 个cores,那么就可以同时处理 32 个 vertex,这些统一称为一个 <code>Warp</code></li><li>因此 <strong>Warp</strong> 的数量直接决定 GPU 的性能<br><img src="../../images/2022-rendering-pipeline_25797.png" alt=""></li></ul><hr><ul><li><strong>问题</strong>:<code>Warp</code> 之间是可以并行执行的吗?为什么图示是并发执行呢?<br><img src="../../images/2022-rendering-pipeline_27939.png" alt=""></li></ul><hr><ul><li>光栅化阶段,最小单位是一个 <code>Quard</code>(即包含4个像素)。以下图为例,其中绿色表示通过光栅化,黄色表示因为 <code>Quard</code> 而被保留的像素。</li><li>之所以使用 <code>Quard</code> 的形式,是因为便于计算 <code>ddx, ddy</code>。这个在计算 mipmap 的时候有奇效。<br><img src="../../images/2022-rendering-pipeline_35771.png" alt=""></li></ul><h2 id="TBR">TBR</h2><p><code>Tiled Based Rendering</code>,一般用于移动端GPU(例如 Mali),通过引入 <code>Tiled Memory</code> 降低带宽的读写功耗。</p><ul><li>优化点:先写 <code>Tiled Meomry</code>,再实现 <code>Blend</code> …,最后再写入 <code>DDR</code></li><li>参考阅读:<a href="https://litmin.github.io/2019/08/31/%E8%AF%91-The-Mali-GPU-An-Abstract-Machine-Part-2-Tile-based-Rendering/">[译]The Mali GPU: An Abstract Machine, Part 2 - Tile-based Rendering | Litmin的笔记</a></li><li>参考阅读:<a href="https://www.cnblogs.com/timlly/p/15546797.html">剖析虚幻渲染体系(12)- 移动端专题Part 2(GPU架构和机制) - 0向往0 - 博客园</a></li></ul><p><img src="../../images/2022-rendering-pipeline_53347.png" alt=""></p><hr><p>很多及计算机技术,都是通过引入一个中间件,<code>TBR</code>架构就是典型的例子。</p><ul><li>面对一个巨大的汉堡,一口吞不下去(IMR带宽高)</li><li>尝试将汉堡咬成多个小口,慢慢吃下去(TBR低带宽)</li></ul><hr><h3 id="Index-Driven-Vertex-Shading-Mali">Index-Driven Vertex Shading (Mali)</h3><p><a href="https://www.cnblogs.com/timlly/p/15546797.html#12411-index-driven-vertex-shading"><strong>IDVS</strong></a> 是 Mali GPU 的优化技术,考虑到传统的 vertex shading,即使经过 50% backface culling(也可能是 frontface culling),顶点的写入数量也剩 50%:</p><p><img src="../../images/2022-rendering-pipeline_08888.png" alt=""></p><p><strong>优化关键点在于</strong>,既然有 50% 顶点是注定要被 Culling 掉的,那么为什么要写入内存呢?<br>因此 Mali GPU 将 Vertex Shading 细分为两个阶段,分别是:</p><ul><li><code>Position Shading</code>:位置着色,发生在 Culling 之前,只转换顶点位置,因此输入只有 pos</li><li><code>Varying Shading</code>:可变着色,发生在 Culling 之后,只处理通过 Culling 的顶点的其他信息、操作</li></ul><p>假设 顶点 Vertex 的分布是,按照 pos、npos 的比例为 <code>1:1</code>:(通常情况更为复杂,eg. position、uv、normal …)</p><ul><li>缺陷是:需要CPU将这些信息分开存储<br><img src="../../images/2022-rendering-pipeline_34173.png" alt=""></li></ul><p>那么 IDVS 技术起码节省 <strong>50% 比例的 npos</strong>:<br><img src="../../images/2022-rendering-pipeline_16452.png" alt=""></p><p>进一步分析,通常 <code>Varing shading</code> 开销一定是大于 <code>Position Shading</code>,所以 拆分两阶段,能够让 <code>Varing shading</code> 充分享受到 Culling 的收益。</p><hr><h3 id="Binning-Pass-Adreno">Binning Pass (Adreno)</h3><p>参照前文,既然某些 Vertex 压根不会参与最终的渲染,那么有没有可能,这些都不用写到内存呢?(节省掉 0.5 Vertex Write)。<br>Adreno 引入一个 Binning Pass 的技术,在 Vertex Shading 阶段(同样先是只处理 pos),将所有的 visibilty 写到内存中,避免了顶点信息的写入。</p><p><img src="../../images/2022-rendering-pipeline_37944.png" alt=""></p><hr><p>Adreno 基于 Binning Pass 的渲染架构如下图,继续悟一悟:</p><p><img src="../../images/2022-rendering-pipeline_13639.png" alt=""></p><hr><h3 id="Hidden-Sureface-Removal-PowerVR">Hidden Sureface Removal (PowerVR)</h3><p><a href="https://www.cnblogs.com/timlly/p/15546797.html#1246-hidden-surface-removal"><strong>HSR</strong></a> 可以直接理解为 隐藏表面的剔除,传统的 OverDraw 是通过 Early-Z 避免,而 HSR 可以无视绘制顺序避免 OverDraw。</p><ul><li>如下由远及近绘制时,不会对遮挡的像素进行任何剔除,但是 PowerVR 做到了!</li></ul><p><img src="../../images/2022-rendering-pipeline_45218.png" alt=""></p><hr><p>HSR 实际做法是,在光栅化之后,<strong>写入一个 Depth-Buffer(降分辨率) 到 Tiled Memory</strong>,在后续的深度测试中也会依据它进行一些 Culling。</p><p><img src="../../images/2022-rendering-pipeline_46152.png" alt=""></p><h2 id="RenderPass">RenderPass</h2><p>对于 RenderPass 的定义:对于渲染管线的一次完整执行(连续地往 FrameBuffer 绘制对象的一组行为),如下:</p><p><img src="../../images/2022-rendering-pipeline_10671.png" alt=""></p><hr><p>RenderPass 的组织形式对于性能影响非常大,如下绘制一个Scene(电视机中是另一个Scene的画面):<br><img src="../../images/2022-rendering-pipeline_55447.png" alt=""></p><p>下图展示了两种绘制方式:</p><ul><li>左侧:3 Pass,先绘制音响,后从 FrameBuffer 切到另一个 RenderTarget,来绘制另一个Scene,接着再切回 FrameBuffer 绘制整个Scene</li><li>右侧:2 Pass,先绘制另一个Scene到某个RenderTarget上,然后切回 FrameBuffer,绘制音响和电视,并将之前的 RenderTarget 当做 Texture Load 过来即可</li></ul><p><img src="../../images/2022-rendering-pipeline_41030.png" alt=""></p><p>分析以上两者的绘制开销:</p><ul><li>左侧:3 x store,2 x load</li><li>右侧:2 x store,1 x load</li></ul><p>由于 <code>Load/Store</code> 操作依赖带宽的开销非常高,因此 2 Pass 的方案显然性能更加友好。</p><h2 id="Frame-Graph">Frame Graph</h2><p>为了优化 Render Pass 的绘制顺序,更好的配置 <code>Load/Store</code> 的关系,可以引入 <code>Frame Graph</code> 一种有向无环图:</p><ul><li>有利于渲染并行、排序</li><li>有利于性能优化,降低 Load/Store 开销</li></ul><p><img src="../../images/2022-rendering-pipeline_06607.png" alt=""></p><h2 id="Compute-Pipeline">Compute Pipeline</h2><p>Compute Pipeline 的意义:</p><ul><li>在多线程组(thread group)之间共享内存。eg. 在一个 <code>Quad</code> 内计算 <code>sum, agg ...</code></li><li>读写 Buffers(例如 FrameBuffer、顶点数组 …)</li></ul><p>如下是一个 DX12 的渲染管线示例:<br><img src="../../images/2022-rendering-pipeline_37432.png" alt=""><br><br><br><br><br><br><br><br></p>]]></content>
<categories>
<category> Graphics </category>
<category> Graphics </category>
</categories>
<tags>
<tag> Graphics </tag>
</tags>
</entry>
<entry>
<title>日志:2023年12月</title>
<link href="/posts/2023-12/"/>
<url>/posts/2023-12/</url>
<content type="html"><![CDATA[<div class="hbe hbe-container" id="hexo-blog-encrypt" data-wpm="Oh, this is an invalid password. Check and try again, please." data-whm="OOPS, these decrypted content may changed, but you can still have a look."> <script id="hbeData" type="hbeData" data-hmacdigest="bdb51053d99e2f462ae67d1ce2d789fc6419bfc2931878fdd9d55d607e487035">b3dc07a81f6459d120ce338ccca550463faa708b9b4d89df9ab14ba4edd809f60b32d15b5672ce84c5d84744f7f475def1af0b6bc61f7912497bd2791ef1396a9bcf0f6940df039a86f945fe75789ec31640a75d317a8abe2cb024b5dba86bfd34e4bd9e09040b251abc3b0833849a256141b075696212f5ac257b95a02cb70c1fb6c1dfa0046c0c4e50ab7d1f131976105380b96af668fb462122068b4e163024b111dfb4e1f0aa6d1310bb266a940e5f9b2c00815b2e6c6d5c94f661147bf2459a3e427b075205c997af090859dcc0f6a2bc00983015e3974124a37d5bb1246404d8c761a8886603c25b1334d4f7ea42576e8e59e547705dba29592e2cc07c9622468e190523cc899663124a6fee22d9435faa3710d6d951e354c26503744daa3dc9b0a026e22639fbc17e6a82f8312dc213906656a56642d95a9fdfc114c85c8540fab525816f8958fd5b6f53fc3ef8119c6b4d6d470437fd6854f6edf1e1fd2c0667d781a5ca4b2eaa5b59d08cf6f503e6c1c1716df8cddbaa7077355b9c35f46447967fa3afcb8c5e35b4e8be3e75565d1a79a4648dfb95e597a6438ca9be1f00c1d2cb89bf9e31e6636d0b2cbd3b93991a6287036cdd3191edd7d8c0a4a0ad5645174358886f2809699484f27ef51d5750abddb669777bc9c2551ae9e1f8f702d5c768c891ef0f863fee9f04a8bfa74057c9c4726dd0dc296eeac2ee9abb448bde504c647aa4a6aa58adb988e016da96fcb237dafb64428b08d8e555e3ffcaa58d922ade78131e10c0268c50acf1b088cf0fbbb1244cf55393cb140bc82719b916667da06ea72f4acc93d1158429630cfd3f55eeab3bcdfdc1f04ad2a3582fd8caf885efc77037ffc2881207a72f5ce14a84a89420d71f2faac7a4d5c5951eea7857660e838551e953814f74ba86c1af6a32f10faf1f664cb9e71bc8cc70ab2f1f27b225ab4b49c3239e553b52eab6cb83c52af2e426e3bef0cb3e40f889303d05b7395082ef77df9914fa9488908bf9c53de603ff1bf4fade763ffac122df68870941919379609bdbc9496fef29ccf6be4a4ce63bd0f5f3776155c10bbbcbcb2912fabe4fb11a0114033af102435c18d2c90e2ae7c1900332e2e1febea5073592e62393b6838dda6f1259a55e4b96d38f3177c567fd15fe49dc6101deabc8902f620ed3934afc4101ba3938cd2fac1d50ad93bb14e59c7291fea9d4093e0b53eee165a0e95805046078351f84adc55f717d3c45cb5f411a8530ad2aa3fac0e7e585810114c4026c7f80cc0ea864ff027fe2680e0c395613d41e37723981d361ff6ee94b5b557bc27694b8bdd685d4ac7da7c10d9313e4fe0b527225ac4dfaa297c60eca2d9cc219803e87a778e67d34ef1babff9b90f64b1a2f83a0dae204e054dc09fe8c315e0d99c83f381c852e232bc133afd86f4405b475ffd98161bd72d86142779ae8ce14b7c62cb4b3c7b630b641752173e5264ac8881eaf6454aaf711a10151842f176a7c78bafe7459aeb3bb3a4c4eebca83c2f4f89f72c174edc64c6d203422546275db1892f94c8127f800f4c3703aaab1870f9b15ddae3d991242919c22650f7a5db341194fcede17fdcb103e474bfd85adaca0c500848d478807cd8d2dd2c03a52d955bb1178aa5cfc0ce09f850e8490e5e3557866d33e2fa0d9adcf8cca0d34330cbb825c8f80b82b0c3367e0aa2b0a70ff43d0a36070819d0ebf25ee0923df066f97616de4914f9c553fa7deb7a5749a9fecb8d38c3875f1a02ce323637d8c52d0efa2648a4afdc329c597d0606b5ecb668b451f7af43d75ea1301cc41347ac427683e6e741daffab57a269c6f955e551a02910b6f01f12b0198bdace5564b14bd93666a3a990410bd6b915552dc60f214bbc76fa227ec168fd67b291c884e2f91c4ccc7f57d2985ce42b45acfda732ac8b545760dd69743c833ea224484d87eee20d032b8125917289cc3aa06941fd4e8a0d1c51ae69d26c2ab94b9f832eb282ba49f14d2407d5d9e9ec73683bd45af6447e521d5f37ab0e0311c972a9f55bb4f0f9f136691edaadbde4364628cdeb85484b79c64d7b78f2739e20bfd6fca0d85ef8c6a53fdc1e9f431e40a932d7d5bd69aec16615845273ea5e32b841e334ef57d425bb34c8c419b04c8c8f72e84a3a27d7bd43d2d5af942daef994d655b7e65b7332fa77abc971ee34db4b0bf180c26c64ac576808300712580c105966b456805c45262ae40f12566c448bf154af8401710033cb9b63c26f1a017cdeaf8658bd9def6e1ebc7ca65b1b7a2cbb60faafd4dfc195983ab2eeab90cd9c3fd8fa2f3a506fda37682bb846543df3dea39200e8ae30bda3c2834571dcd7b2fbd1e7d3791f0aad045c052235a5df58ee4c151412e5b97ec760f77f907dbf203842f6b421e5a737f396ecfc85624bf58cc70aab2aee0acc18d35c46a8577d4b3efd239b2a868b413262d75b9d6aeffaf18c3fb02d1f97aa433e8f422e4ea77f3b3faba6b09e747f009520f39a44336b7c61e2b214b20931bfac09dd7034a281bd0a6f3a7694256cc4bef53343dd59785c3afff662a8f6bfd776f8d8ec265559d2cdfc1a1574ddd229d7a67001b87013d42db8b1ba73e4d641bfe8f3b37068716e172559cc5e4b98532a5bd2315e81c66a07b68a9a4ff0a2728c45d423dd33b3a03e2fddaa30edbfa838f0670e2ee9d181e3d086fd3afd4849523f71d8e4d188ed0f4bd2296dee768ed789c7728b0b5a0003c6e57363e9408c8c1d0ce64bf818cfaab5d9e42b855fc6083f018b0b7fb341b4c2f4f8c71dc2b9df04554703395b1a82e0dbf96cb415b9e617e2e23a12153f23b9797377c892c3b0104565ed2ebca1835fb7ffe5d9f69d3093b54b76255468f552b4fa247287efca8394604ae4c3e0e74e1d6932380d7d0dd17a82f3dae2c3bedba8cc3e88c50b8d32abcacdc2e64189b75715ee6ad50f856f8ca9d255137bfc61a396a3363702348656ed725020b758c5fbe07ea0898521e7d1fcd0073837cc960fd27122614a804638f0b0a384beaf270277e4a2416de67f6ca68aaa9996725b88e4b61e540c2a39f4cdfdf917002567c8133b08c1572c46245dcd86316192d298d4892f81d3d9fb11e69aa42eb68c5a1a37e0457c093885917f8f69fc438a754cce35ba84811c1721e0bce633ba9eb4007e7e0651dd8f925c5fdbbe13a21f44bcbb9c40089288a2d12c48480a1c8e21bfe3cf3699e1f1d2ebb39a90e22927f6c1dc87eab34fd85fde5c5cc2bcee76b2d70b870431c187c1ecb0cd4917ab291ae2bf0b890c676e52637065ef2cb2721f68223c8f350</script> <div class="hbe hbe-content"> <div class="hbe hbe-input hbe-input-default"> <input class="hbe hbe-input-field hbe-input-field-default" type="password" id="hbePass"> <label class="hbe hbe-input-label hbe-input-label-default" for="hbePass"> <span class="hbe hbe-input-label-content hbe-input-label-content-default">Hey, password is required here.</span> </label> </div> </div></div><script data-pjax src="/lib/hbe.js"></script><link href="/css/hbe.style.css" rel="stylesheet" type="text/css">]]></content>
<categories>
<category> 个人日志 </category>
<category> 个人日志 </category>
</categories>
</entry>
<entry>
<title>【工具】Obsidian笔记</title>
<link href="/posts/obsidian/"/>
<url>/posts/obsidian/</url>
<content type="html"><![CDATA[<p>构建 <code>hexo + obsidian</code> 工具链<span id="more"></span></p><div class="admonition note"><p class="admonition-title">诉求</p><ul><li>提高笔记效率:<a href="https://www.zhihu.com/question/384309878/answer/2713962647">什么是 Zettelkasten 卡片盒笔记法? - 知乎</a></li><li>提高 hexo 开发效率:搭建 hexo + obsidian 环境</li><li>todo:还未掌握 obsidian 的核心功能</li></ul></div><h2 id="功能">功能</h2><p>汇总一些 obsidian 值得使用的功能和特性</p><table><thead><tr><th style="text-align:center">Features</th><th style="text-align:center">Hexo</th><th style="text-align:center">Obsidian</th><th style="text-align:left">备注</th></tr></thead><tbody><tr><td style="text-align:center">Adomination</td><td style="text-align:center">√</td><td style="text-align:center">√</td><td style="text-align:left">不兼容</td></tr><tr><td style="text-align:center">Todo Task</td><td style="text-align:center">×</td><td style="text-align:center">√</td><td style="text-align:left">有空实现下</td></tr></tbody></table><h3 id="示例">示例</h3><p><strong>Todo Task</strong></p><ul><li>[ ] 测试 todo 功能 <code>- [] xxx</code></li><li>[x] 测试 todo 功能 <code>- [] xxx</code> ✅ 2023-12-02</li></ul><h2 id="插件推荐">插件推荐</h2><p>核心介绍如下插件,对 hexo 有所帮助:</p><h3 id="1-Templater"><a href="https://silentvoid13.github.io/Templater/introduction.html">1. Templater</a></h3><p>通过自定义 <code>post.md</code> 模板,可以快速创建 hexo 文章。目前需要定义如下信息:</p><pre><code class="language-md">---title: xxxdate: <% tp.file.creation_date() %>abbrlink:toc: truethumbnail: /images/default.png---这是摘要<!-- more --></code></pre><p>当通过 <code>Ctrl + N</code> 使用该模板新建文章时,就会生成默认的 <code>*.md</code></p><h2 id="插件开发">插件开发</h2><blockquote><p>官方的插件开发文档,但是很残缺:<br><a href="https://docs.obsidian.md/Plugins/Getting+started/Build+a+plugin">Build a plugin - Developer Documentation</a></p></blockquote><p><strong>具体步骤</strong></p><ul><li>先clone官方示例的插件, 在此基础上做开发</li></ul><pre><code class="language-shell">cd .obsidian/pluginsgit clone [email protected]:obsidianmd/obsidian-sample-plugin.git* 进入目录```shellcd obsidian-sample-pluginnpm installnpm run dev</code></pre><ul><li><p>此时重新打开 obsidian 工作区,新的插件会出现在设置中</p></li><li><p>如何发布? 通过 <code>npm run build</code> 生成必须文件,然后拷贝到插件目录即可:</p><ul><li><code>main.js</code></li><li><code>manifest.json</code></li><li><code>style.css</code></li></ul></li><li><p>如何调试?</p><ul><li><code>new Notice()</code>:弹窗信息</li><li><code>console.log()</code>:输入 <code>ctrl + shift + I</code> 打开debug窗口</li></ul></li><li><p>Reload?</p><ul><li><code>reload app without saving</code>:通过 <code>ctrl + p</code> 呼出</li></ul></li></ul><h2 id="自定义插件-Luhao’s-Attachment">自定义插件: Luhao’s Attachment</h2><p><strong>背景</strong></p><p>由于采用 <code>hexo + github.io</code> 的技术架构,文件结构为:</p><pre><code>* /posts/obsidian/index.html:名为 obsidian.md 的文章* /images/*.png:存图片</code></pre><p>因此在任意 <code>*.md</code> 中插入图片,需要采用如下格式:<br><code></code></p><p>但尝试一圈 obsidian 插件后,发现不支持带 <code>../</code> 特殊字符的替换,因此决定自己动手改造。<br>为了避免重复造轮子,挑选了一个插件魔改:</p><ul><li><a href="https://github.com/593413198/obsidian-attachment-management">593413198/obsidian-attachment-management</a> (fork from <a href="https://github.com/trganda/obsidian-attachment-management">this</a>)</li></ul><p>主要修改如下:</p><ol><li>修改默认 setting,如将 attachPath(插入路j径) 写死为 <code>themes/pure/source/images</code>,将 attachFormat(插入名称) 写死为 <code>../../images/${notename}_${date}</code></li></ol><p>已经打包发布 <code>release</code> 版本,链接为:</p><ul><li><a href="https://github.com/593413198/obsidian-attachment-management/releases/tag/0.1">Release 0.1 · 593413198/obsidian-attachment-management · GitHub</a></li></ul><p>使用效果为(图为 <code>obsidian</code> 中截图):</p><p><img src="../../images/Obsidian_46986.png" alt=""></p><br><br><br><br><br>]]></content>
<categories>
<category> 工具 </category>
<category> 工具 </category>
</categories>
</entry>
<entry>
<title>【RealtimeRendering】3. GPU</title>
<link href="/posts/rtr-3/"/>
<url>/posts/rtr-3/</url>
<content type="html"><![CDATA[<blockquote><p>本章主要介绍 GPU 的硬件架构和管线,重点理解 <code>SIMT</code> 和 <code>Warp</code> 的概念,并结合实践理解 <code>VS & PS</code></p></blockquote><p><strong>GPU硬件</strong></p><p>关于 GPU 的硬件架构,参考 <a href="/posts/gpu/">这篇文章</a>。</p><ul><li>Warp:GPU并行处理任务的硬件形式,一个warp通常包含 32 个线程。</li><li>SIMT:<code>Single-Instrucion Multiple-Threads</code>,多个线程执行相同的指令,高度并行化。</li></ul><p><strong>GPU管线</strong></p><p>下图是GPU的整条渲染管线,其中绿色表示可编程部分,蓝色表示固定部分,黄色是可配置、但无法编程。<br><img src="../../images/rtr-gpu-pipeline.png" alt=""></p><p>可以结合 renderdoc 的 pipeline 试图理解这一整个管线:<br><img src="../../images/rtr-renderdoc.png" alt=""></p><p>对于可编程的 shader 管线,虽然最终GPU硬件执行的是机器代码,但是多家厂商退出了适合编写的高级语言,例如:</p><ul><li>HLSL:DirectX High-level Shading Language</li><li>GLSL:OpenGL Shading Language</li></ul><p>通常 shader 会允许被离线编译和存储,这些被叫做中间语言:</p><ul><li>IL:Intermediate Language</li></ul><p><strong>图形API</strong></p><p>每次图形API调用时,shader阶段都会包含两种类型的输入:</p><ol><li>uniform input:指在一次 draw-call 中不会改变的常量(如投影矩阵、texture)</li><li>varying input:指在一次 draw-call 中会变化的变量(如顶点位置)</li></ol><p>图形API的发展也十分迅速,如下图所示:</p><ul><li>OpenGL 和 Vulkan 显著特点都是跨平台</li><li>Metal 是苹果与 2014 年为自家产品研发的低开销的图形API</li><li>OpenGL ES 是指转为移动设备研发的<br><img src="../../images/rtr-graphics-api.png" alt=""></li></ul><h3 id="Vertex-Shading">Vertex Shading</h3><p>VS 通常是可编程管线中的第一个阶段,前面有说过,它的输入就是一堆 mesh 的二维坐标(附加 color、normal 等信息),一般有如下用途:</p><ul><li>顶点动画:草、角色</li><li>地形高度:通过一张 height-map 模拟地形的高低起伏</li><li>程序化变形:模拟布料、水面的运动</li><li>粒子特效:创建一个粒子发射器</li><li>…</li></ul><h3 id="Pixel-Shading">Pixel Shading</h3><p>这阶段是基于像素颜色作一系列计算,其性能也与分辨率和Shader复杂度直接相关。</p><p><strong>MRT</strong><br>早期的GPU,仅支持将PS的结果输出到一张RT上面,这样的绘制效率是很低下的。<br>MRT(Multiple Render Target)技术的推出,<strong>支持在一个pass绘制对个对象(GBuffer)</strong>,这催生了 Deferred Shading 的诞生,这里不展开描述。</p><p><strong>ddx ddy</strong><br>硬件提供了像素求偏导数的接口,由于GPU并行处理像素点时,会将 2x2 个像素放到同一个Quard中,因此<a href="https://www.zhihu.com/question/329521044">计算偏导数</a>十分方便。<br>换言之,这三个像素的 <code>ddx ddy</code> 取值也是相同的。</p><ul><li>ddx(v) = 该像素点右边的v值 - 该像素点的v值</li><li>ddy(v) = 该像素点下面的v值 - 该像素点的v值</li></ul><p>推荐阅读 <a href="https://blog.csdn.net/u013746357/article/details/107975128">mipmap_level 的计算原理</a></p><h3 id="Compute-Shading">Compute Shading</h3><div class="admonition error"><p class="admonition-title">Compute Shaing 部分缺乏工层经验,后面来补空缺吧</p></div><p>DX11 引入了 CS技术,它充分利用GPU并行计算的特点,使其扩展到 深度学习、神经网络、量化计算 等复杂的计算领域,而不仅仅局限于图形学。</p><p>它的优势之一在于,可以访问任意GPU上的数据,具体有如下应用:</p><ul><li>粒子系统</li><li>Culling</li><li>景深等</li></ul>]]></content>
<categories>
<category> RealtimeRendering </category>
<category> RealtimeRendering </category>
</categories>
<tags>
<tag> Graphics </tag>
<tag> OpenGL </tag>
</tags>
</entry>
<entry>
<title>游戏引擎岗要求汇总</title>
<link href="/posts/3K6XB0W/"/>
<url>/posts/3K6XB0W/</url>
<content type="html"><![CDATA[<div class="hbe hbe-container" id="hexo-blog-encrypt" data-wpm="Oh, this is an invalid password. Check and try again, please." data-whm="OOPS, these decrypted content may changed, but you can still have a look."> <script id="hbeData" type="hbeData" data-hmacdigest="a57dda4b7b82ae17071efb24e494716da9c4a50fd3572560fb9a883a3cf48a5b">b3dc07a81f6459d120ce338ccca550463faa708b9b4d89df9ab14ba4edd809f60b32d15b5672ce84c5d84744f7f475debb79baf718116b8398ebb7a36965f5712db04a990a04196bf7d89b9188d2f59696c274d67c5d338176316d0990e58740237b16c70080fbc3d186f48f65ead60c9f3efb14c16981a609c6df80b790f379e6b80f20ef476aa62233182f1be3173b510d09a093b5d25c7164e684586e9473ef084c4f40e2dbda8db3191087876f0c531d728a5d1f4d23091f9671910335bdd2dc460cf799674f06ca53e241a6a722de1fd81291bb9a501fcb5a6c362b80ee2ff346f53bed741db17b867184dc18722db17c8cdb37a99a8973386368fab795f91e4a480544b15380df3928c3ac7402e2ad0413032d30ed13b2b4080cbe28adc7a9539cbdf3ec9351fbd236fd587fbde15dbcb4010ca92202814315437c081c4c06a533032d0160baea7e0ee8fe85bf7e6c31851b65834dfe4b05ac88de74a61af7c67c4d3e749e09db00f6397cbccd52e6b2bcb04e043643b6996a587a228936b537377f69214573b844fc9c0ce0ac37729a985faab847d80733cb1538a56907f96b215a1db2de50ef00c7d093b717e3c3e3fca1befc064c0dd0777403eda61a876757d07d9a4fcd4219ab115fdadf41483cf5cc776162b659b42edbe066104f8f83c7a8a3bba84aa2c9a0b5be0e75e6b8c6bcbd4fca788c536bfc0783504d5a5cbc5110b0d2799cb6366cab37538acb97cc29005709c1083cc134519c08a9027d97d8c3fad8556e516435723465e677b68d6f1c1845160bc92f94fe8918a209f5c741e4525f16e877714d16e42f143a1668f46787ba09645c4309424cae18bdc04952a058e8c426677c54143f95c548671fe6d3bb1e4a7b5288a52ee4ef6b09ce8a2bf41922cdb08af1c78edb0dfc3e0d07e7a0dcb282156645c3a498946ad854b6c4d1ff25abf9edb9a0d77de321437b76b92eda816c0315484daeb4385812b7d7f44b1b19a98d5cab10e89399e50cbe83685931f879617afca50cb67725623bff904ca86f9eb022ba29d39458c30953be5ad06278c28d1b30cae0493be7004b649f742c79eaf2e39f8fdcec066f5ecf3bb4d1847d080cec3cf2a7f02f65e9be3653542e80b392788c3bf2e1c71c3048ba8b02604d6f84ae52c22fc414ed73a5f117a50e053d547ac6fab8dd12a35cb3853d679a5184c6d207486f6f1e922e10f7dc47538bfed34e52ed540f65a9b1bf7fcfa6544b7f546c30238152b62b8c3865c50661f365c792c624690b8f304945edd7f9aaa917c8d2ca46bc1ece0fcaa191fff959b902d92fde78ce97a8f560ddc49524b070259f067d2ac966bc1aa345dbb831e5243db9eca324f4947bf4187df5f60201dc5efa81ba85474939e5ea86851ce248ed2253a3cbc69028789576b5fb51ed4c2bad29cc637ce36c341aac3bd2a86d2383154e25b803b1004facbc505b52710a67f1bb9cdce38104133136316cfc9ecf937e3ea40ec87cb1501e6bdfdceb15b979e26b89133cc26f39a73e17ed206a81dc353da0fd49e6e4739ac40ee145890a5d96cb2ffd1e7a0faaa6cd03fc852802fb097e261d58396c0a987368480aa8daa89426b90b48a43c7fae6d65dfa976ff0712281cd0bab23333f90fb1fa708da5e28386c39009faad83daa2ba9de1f1d81252f70712142cb6a686175603c032442bcca0fe95041d39e728df69d417a1b22eb7d013bf257c65f1eb5d415924423231c84cc33760c19379f8731c851f677963494dbdc9a4f1b74b672ffd9f42cac9729ca2f43569a61f507ef8f364ce9ba22f71a343c9d86bf69d3d349d28867e0cc2af9222e5a9afa0a0c42d2a3198d318e5b0ca804b2a9b4d9ec6881623f4e7f26a633cf096e3f5676bcb15416d3f904937bdea2bce2b5ecd6d45d6c84e8e66f828e8d532eab2e7e784a853ace44cb35b26cc1add287838b2439320bae546673c3319109cdf7f0da5d1ace15e69aad5218699460fff3dda1b7b032a8088957495fce20cbf8b8080c84e0a3ed3216c7693789a4c08c3b4eeb623869ec051fc9d022abdadcdf44637b849b117cad419b40df12345f9c918dc71a5448e800fa6bc76f6f8858f5819dd1a83874c28b71a80abcfb9801979971f31bb038fccb0b576657e84edc6939095c095aac95f2eb4d828e4d45b83f5e728ca4c78f9455a2856ccd4cc41afa34d5d6f273dc2d995d98112474bdb67c478ba1738f9a429e3320985d19b9480f4dc19ea4b5de91f6c6d680af49dc8692fd15d69e62153f76db26d4681e14f4e30cdf20b427eb643d498af62b5bf66201b7f1b02302f6699aa1639200726fc153f955cfc5ea69186c3a414602764aec0071298ed540fad5f459c26fae893705cb83d2049458b19c6afd389206847ca503cf11e08228c7b858ec02219840725abf66d0d203b61ccb0bc15c16cbef01bfd8eb0dc0bdf63ed8b8b64be1bb1add8c1bb0bb3d1d099f9fb5bcabf05ef3f1b41b4142747bc25889ec03715a701dcd6b4995bbf1fad6aaf9f43891a797eb4d22e1fcedecf000921678fa63fc711479bd2bf193b0abbfdfb654c5d0eada2146e2d78f5f6bfb29b554dedbda140371ed26acfbd39f865655b5ccab336d942a7c9b352202e53a8e01e446b0340389ee7195b4b5fab00c46d1c3895ad8495dabcd32fb4fcc5fa36d879a419e65a2d59e58ab10085f223a80935fb3490a46ded7f7dbb1702be610c16ed8a560113ce4a0eaf7899bf08209106d10105f976ed591694ac165dc2c960e771cbd06cc1b1fd3cb457261c216a8948c8146eb9cf86cadf24f06e7daaaffd9478cf5be817f809a93d4797b4d20fd880d88923b348967d01d665446e151a914817267611a51fc73d4bd690f478dedb7552fe4faa4668d64a1b7b56aaeff10a0b9a4e9e7d17e30fc0eca048448c8189d4fbc92de70203f3d0548220541ca2a63d52632b95e78ccc9fce208721131f935aaa484c06fb99b2f6c8f2063d4ead37514da0faaf07950744a38fd32a1d680f03fb509b073a5362697cfaace58b78e58c9da46e71ec5dbeae73b26b1a996a03b6821db5f1b668308d5de6b123087cc081da7ad46777148e2c0138451757ebbbae3777112395d1735ec273fa6da7c0024b13431d655e98194662a3356cb03a31ab4529b769b018d1747c12137629c5de0b813f7e274e6c51cf640e355f0961eddef8d009b24f1470e00d40d37c0b2379326f8c2f16ba5330b6c7776bf05e8731f6403c6f821ce19ded55084c7a2e31d5625ae8b2d277204db3a540d5d7b56accbd4bd5b56ef72ffbfc6af148d2210796cff7087299f712648145b0ffa1548d90d215a4b05646aa819c8deeb93bb4e8f6dc7610c55f2376c260a8ba2198f4b663e7f9cbc5b6d45b08d9a29b5750982a1dc2c33a26e47257d085efc3cc4ccb1ab31ce0af8aa642aa50129b91ce4fb5736daa613e5e2ca5a680d631bb80805d207612ea581ed325e70138704871b4b70d67fb9909fbad2798f593b7997b5dfc9ede7ab710a4d4c194e6f2dcbd89d1946b24e19f4d4960d7da95ed0fa60071ea9b0f283256eb396486a1b7f10ff2d33c0760aef10ec679e7d0d2ed8ea34325b3e0450bf03d267c9c280720c3ca60a9f81918212f9e73c3f48afabe8110cac633d2ef8b1253e539c87592d560613a4a54b08932113e0ef1390a894e19fb3afa3bcca76813bf1903fcbf8fa0436f4ae1f513881abe025a43ac50cb61084414b03f460b97612a01b6c6ee89b3d454f6e4b886beb16852892d3aa4e42e761962bc004810c91e02ed88f995a5bcb395495b8b87f02cf2ff5d61a05b87a74e3943a4f0eda2d5e8c6a6bc4996d43e03cdbf7745a9de853af84a4618da1047374498e6b3625dc24af4a868b92515eed3c18b5bb8acdd51833829037c0f506a5c091107d5dd8ddb975cc183153ebbee48cbcf1ff487709c5ad2d69c4d90bc8e77caa34a43dda6525d9725e4785367c23385da93bd335589d4777942c0c32d9473c9de5bfde2749e5948c56eee57904819b85ded14953db42eae7dc4ab72dfa8c680b7a1ab0170a5c4a15df24f939f2078a4d7dc184466d33621024bd9c87741119524e24c1e4e738b84faa71b34f9cbe18b066db8aa4dd5f44cc3ec240ed50adf0baf49a2fe80539b272d45a3abe176bfff93521bfdaed776d82ee5a05aa75624ff55de3521136cf3571fafeae09ff1e763dbaac45fb68abdb066ce77f6b95e200c7d2669505fbd1457edc634993fdf86396f4caaa9ef00c9ab7182fbfed9916607c232af7d4db0f934bf393bc507c7836faffd5a3514e99fb5ac50a88e1526c32143c288e1dca6a1b1ca1f2fa6bccded1781568a7b95602856656c3626c2c04e92294f7a9f3abd1021733cd30845b2fd9b5e105780d510dfe682d37a717f7d4943fbab78ca7d20ed7ffb6bb0cd84f98464337b827a34e5b593d94050a88978a024f9753faf12d047c05dcea854e0a55c22d8fd852140a3535243b8d4ad974b40974b4c9e0cbd20e45e8b8a3e2862d0f6176b9959b4f539f50cefdf24351260e4cf975c6eca1893e0aaf1f19c8aa21f5e5f4b5c3ba8681ed99cf635ea99aeecffd2395ca92df079171d609e31ece572d21ff85e1070b3ef355530df65ce869af5aa0466f795a8225db6eedbf19d9680c6d83a4fa800a2ae1ad28aa21c7cc4a0342f11025ed159f64f6aa6d3c21bc02ca86879f71e23c738087f149b5c50786b13eac7473eeff40c12837ac4cfa8ff0005eefc53c740d968a5ce8c16a03f7af3a39b8a823688f54541b6abe3e28290a3751a71312912238e0b75f8e7fe83f3f0eec7fb6f9159c319efa0052112364cd132c15e6a80f30cf7b03ad7fae29f503628012ee002993c030b93202b4301c8d7d305c0bb1335a3dd4ea1f82bef485c784e4d0baec1d5c76bdda24480db4ad9e86e9776e876bb066aee75561e4781b65b2e49bcd392b50dd14714475aba37e24471b30d4e5d385607953440de9ce97d73d260488961f88ad46329a1f72e68950bd222e26289bcd287d047678af090beab87fd2ff1eb7e5c6eaa8d8fc7de2a5f04406acef76415caa0eedd262409d068ba5873e58a6889d34028318c39677751f7839b0480edfe8fa5dfc71e653545d107067e6792be3c7dd84e41a422196ef31c92d54998e564266780e7799945d50089b770648d8dc7d13845ae114628bc7fa1b5c628f62eab23a4aec8c22a38d90867d14f081098f12c17c96983a5a388d1eadfa3b57a868f4bad87d8a53a838687426463154480589417cf8cd392f0d29b7f559506e48d2aa8e4ddcb67bac1db537f9223c14e50b64a62b5148d33e8ec6e6640ce017c14afbb5958b835a2ea9e371001aac248026ff4ea8bcbdc69ad08b09fcc68368783a74cdf7dd0392ee414eec5b4a40aec6bc7d24caf48f90bdf9f6b1483ca6d493b0b4e01062aec3d950eac3eb0194f1b14922ef3a0c4083b1d66b07b400752204a464f2a1f7c364357be8530ed890cb8e5918511607e07b3665e73cd2b31312f575cfa28a2119672bc85df732a5a33b91eb18bf5fb3e30b1ab1a0ddc80b6f4a5a8e7910123769f169b229110a6ff2e8fba10fbf2b1bde72600e110c459a2472544fcee4a1b1b83de29b10059f659fcdd122c7b10ad2f74656022a93e205e837950d2e578da257c4fece34d742a720df0c1f478d5f63e82dce41397405a7691c1b23bad5baf5668c0cbde588f1b08218a037d434f6cffe3ca670ee19d39bff1813e61acacc79d355f8adab6f1463cc6d87a948d5faf197f5ccadf6c60066eb48526e16257bbd5eda2d4763a542c7edd9530596a197a3a0f412116dd7a643dcb78f963d1444dfe288f6be49ec77ea4c6adc227ea63592121476325b02f549273834f53b94a4effdb5f71afa01b1559dc706b8ea641fcf42421860720cfd42d6d9e0b5bc5ae29f681fb11fab60685ee170c5c13f582221cd4a58ca2de1239a4eecede737930174db32a8d54ef1ecd3994d0812a083fccc3f5d602c1aeed84573930cd3d00e1c69ab81a5533ec1307829eb457e6c3b6b9ed35ce0fc232012d5cd6bec3956cbc19cf6f2a3474894a77deb377252be51309e62b38952bfb2c8848636522014e551bda54987cdda04375a625c21887484c14176087a5c3378ef7131bfc913be909ddda65d48796d3ba18429b203fd531ac90ddf1d80c912cc10fef148eea96a3159bd19d17f131a563fa500d8e4ff8aabf637f2a2086c7e902b75ea124bffbfc71cf13d99e57439c7fcff864fe33068eae4f019f6dfb227a54d646b14c5a131d87f000e0599f6d01030742844574014f8ca942e00bfb7ae983fcc691316fedaecd8fbab9e1cf4ebad96272110075266927cb208b6bebb3ccd6e3f24088c6ae023382f49df1a0f45609786bc2b192e0058deb017ae17de93630868e3ae519fb59543b8bac090fdf3c6fa80b18784162e728d231192693845369ed4875721c3f97f53f155e7b8b498a03925b87eaa132086000d6012f9f190d1f15688ab4e13206ad288e1960fd7004666c76965c3495c20c309671bffdb30ebe9ecbcd08455ae74b2f0be97fc677b3ca314d11783b44d7462d92df010993284ab054b1b0f2d408b7ea81cca8f861f0a8511d266d5e5f82d8d406dc96870c8ab885898b8fae70385e05ef1a310f9842961a10c24fc705860146d117034a194a333e7faedff11cf4f418cee260b7eca85b7bab0cc0afd25f473ac6b018f6ec7b11d99baa70dd583a68b509eb51e184ed4c55b82e8ea022ba7032bda77826ce16280ec673f576319c4edca1af9c548ca024a1a07dc4eaa84ccfdd9162f6651a38a02a91f78f7b2c5a4ecddc39601e5c1426d4bbfd7a386188c8e802c7ae43eeec89202ca45504df5a87434ea97067402897c551f8afa0f605c8a521acb2200671f06b08df4cd330598bcae48cf4ff605ee78896583966740e1d2f22ff9768c5557f4a4c659fb6beb5b5bdfa763afa401eec20ed92a20c08bbbcc35e8b7677d118570f20214745d75ed760c36de6dbb8f54cb8c95c097d328162e18f393cc1ce7d7d103446c4919e366b128163e720b30d9c71100c3a6c0b6de686b4be3389cb71a008c1b6f9e35cc9edceaada6322fe7644eb9990053134d195aab4edf8c8d7d5d7d4922c677937b706a5a412d7117cc04c9290651b4fdc04befea9d5ed94a65a228d594b06f7a25f55ce730bf886a131387f0f7cecd4c09fdba98da97895ffafd0b5e382e6edc4bece618c7be60b67ca80b593a7d99c0306d21d6f817737603873cdb13ac852c4668a67b9200096ebe4af2dea257cb2c577db037a26788d194745faeac00e35b92e73b24e851725fd4f39bb02abf74dea7594cd199fe708f678922e1289bedca381311880e14ec9299a11f6c6c91279f09fe4d4d89e9cce746510f68992312b7a6527f6b4d3a6e1a8ea55b1ae196d204e2b318156078f558e7225c7e47a6ee3806410a746fd95f16fb22b1fdf08665c9aacf00868705f388a487cb2c9c198ca2756273b818e51a5bbe95fb9a6007c249456ed0f7b07b6d4d7053c82b0209ec72d6de278c54b3003f6a316bd13475207b9ffd0f810e5cbab9e2b70aeaeef83583ee83e41b24a5a0a1cb4096bda2b4c3910e85440a160da32ea16639b5dad4f838f224cfcade35576e17df266f797a1ad26228ee7b991674e1d60344ac52d7127066e496d5952f0bf48ad9e1dc1f8b1dbee40e6507e4c1e71a781de825b7453d507e2bd9a0ae69558c5496f99221d14180f39253aed556e203732ed7fa12214b6041a2d42e808a5c6c627b4839bad1888cbce2498d37fbc9493c2e3cfedc6afa3c2fc0c2e78b602967628a12c32064ade5a382e2d7a07f19bc3fe3d2771686158f495aa1b9aeacf36bcbdbb3b52e87cef4888f733d1534ab11f8551ad0221768a39d92fb9935d124a2ea0b1317e28fe7a02de1855796150092a25d64ffa6b97b615d9b73194b6c0c5fc8086ebac2b8cf90ff330aacf4d1567fb2589a5e07f5a7285b1f792e734d1eae8b73994c23a5c04b44bb96e470983d66e46f5aca3e9c4f261addb760e97acce34365a1ad800d1a09990948181bd6dccb1081dfda3c7b55a00fae239b82e0d8cb3f3fcbab449f9ecc26562ab9b513cf69a95ec4c285b53eda2c03455895fecd5ca0fc686f61970572cfa7fd0fa92ba175a0e8cc692e1669efec359f57ca198c9be22ee2e894b68ee2c6a0eca97b5115ada9acc7a87af67b97bab1c84e434fd48bd28ea9c5201d605fcb31dc7b0ed56cd9328885503b90c495c61105a7394ac67334a42980973fe89633f604001b3fb45c214b733b8cc07b50aed57898dd59084e4213fe2523f077981fc942eda9c884643176238fa0ebfdcdb3f8a3d6c6223160cd70b788da4ccd7958cc0e5d445d5f74f75bc5c9af7d1b2928f655a972003df88b82bfb7822f2891f5aa4826fd4f161e25413af5e9517a6e37c5a69c34fc429844bc87e1e5a8d9e1e747f74e014017a87083e90baf5e7c30f5e0282bba74f2c4be954dca4d897f9386e35b359c28bf7124a9ecda0f2b20ebfa044437bdc694687caeaeb116ccbf1dc45255e67584771ffcd426968d4aa717d7f3894d4a7c70ca216cdff84768b1760388d3a9905531e36ae480a1f228586194cc564092b91cfd939b86a7beeb548b4dca51e4faec2d5aab87469eabb4417b6feb99c62d450c4fd294dc96526287cf61d5971c04680d324441591ec8331fe20769db5cfbdb75c63a2ce73179bffcbd52c9ad5d24e7aed50d5b572134c9cefc96992600c5a858f0af8572906ed6281ebf59637698bed36eca4917f4ea169826d6a32afceece1e1c27da7388bd9b520b878360bf10f39eacf35fe59f6d5766c6d7612735fe3054ef0dd4e8be953820475b0d499a449e2d6103a37384294734d36948aed6b1906701f87d17fe248b227559bffc7698034d94236e9adf338417038f9c9ff75f5b99c82bba887677370190aadc34aac5e13d6234d2f8cbdbf53f86729bdb06c860f39e938ec2fca708b83dcb7f867ff131119308f7b434ab86c9ad35ce15cb0c760754f4bb59a72bbd67b4014793024e096d9a4a66159705367d0709d75f81204d1660976071ee48f7973b4e18d7fa86029751b783136d9ebee635654a6561e84427c207513c9f548330b39372540ca565c698fdf7ec0c02b4190ffb734f62149546662cc8787d54c6e60222332726828ef4a9da0e66f8fbb47148f9104ad8da6db6f2e57bfc395b6f48ccb159af890951461612e52033c5c474563b0a9a9a67400bc2274e92ceedc271e7d21e8051d16c54ae690daf966de50a0a0f8e64ad45d8d21744abd14d51cd771c6430ad083242537f983f1af5937c549e5cad5083700416f15b9aeede143a319b469c8ef32f99067127369bc0a99a74f634d7e703e680059bfa50d9aeb1996ae0835927720c5a6e9512d239428da94f4ee6333ef8453d4bb3ac664d9d9aa3d930b388645da6e5cb94b44a729e066d12798f7f2635a0972efa834c9554da62bdd35b4626fbe5167b5f3ee685a4e0746861031e2232b9211bd1b9b2bdbfca58250b167f4182b8db4e37f0c674b860fb29e57c46bffbe9ac8847a2d0849c3a000bdfefbe2ade4bfb476e5b168d84446d897f796b6477fabbdeb75cb8f95d4f0479622dc70e28af5e7acdd7d970e48ee504b076946f23985bf89cef9449f3d95497ad4f7be48f745e5e6b920755de158acf4f916baff44b4dc3c72b885052f3bcf2452ef315bd5d76284faad1e1d662f9dbbd282bb81d1634b748b5851d90634b70fb505e2a2e692b7f86ee0fb02389a3d75ce0e65bc4efe287acdb8ad60fe83cd7348c5e3e24f92b23b417aedc4749af5acae09b856db58782b75e965b6d76adb467dbabeb8cfde7e20938bd5976daaff959b9f91996d3cf10df6b1d5602e0b8a8380621dac8ba906e57871c73a943eccfbb05567d48a791a583c081616df24d3d464967b15921a9ef3f46bb1de189939e7523374be2c2fca7194162d41ac9b33070d33f3b5ea66718dccc3d93dbda6abea5c1098b3702689951ccdee421c0a4c741ae3feee318dd4a96f94bca9033e055474d41a1d0bbb89f3668203330fee3657c3db45483132162073e6f6b59899fc815fb3627e4244a999df15ddb11aca4c253e6c0442e722d4360d5535ad98a94e3997650f7eb257ec56eda68965b545fea8eaaad77631c20b1cc8962a0f51c89cb0339c69ae7350648508807002114cd603fb75f8695c09baa1484a1b1f21c81cd05139863040342cfa618119a8e7ac1e59394363aaf6c181386cbbf46bca103c198095cef38586c5a592fb6f677cfe2eb6d43410a50a93935622ebba35b61a1001778a805d6d9b88be7e1d44eb117ec0c55765ef24adad9e5a2e37a361f4712ee131864ff8abddfc731c0532aa13bfa5c5cd89c6a009194389a7542a6e568123825d099e726e1a189a36d313e8cc010edf43817b0562ddd48c28d3db1923e6e39c431082a30c8278b2bd178bdf5aff72ec34b94efa2e3eaa5ca1d7b47f56a1b22cfbaeaf320dfda9913ccb54505b41eeb6b1cdc75bdb402c1ba357c378571bf1a2346526db585f6f59ebd3117ef435ae7504ef97ab6a494d1ce5fa789279f6cde07367afea75ac6b6cd710c02e751b39d0612f570ce1e591d25b24c0cfc003b35eccffd2af2bbbfce50199309a799df98079888ef6ae5f48de7ba41207a8d35cb04dc8ddf6ab6db7d29c59b878142a77a4ab0f2ff8a6cb124baf60392c8b8b123da6c0178dd12e1513af881b05357f224276f314a388fb20b7801e7bf7f7835d57439b2ad72e7b7e39257aa68018598da4bc57bdc05f44fe1f753aa49e66c51e2d6f803cb36725af835cc26a1dbd24695dcc47aabe5919f72feb2d380e86595610a82fb7ceba4fff6fa2041111454afe666e7a27316ca5aeb21f5791b876c95ff078a8c65a8d9870d857f510f08cb5ca2fba4b6a381606b681003e4d862b6f86a4ad4b9f1dfaa9cf85225557159d798fb0104ac8b4e95c63ad9512b940b56762914348716bc0e9b13bdcd06a1260b64b4d7104313eece8066b5b0b29071822e7dfb914cb47f3a07b9eb8b698adac5a5a1a32f90e2fdfc49dc3746641316149a18e512b39f744bef52d50ba093e055949bd4068732891d154542cafffd9ccfc3318f2eb04ea4e999501f6829379ded1788ca19ad2f0b6bd83f5fb2f99e0cb4fbc6ead8c8436869f0ebed2d5bc9415e5ae94d810538f25d708fb870fb0d32c0df1db07cf9ec7575d6ddb01ad81416dd0928a91aea7d6a7647865fffc5492fd1186135889e4b81c4caf9e2ef24c436dbb07b35f9033e421134c3859066333169736ac58d32770baec6f35be38d429de4fb25569bb5cc33716457e34208a111bb74a3f23e7b3cf3fb16b1c010a3bf24cbe3cad618afb0bd9feb878cc484a39c6e0c139714e2906ac9abaed062445114e6a8883e3a3dbc421acfe865866c3aa0f7bd4a55449a2d9c3345b9aaa945a74da8ab046ff7d76bb3623b2d7590a02f941bbc021b463502cff23511d66a309d57f7256d4edea9acb739d5a3c3f7a3a88ad7c625149d2034d80cd037cae167727d4330ea3915bc490370204a387d7351285f7dcf4699531b524ef00d354c5503dc07acaec4e67be0240437a09b716498b98364ee6207059f1c673c8575724a189896a969235f5d2f36a734f241467e3aec6bfcda5e463170542da564f31dec9db76757155d95f4a1cfef199296952d3514b5ec7b5f3c89ba7b594c79182f34bc29e4b7da88b28ca8cd58b80ab62fea0e90396422c0b3d35314b7a8bf1b2ae1a781d56cd4deea89623c6a99daa5539538015d99f9278608da4c6d9736cf5f71706329e2bfebbf7ba2d096c4bf04c7249317904a2824d814359dc81294874c344ac5e6f239fda2c3584d59340d49903f32b751e1c75dc7eb89b069053806050b5c52d3599d5036cd06c23a356904d2ac8bf21d20bed634820f281f0a24c3bc2a6f187ed8c257691daab3f35e61d04b715831b49860ac9988b2c4c98d92fc0065909a1948b8c97fe89a00904e32f4a62236664f8111be74e07004f4db26ba4b4efbefb9a347d2f73ebfa9cd4f6311730537c3db2c56d8d9e81fdb64b2586566904846a72fff3a8eb9df86d3e8e9bb098729ea28a13890510fb60860f17df76b46eef6ab20b4bf1be65f2dfe433b3e68ac63d9e7cb99d525fa4b272c2cd0b24439726185011f4c60852132fadfcae784631cd510aa56a9107afdd399f2514abfeacffa9284a93f5d93ab0b339d730cde541f56e5dcd83118dba19d4d1f867bdb98a2ba6c70effddc6567bbfe455402abc82d84b08b60ce4cc5224e4cc825b29fd127b505de5c4dea7f831455fa3a9e7b3531959d7e4f056136efe1281a0cce34fdb9bee542b205eeb91824ce6bbe5543781e94c8c16bfc4c35434a58647e531e942e3cea266ba7d5c0a989f715097707c50bf85c12bfa31951041f0ed8277145117b5093b22e862773d232aec3ac3ccca8343ee08dcf7c356328c69aa03db3dbc7f4d5779b152aa0551d05843f92007c2554bcdc48aa07559207d65e83a0fb044c4b4111f3bc2e07230f916cb62b284d0781cf7d96ea8579a38cdc97d3acdf4eb9372e498029b5e894320ecec1979e22a974dfefa43ea48e4061949963a03c2a446d35b83f615b47a7e8eff03af174bbe23d0000e539b30f217a2c732a4a83709763cdb43bc2f824d14a24f64905c1c7d15c181831273d84d838a85016d3c69285cb28ca5641399ad8f617751fa1883996b98fa15b19b3c9d3d4f1bcdb3047180edbc9998c1f528cfb418e907b1cbbaafe96da6a7537c823f38347476182ddf2f8d274507ee3e8ab783de1a21ad87cb7b4d3edbdae89ae9bc138da16cae8c8346fd4ac738b77a952ebbb96c316a4a7005b1a4d5bfc8c2ac1094612b1ae40fd94e635791898484c35907e57f84cc9e65fe0454a077de39a9fcdf953e2d9c8b0670935cec0bb1ebf989090d5f2fb5dad01d6660183f83e3832bdedcc0f932ab1a78dab4b1702ce25352f1ddbd98bf83ed400e0a1cca9bda285135b3f6f42c58395d323ee98bd6053f485c7f290073790d8faf9f25d74186c1364fbe32589277662a8992b0dd1d0857698dfe38ea3a4e2f23912991814dff303c1d34d95c9f3728d76387bfc8f6ad692aa4cd13de3c37a1c6c75310c1cd5a64c9575aab575e7e88811e9789cd2690a373130549cb69837c929802c4f625761065528456cc0f2caaff207993a8470bcc80a1b5850dc5fc13b21613fb6c747a4f26095f6c4131b34cea93ce2f94959098a47ef251c953013c410b793375f6d0d9739f934db417d39efe28a512a793e15a6e47d50a4a2b038da1d55ffccb0fd1cf626bb761f6b272036cff0c138ed00e7bf498e93ea69332157f148f658cbd3a603dcd</script> <div class="hbe hbe-content"> <div class="hbe hbe-input hbe-input-default"> <input class="hbe hbe-input-field hbe-input-field-default" type="password" id="hbePass"> <label class="hbe hbe-input-label hbe-input-label-default" for="hbePass"> <span class="hbe hbe-input-label-content hbe-input-label-content-default">Hey, password is required here.</span> </label> </div> </div></div><script data-pjax src="/lib/hbe.js"></script><link href="/css/hbe.style.css" rel="stylesheet" type="text/css">]]></content>
<categories>
<category> 3D Engine </category>
<category> 3D Engine </category>
</categories>
<tags>
<tag> C++ </tag>
<tag> OpenGL </tag>
</tags>
</entry>
<entry>
<title>Tracy Profiler</title>
<link href="/posts/tracy/"/>
<url>/posts/tracy/</url>
<content type="html"><![CDATA[<p>性能<code>Profile</code>工具的使用、接入<span id="more"></span></p><div class="admonition note"><p class="admonition-title">导读</p><ul><li><a href="https://github.com/wolfpld/tracy">Tracy Profiler</a></li><li><a href="https://github.com/wolfpld/tracy/releases/latest/download/tracy.pdf">官方 pdf 手册</a></li></ul></div><h3 id="Tracy">Tracy</h3><p><img src="https://raw.githubusercontent.com/wolfpld/tracy/master/doc/profiler.png" alt=""></p><hr><p>Tracy 是一款<strong>开源性能 Profiler 工具</strong>,它具有如下特征:</p><ul><li>支持纳秒级别精度</li><li>支持 CPU、GPU、内存、锁、线程切换 的采样</li><li>支持三方集成使用</li></ul><p>Tray 虽然作为插桩式的工具(手动插入标记段),但其对于原生程序的性能影响却可忽略不计 <strong>(约 2.25ns)</strong>,<br>因为它是基于汇编统计的时间戳,官方手册中 <code>1.7.1 Assembly analysis</code> 节展示了 x64 的汇编代码。</p><hr><h3 id="如何集成">如何集成</h3><p>官方推荐使用 <code>git submodule</code> 的方式集成 <code>tracy</code> 源码,但是又强制要求 <code>client & server</code> 的版本一致,不然连接的时候会报错:<u class="error"><code>Incampatable Protocol</code></u>,因此还是手动下载了 <a href="https://github.com/wolfpld/tracy/releases"><em><strong>0.10 版本</strong></em></a> 的源码集成。</p><p><strong>Step 1. 配置CMake</strong></p><p>集成源码后,需要配置新的 <code>CMakeLists.txt</code>,可参照如下改动:</p><pre><code class="language-make"># 打开 TRAY_ENABLE 宏option(TRACY_ENABLE "" ON)# 添加子目录,注意 tracy/ 下需要包含一个 CMakeLists.txtadd_subdirectory(../misc/tracy ../tracy)# 将 tracy 库链接到引擎target_link_libraries(${PROJECT_NAME} Tracy::TracyClient)# 添加 include 头文件的目录target_include_directories(${PROJECT_NAME} PUBLIC ../misc/tracy/public)</code></pre><p><strong>Step 2. 插入代码</strong></p><p>因为引擎是以帧为单位,所以需要使用 <code>FrameMark</code> 告诉 tracy 一帧的范围是什么。官方文档推荐将 其 调用处放在每帧渲染的结束,即紧随 <code>glSwapBuffers</code> 调用。</p><ul><li><code>FrameMark</code></li></ul><p>最常用的 Profile 函数是这俩,区别是后者可以定义 tracy 条目的颜色:</p><ul><li><code>ZoneScopedN(name)</code></li><li><code>ZoneScopedNC(name, color)</code></li></ul><p>可以通过 <code>ZoneText</code> 传递一些参数,例如 <code>shader uniform</code> 的名称,以观察哪一次调用开销大:</p><ul><li><code>ZoneText(name, size)</code></li></ul><p><strong>Step 3. 开始 Profile</strong></p><p>运行标的程序后,注意打开对应版本的 <code>tracy.exe</code>(<a href="https://github.com/wolfpld/tracy/releases">下载地址</a>),点击 <code>connect</code> 即可。<br>对于高版本的 tracy,下方(红框内)还罗列出可以 attach 的进程,非常贴心。</p><p><img src="../../images/tracy-start.png" alt=""></p><hr><p><strong>Profile 示例</strong></p><p>笔者实现一个 OpenGL Demo,发现 <code>Render</code> 部分的开销比较大,于是集成Tracy后增加了一些插装,最后定位到是一次 <code>glUniformMatrix4fv</code> 传递时的开销,如下图:</p><p><img src="../../images/tracy-demo.png" alt=""></p><p>最终定位到 <a href="https://registry.khronos.org/OpenGL-Refpages/gl4/html/glGetUniformLocation.xhtml"><code>glGetUniformLocation</code></a>,它会在每帧查询 uniform 变量的位置,开销非常大,改成 缓存 或者 全局固定ubo 可以优化掉。</p><hr><h3 id="其他">其他</h3><ul><li>应用层可以对 <code>tracy.hpp</code> 再作一层封装,便于使用,参考 <a href="https://github.com/urho3d/urho3d/blob/e0ce107356b255bf2e24d94a41d00b512b9ce633/Source/Urho3D/Core/Profiler.h">urho3d profiler</a></li><li>tracy 对于 内存、图形API 的支持,网上文档和使用不多,有时间研究下</li></ul>]]></content>
<categories>
<category> 工具 </category>
<category> 工具 </category>
</categories>
<tags>
<tag> OpenGL </tag>
</tags>
</entry>
<entry>
<title>【RealtimeRendering】2. Graphics Rendering Pipeline</title>
<link href="/posts/rtr-2/"/>
<url>/posts/rtr-2/</url>
<content type="html"><![CDATA[<blockquote><p>本章介绍实时渲染中的一个核心观念:<strong><code>graphics rendering pipeline</code></strong>,它将图形渲染的整个过程,抽象为一条流水线。</p></blockquote><p>渲染管线的开发,有两条重要原则:</p><ul><li>各个阶段可以<strong>并行化</strong>,但有些会依赖上一个阶段的输出</li><li><strong>短板效应</strong>,性能总是受制于最慢的一个阶段</li></ul><p>本章还将渲染管线细分为四个阶段,后面会依次介绍:</p><ul><li>Application</li><li>Geometry Processing</li><li>Rasterization</li><li>Pixel Processing</li></ul><p><img src="../../images/rtr-pipeline.png" alt=""></p><p><img src="../../images/RealTimeRendering_51788.png" alt=""></p><hr><h3 id="1-Application">1. Application</h3><p>所有与渲染相关的CPU部分,都被统称为 <code>Application</code> 阶段,它的核心诉求是:<strong>计算出所有需要渲染的 <code>render primitive</code>(点、线、三角形)</strong>,并输入到GPU中,给下一个阶段执行。</p><p>重点学习如下领域:</p><ul><li>(CPU) Culling</li><li>Collision Detection</li><li>Multi-RenderThread</li></ul><hr><h3 id="2-Geometry-Processing">2. Geometry Processing</h3><p>这部分完全运行在GPU上,<strong>输入是CPU传入的 <code>Primitives</code>,输出是屏幕上的 <code>Pixel</code></strong>,它的功能较为繁重,因此被细分多个小环节。</p><p><img src="../../images/rtr-geometry-processing.png" alt=""></p><p><strong>2.1 Vertex Shading</strong></p><p>先明确CPU阶段最后传入的数据格式(参考 OpenGL VBO, VAO),它是渲染图形的所有顶点信息的列表。考虑到它们是基于 模型空间的,因此要<strong>通过 <code>MVP</code> 变换</strong>转化到统一的世界投影空间。</p><p>同时,顶点上还包含<strong>法线、颜色、UV</strong>等信息,<code>VS</code>中可以由这些信息进行一些廉价高效的着色计算(当然效果比较挫)。</p><p><strong>2.2 Clipping</strong></p><p>在经历过投影变换后,我们得到一个 (-1, -1, -1) ~ (1, 1, 1) 范围的标准立方体,出于性能和可见性考虑,所以超出这个范围的顶点都不应该渲染,即被裁剪掉。</p><p><strong>2.3 Screen Mapping</strong></p><p>这阶段的输入是经历 Clipping 之后的三维坐标,输出应该是二维坐标。<br><font color="#FF1E10"><strong>todo: 比较粗略,没搞懂具体做了什么</strong></font></p><p><img src="../../images/rtr-screen-mapping.png" alt=""></p><hr><h3 id="3-Rasterization-⭐">3. Rasterization ⭐</h3><p><a href="/posts/M5TXVE/#Pixels">【GAMES101】Rasterization</a></p><p>这阶段的核心,是将二维坐标,映射到屏幕坐标。</p><blockquote><p>假设有一台分辨率为 <code>1024 x 720</code> 的显示器,那么光栅化的作用就是,计算 1024x720 个数组的 rgba 取值。<br>这些 rgba 取值再传递给显示硬件,就是最终呈现的画面。</p></blockquote><p><img src="../../images/rtr-rasterization.png" alt=""></p><ul><li>(上图)左二阶段:<br>通过确定每个像素在对应的三角形内(<a href="/posts/M5TXVE/#%E9%97%AE%E9%A2%98%E5%BB%BA%E6%A8%A1">参考</a>),以决定其着色。<br>其中 <code>Triangle Traversal</code> 就是遍历所有的三角形,并对像素进行插值,其中学问很深不细究。</li></ul><hr><h3 id="4-Pixel-Processing">4. Pixel Processing</h3><ul><li>(上图)右二阶段:<br>即经典的 <code>Pixel Shading</code>,逐像素的着色计算,这里不详细展开</li></ul><p>这阶段所有的颜色信息,都存储在 <code>ColorBuffer</code> 的GPU内存上,通过 RenderDoc 抓帧可以查看其具体内容。</p><p><strong>visibility</strong><br>另外,<strong>更新可见性</strong> 也是这一阶段的重要任务。通过每次绘制时与 <code>Z-Buffer</code> 比较,即可以判断深度遮挡关系,这里是硬件支持的算法。<br>值得注意的是,<code>Z-Buffer</code>机制对于半透明的绘制很不友好,因此需要严格遵守 <strong>“先Opaque、后Transparent”</strong> 的绘制顺序。<br>而 <code>ColorBuffer</code> 的 Alpha通道,通常还支持用作 透明度测试,即 <code>AlphaTest</code>,也不详细展开。</p><p><strong>Stencil-Test</strong><br>通常还会将 <code>Z-Buffer</code>的其中8位用来实现 <code>Stencil-Buffer</code>,即所谓的 <strong>“模板测试”</strong></p>]]></content>
<categories>
<category> RealtimeRendering </category>
<category> RealtimeRendering </category>
</categories>
<tags>
<tag> Graphics </tag>
<tag> OpenGL </tag>
</tags>
</entry>
<entry>
<title>RealtimeRendering 阅读计划</title>
<link href="/posts/rtr/"/>
<url>/posts/rtr/</url>
<content type="html"><![CDATA[<h3 id="方法论">方法论</h3><ul><li>建议阅读英文原版,遇到困难时借助:翻译软件、Chatgpt、毛星云的中译版</li><li>建议结合引擎源码 + 实践落地理解,不要纸上谈兵</li></ul><hr><table><thead><tr><th style="text-align:center">章节</th><th style="text-align:left">概要</th></tr></thead><tbody><tr><td style="text-align:center"><a href="/posts/rtr-2">Ch.2</a></td><td style="text-align:left">渲染管线概览:四阶段</td></tr><tr><td style="text-align:center"><a href="/posts/rtr-3">Ch.3</a></td><td style="text-align:left">GPU 硬件架构</td></tr><tr><td style="text-align:center">Ch.4</td><td style="text-align:left">Transform</td></tr><tr><td style="text-align:center"><a href="/posts/rtr-5">Ch.5</a></td><td style="text-align:left">Shading Model</td></tr><tr><td style="text-align:center">Ch.6</td><td style="text-align:left">Textures 纹理,<a href="/posts/2022-texture/">【Graphics-2022】Texture 纹理</a></td></tr></tbody></table><h3 id="资料汇总">资料汇总</h3><ul><li><a href="https://www.realtimerendering.com/">realtimerendering.com</a></li><li><a href="http://110.42.228.178/pdf/RTR4-EN.pdf">RTR4-EN</a>,<a href="http://110.42.228.178/pdf/RTR4-CN.pdf">RTR4-CN</a></li></ul>]]></content>
<categories>
<category> RealtimeRendering </category>
<category> RealtimeRendering </category>
</categories>
<tags>
<tag> Graphics </tag>
</tags>
</entry>
<entry>
<title>【源码】开源游戏引擎系列</title>
<link href="/posts/2ZE2VGG/"/>
<url>/posts/2ZE2VGG/</url>
<content type="html"><![CDATA[<h2 id="引擎汇总">引擎汇总</h2><ul><li>一些老引擎(如 KlayGE、AtomicEngine)不支持 VS2022 编译运行,需要额外修改</li><li>一些引擎(如 ogre)只能采用组件的形式嵌入,没法直接当成编辑器打开*</li></ul><table><thead><tr><th style="text-align:center">Engine</th><th style="text-align:left">特点</th><th style="text-align:center">Build&Run</th><th style="text-align:center">编辑器</th><th style="text-align:center">Profiler</th></tr></thead><tbody><tr><td style="text-align:center"><a href="https://github.com/gongminmin/KlayGE">KlayGE</a></td><td style="text-align:left">编译失败了…</td><td style="text-align:center">×</td><td style="text-align:center"></td><td style="text-align:center"></td></tr><tr><td style="text-align:center"><a href="https://github.com/urho3d/urho3d">urho3d</a></td><td style="text-align:left">代码风格,容易上手</td><td style="text-align:center">√</td><td style="text-align:center"></td><td style="text-align:center">Tracy</td></tr><tr><td style="text-align:center"><a href="https://github.com/AtomicGameEngine/AtomicGameEngine">Atomic</a></td><td style="text-align:left">继承自urho3d,<code>C#</code>脚本</td><td style="text-align:center">√</td><td style="text-align:center">√</td><td style="text-align:center"></td></tr><tr><td style="text-align:center"><a href="https://github.com/OGRECave/ogre">ogre</a></td><td style="text-align:left">功能庞大</td><td style="text-align:center">√</td><td style="text-align:center"></td><td style="text-align:center"></td></tr><tr><td style="text-align:center"><a href="https://github.com/bkaradzic/bgfx">bgfx</a></td><td style="text-align:left">图形API丰富,简洁</td><td style="text-align:center">√</td><td style="text-align:center"></td><td style="text-align:center">Renderdoc</td></tr><tr><td style="text-align:center">o3de</td><td style="text-align:left">todo</td><td style="text-align:center"></td><td style="text-align:center"></td><td style="text-align:center"></td></tr><tr><td style="text-align:center">godot</td><td style="text-align:left">todo</td><td style="text-align:center"></td><td style="text-align:center"></td><td style="text-align:center"></td></tr><tr><td style="text-align:center">cocos-2dx</td><td style="text-align:left">todo</td><td style="text-align:center"></td><td style="text-align:center"></td><td style="text-align:center"></td></tr></tbody></table><h3 id="注意">注意</h3><ul><li>编译前请阅读 github 上的文档</li><li>如果 cmake.exe 不清楚传递哪些参数,请使用 <strong>cmake-gui</strong>(推荐)</li><li>因为某些引擎版本古老,编译脚本写死了如 <code>VS2015、VS2017</code> 等参数,这种情况比较坑爹得手动改</li></ul><h3 id="编译相关">编译相关</h3><ul><li><code>error c2001: newline in constant</code><br>这个报错一般是因为文本格式错误,可以用记事本重新保存为 <code>UTF-8 BOM</code></li></ul><h2 id="KlayGE">KlayGE</h2><p>截止23年还在更新维护,但是提交频率不高。<br>cmake遇到错误,手动解决后还是无法生成,已经放弃。</p><h2 id="urho3d">urho3d</h2><div class="admonition note"><p class="admonition-title">NOTE</p><ul><li>重点学习 urho3d 代码风格、项目结构</li></ul></div><p>截止23年1月已经放弃维护,且已改成俄语项目(?)。<br>抛开别的因素,代码风格和结构非常 nice,同时又不显得复杂,值得阅读学习。</p><p>支持两种图形API:</p><ul><li><code>OpenGL</code>: urho3d\Source\Urho3D\GraphicsAPI\OpenGL</li><li><code>DX11</code>: urho3d\Source\Urho3D\GraphicsAPI\Direct3D11</li></ul><p>项目结构分为 samples示例 和 源码部分,非常容易上手:</p><p><img src="../../images/urho3d-demo.png" alt=""></p><hr><p>代码、注释的风格也十分清爽,这里给俄罗斯人竖起大拇指o( ̄▽ ̄)d:</p><p>推荐一些模块的阅读:</p><ul><li>Graphics 渲染模块的API组织</li><li>xml、json 的模块化管理</li><li>IK、2D/3D 的物理模块</li></ul><h2 id="Atomic">Atomic</h2><div class="admonition note"><p class="admonition-title">NOTE</p><ul><li>这部分源码和 urho3d 重合度较高,建议一起对比阅读</li></ul></div><p>Atomic核心源码是继承的urho3d。<br>编译脚本只有 vs2015、vs2017,因此手动用 cmake-gui build 比较稳妥。</p><p>严格意义上说,Atomic 算完整的游戏引擎,拥有 runtime、编辑器、创建项目 等一整套流程,通过启动 <code>AtomicEditor</code> 可以打开一个项目管理器的窗口后,参考 Unity、UE 的实现。</p><hr><p><img src="../../images/atomic-project.png" alt=""></p><h2 id="ogre">ogre</h2><div class="admonition note"><p class="admonition-title">NOTE</p><ul><li>ogre 功能庞大,但是 GUI 真的丑</li></ul></div><ul><li>全称为: <code>Object-Oriented Graphics Rendering Engine</code>,即 面向对象的图形渲染引擎(不禁疑问,这年头还有不是OO的引擎?)</li><li>支持几乎所有图形API:<code>Direct3D 9 & 11, Metal, Vulkan, OpenGL (incl. ES2, ES3 and OGL3+) and WebGL (Emscripten)</code></li><li>cmake 时遇到 <code>imgui-1.90</code> 缺失的报错,建议手动clone一份</li></ul><p>可以通过官方demo (<code>SampleBrower</code>) 进一步了解引擎实现:</p><hr><p><img src="../../images/ogre-demo.png" alt=""></p><h2 id="bgfx">bgfx</h2><p><code>bgfx</code> 是一个 <strong>跨平台的渲染框架</strong>,支持图形API如下:</p><ul><li>DX11,DX12</li><li>Metal</li><li>Vulkan</li><li>OpenGL 2.1,3.1+</li><li>OpenGL ES 2,3.1</li><li>WebGL 1.0,2.0</li></ul><p>值得注意的是,<code>bgfx</code> 实现一套<strong>跨平台的 Shader方案</strong>,后缀是 <code>.sc</code>,有空可以研究下。</p><hr><p><img src="../../images/bgfx-demo.png" alt=""></p><hr><p><code>README</code> 首页提供了很多基于 <code>bgfx</code> 实现的渲染 demo,可以结合工程代码理解:</p><ul><li>Cluster Lighting: <a href="https://github.com/pezcode/Cluster#cluster">https://github.com/pezcode/Cluster#cluster</a></li><li>Cubemap Tools: <a href="https://github.com/dariomanesku/cmftStudio">https://github.com/dariomanesku/cmftStudio</a></li></ul><p>推荐几篇 渲染分析 的文章:</p><ul><li><a href="https://www.cnblogs.com/crazylights/p/13555816.html">https://www.cnblogs.com/crazylights/p/13555816.html</a></li><li><a href="https://hinageshi01.github.io/2022/05/30/bgfx/">https://hinageshi01.github.io/2022/05/30/bgfx/</a></li></ul>]]></content>
<categories>
<category> 3D Engine </category>
<category> 3D Engine </category>
</categories>
<tags>
<tag> Graphics </tag>
<tag> OpenGL </tag>
</tags>
</entry>
<entry>
<title>【量化】OrderFlow 订单流策略研究</title>
<link href="/posts/orderflow/"/>
<url>/posts/orderflow/</url>
<content type="html"><![CDATA[<div class="hbe hbe-container" id="hexo-blog-encrypt" data-wpm="Oh, this is an invalid password. Check and try again, please." data-whm="OOPS, these decrypted content may changed, but you can still have a look."> <script id="hbeData" type="hbeData" data-hmacdigest="ccac50fc08d0dd2d4fc4a571702034a4c2d2400428c3fc8d0e47cb23b2617d98">b3dc07a81f6459d120ce338ccca5504603780459e0306826441972681af56d624582f341449102626e559c75af687736c2dbe4e6baf6a934a24c0f78af13a83ced8b74c5e59aa1777c6d8a7c473d8eeb65368e9c8d836d47c12efb54e65da712f7ce30f43d95ed5d1faedfb647a5ab9ead8e3ea35bd6ffb81f3b46f64dfb989dad5998fbd968d8322f354aea44b5ba730decb5ac815450b1672014ccad9da2c73da7476fe270af5d2cff0cff70aeac6ccfd1fe160cace9d4e1a0ddc43cc0c379326660785b5fa0934f65e2fbaba535db0dbf085a0c6ca61625a8846c4855b0bb0f52ea598f22a9c9a53c1c2151f04eecf16c58c96444c417e73d4037ef69a461f975df7e624370078210fb5f983c29ee8e3f0ef2c6cc50e939615ee37e33c92992883c6c9ff8828c4bee8ac6287103ab26f6c658c0aa9dd6990058d3f440d0cc673a6d3db6cc7c6b646feeeee67bb10eef676edeb707e4d7b9f10b7243868021dab0b094fd3638281b2bb15d96acb1be6b5daec423d2e6ad1c133e3eade3055787e356da765caa8c93f298959510f5e4ce2c50265106bfb4408cfff89f290d2e96955727eeb52a627ffa26da381cad9a0aeaf46a1cf4cb7fd56a2090c3762662e44863a550a9bfffa3a98c86ed1db8194b6a9d66129cfcf34367f3c86c7904d8d915251f1e5dcd81ff6a9936d7f52369388053ba60113acf175086dd5b31521dbd052dd0a7e59248f5dbad97ba6aa059a6ab76036ca2dc21e607d79c68c041e1ee288628f460fa00c5a79ee62d0f625d2c767a7bc8e5fa99ca83b009a20d1db280d774f026ba2a8f343156db04b7cab70872e48ef48d7db1ddb4e81711d788a4ebc7741615ae93a92cdb89b685fa8f0096ec789dbd769ad90ec5c2f56de877398efdd0f56a840891a627e008d0c402015ee2225d3a1f837d5eef62bce525e0ebbd5a432116e54d8099f7623137ba9ea7c1431bce4c902435fc690d589958813c54c09118355a8ebdbec39ddbec6966144b43f689a0e1b267a1f97a4b9048f352544f920109c7ef2ad0994092ab990ff3e84441639b2914f23c312a608acd23e3551722bb515062b39835df49e0507719aab9b52a65ecc00603c18d51c3fd613aa4732536ed84399e7246f81a40c36d5b42dbd59dec4217f1ff9d054dd4bcae4d8c3406ce5ed05cbde2b0faf6cb7410312d768f1ecf50af72d695fe9b8df12c4ab45d6ea1398a998d8c221e92134a79d1e0c7f7d342ad164d51a331501f97917b1fdb9badb901a5007a5ade86f2551d5a002273a622a5bffea692dba9c677d1b8efce53dd4d9443ec2fc638c5434dd7fc5d3d1b7cc49e92a2b5b623cc6377a42cd83382f2c47751cf8da811c35a9388494b086f74486b57127c61e1d96cec892d4af68ed55fb7c7e2d27cf1dcf90e28578b680936476d65f1a6a26eec30a0c1ce4a3829af2edbbbf9c935b6e91537f7e2ea5c46dfa534f3bba03e3c0b5e4d2ea2382acd99c108f9d740f5fc27780cfb92c308d72310e13d6b360d700e4afee1d33b6fc8e8c722a2d8a8b499843ec2cba4ef4e707bee1a6c5b8cc87b509af29ee50f69302df04f61b3ee2cf240496fe12271d89e601ab5d9587453b985cc3a08aa45a51db840066ff380afecd2440b7fac168eb97337d4192908def4a519b3f7545a190d01467ae2202e1cbbaecc16eb1139c9744c839747f60c7e8d9bc60368612b0cf8dbec3fc1631d070a8dcb15c11bebede7977de65a9e3c77331dc68ec20bfd528fbcf1973bc11afd1aee7919edb34e0ba1e93b121d6898bdc893a3c7a9742951d30d4c066aaefd4d03a88435b4619a4061c9cd1926a70805849ae72caca09972271f205c51be5a43e1dcd10703b06e63f7c1ed8d3e4c5bc82e52612271b1a9c22ca1d4684c7841bd4148a584f6ca6a7b809e72593f435800f946213709b7c81ed7a6f8d761de4caa4e5e86d6da13f6ac8921c77ae62d7444ab9f5729ac4df2e434cd159a5faa1e87fcf58b552ab594ffc917e8d4ce78cb1dff1ac758aae1a2f00622df3e18dedd2901c4279321bbd29cef2c6e5b3ee23e91982ac5d5fa2046def80e9fa6f92d58ffbdbfe1a6b3a30943b46e671f2cff6201ed6b123cfd8967986aa0b1865ffcba603ff4ac1222391a8c1ea881033b77bcfa2b3d5667267d787ff997b0a547ed2e19b9b12248291d1bf90b7db8975413cf50152d6399474bedfcbbb1b53b703545972071895cbd40678f9384c59cca794c1517670a64d392bbfbbaca3a471e509107d11f18bb33db61fdaa63b3a36f109148a7b6d586f33266282222de2b8bbb4c81fbe32a59f6307562810eddbb5a163bc613ea6b7267f1f7e3c978d5071ce1caf590ed32246a7cf964eb21211327d0f85fe2ddb5aa0ca5d34f578e9ec9b343ba4b7b46fae4c6b484c76b56d0ff42fab92d68db4ec25805b27caf8b56e8c86bdb28ce85e8d29e12ae9dab79c758df57678da22d568a332a049b7216ed61fc1b3ad25bea9dc6cac945d179e16ea474e731ae155d96bfcf7b3e5aa67cc843fc7d35168296fcb81454833ba31d0eff11af7ef78a87ad96ff99f08ab20cf0d0df37e291987a012d0ae647f0f9d2d94386d26f1e5931d0f703e1fdf1bea15882564ff4ab46f7885463c7a56722cbd339d32ef5474e3fa544db9c5913658d9c724fa190f03a5369feeaeb612c83a65f96125deb436098588c9eb674a0d4bec78cea3fb11006194495cca518d92da97e65b8847a084c9b890be7867686d5de5a9a85feb332bdd8d165e3a5d7c2849d068aed5dc19eb0c6a93c2d8a06e090226fa29a097a709464315b123706245d85eac0c8dc48115caa926077a9fc04a12c3bb23855f7879f8cb61a28982c6c38fc5585e3ee5c37975f7ee96756cfb770141b7e29a8a132a4f21e50564e68d082c52d6513ed08f878ef3d4b27f57715d0f1545c66ce3c12c5a6db5d3a15db707487db6bae609219a293e5ad8e6eceebc7507809ad490f6b9b5476761668d5e9e14b68668eb3e137784acb245546c196f073bcf7c42330dd938d55eddeb2d14d370fa0320a1d1c2224b1fac5fcb156f35c0b55942bff19ef9e47a6340e90e9fa763b71f3e88f98b5b9c6c52199b0da43c685e8a47480f6d34aeb88411c612f90d45ad0a086548fa391285a827fbc12becd67ca3505ffb9fb50e679db618fd388e45918e92c54dadf8cfddf08f7a7626d152a31c327cc9a8a6abcbf02d7d91d301c2e166db7bca6a6ab2cd46d31fb0d655bb6beaebd2fe16b55b7e39e53eb74faae64c796a6d45226b106180c153cb708a47f6a69ebb4e5726b6e3a511fe440bc46710f0edaf058ca35ec0f15c21dea3b4827be686d8a87c437684c168f6b7a4d2ae3f01e4a376fa660bdb8dfe9f5106017be3298f5e2fb423435d84213a1dc17dd2bdd5b4474d640c46e591d4f87520af85b62c524b8e9bf5aa83ab8e1b98e89390daf542449a48b02aa02c691dbe354292310677796525596ad33232c1c48742e4867e3c0a77cc9b58dc7b264aaf126997d934e85b18515c0e680bbed9dd8918d5308d43108ee26f0b3d62e007f455133288deaec3ac798817b9458c9bd88373ea5eaa823850aaa32bd899774d0d4d87693194e319d93004a850618a9767e02a512eff348d8afc9d03cb6b0dec2f3d10dec3970e324476184b389d5d0603251d0a90d2e3e758a32ad20e6bfdfc8993391bff054fc0785462bf63df64b17f993ffd3c81260c012f41f66d3b4b6c5047e2bcebf2c9510558e2dde850542246275f5dc9aa5ecb350ed03f03fe84a896e52f74afa4ebc987bf267f466aff06552233ac2f22262d62d77653ecac72d7f5bec72da6b608f07dcc63642c91219057fdba0a625e4d8beba0466f78386b9e5595b5c97620373ab0edc63a8c9ea0e9d54ac017c74a87a91252f46b5733f5b8ea4da1d6b3b0a8d2cceb3837f3586d0c5f59296e61702e085f3450eaa73ad4c8335fe5bea2c101294ec4e6a3a44c726463b91897b39cd710f785daa67260d8c96bd02447362701ff85057db5b5dc6e0886f55ae77af47213da90dbfb5c377a8e8fa334c15e4c0ef40cbfd0113046efb76b985e4bc8cffed63933db58180a671332ecbf14c4e78bb4bf096562a8967a312eaaa0e7c508e637c8013ba5e753a14fb17b0296b91e81a6ee88f280211b6b2a944e5d19f3dcbec103c6e69f232096541ddbfd64a3199502ba6605d2925b1c913a3515875127ce36cdbf0622774a12b3e001002ce08d37f3ccb7e4aaf1efeb97c7465ba538df0ffa8a600d8d32961c32dffa06f484190f8b1db14e9d99a897d3bdaa62afab5a5bda81b19f476a04a7f5653dba71de1a516fb81d14809ee628e9faf78782772571f9c528e038c1ff8408e1c654a9b363b77bbcc903f4cfcab0d500b789e33994368ccf8530fdc0b299930a14881477e87f2fdcdffa56b2f9780315ce06e299a1a0b0c85f8f2b090906d2e9556ee37e83e8e331b880cc87f1bad37e0ed79a02708d4844f0136b949dbd3c074985a380b662d48a934f4ee1711097ef9736a71cf7534ea8c85565428a7408c324de2066c716bf6576b03bb3b78b983f33661490cc0976a7150e17f835ba09590ab9670e220e7bcc3ef253cbafd2bd3cd92b8ab432fd6db13d11476a38ee69642965324e1cd411bc032547706b91001886d3c44bd211cbe15d136912942596e167d48f832489f1c9031bd64ff212ee6b4e89c4429480a4b9370d25fcf2d889e2ad7afb6f80444e755a7f30ce42d4366193eaea3b6708690b15847ac317b6a75da7ab1c8363440addc9f162e68612b3e4515e969e71f049e2ec173554fb10a5ffa5a999f54fbd564c9ddf595ec5244ff0211a7ef8ec7812922870086a213d01038d0f76d6371fa979303b5e3653e27978b565b8b2569b9095956f5df76c65bfa64b12291646237a0d022c099c5191d213ca318acad044192c307dfea73c104724afcc2e6cc29505a3366b8f3fb73a732221f09c4db95ee59d7aea7d185a492fc668e36f1ef9395018c4eecac0230181133387525c6a8c6f9448f0324ca88f61df08a8421688249d9a01938acba3c42f4e97e96dbaf290a96135ed97b7b7af67cff5a6648d9d3a0ae2730e5693465070683d912dd9631f81bc692c7c8232ce73d87459a232344bb06fdc09e2f70f96bb1c92f0bbfe541ded74a5508a21a7f3f92bd447ca30e1f35f383d3962008289d34c40d6d49eb74774bc31af3bcf3bdabc05972126a98e696c5e7e8e923be2e5b30486fa7c4ade5d2979df62cc094d1dc4f2919483deec9e745637c8f561563e11d7475d2cff990e7db05a30d2e29fc6d48ef3259f477472704e16aff4ccc3765d9a250e8d1766d8baea68a3b91093e833f5326dacc5c55b2dedcd0b1d23cceed1a64bb1003f4fd5a7cfe4af6ae229117010cf9b07514f2f1feadb0356d4c0ea4b0c26d5b2b647241e035d65cb476a9f1a55e131708e7b4ad03918916b52342a6361f35716a260b39bcf6876490f0d213d0a9264a7277191098c20743d7f754676bc22bff0556c63dc955e741aa4c4d58d5fbd8720a503cdc37ec5b67fa0b13a6a80f06821f5ca52f63d9489a1e00b208218c7135d42e5078d143173944f188c6e1518d1567bc3768c486aa656b3e071f97131385b7c11a613679cd581a4dc69fe3fb7c24b49a4778d1a90e13024b522e1d44a70997d436d2f484e6be1a57c65fb292e94dd3ff810215b312d752a13ae10af71ab554c30245224060bb6747b3004378d4906059bc154efb5bfd94c3b82305089912ba62586ab9c9fa79fbc15e72fcfcb943739584dbcc8ce5c1ffdec4d0391561d5e3f52817dec927b56b0c938c85d69260ee30819b0dfd688dbcf4fc09a46dea592ca6fc9c498218f0d81c1981af450204f07d0a52afaa4bb9c4591786f8bb444b9d4b6402509579be36900a2fbca4b0c2f42255a8777687bde8e9af2e14f872190ea8e69d6b876217c7fac0cabcae3b5def97924576b981db5d1068a6071395e916c84a6e7e28c62688a3b2f489b392bb48ce550580fee2aac74343b9254fa749a723b44543e7c89e259c8685e017987ada3431d1198946c06cd75122cadfc2725e8085e3d380877bc5e9f123a85744974198592310b2da5226b9275250b9fbcb7c1fa1f853dfad337aa9b6bae94e350a01719e2205145b1386e11bdba77dce442e218692a2ea4269b9ff4b4a62050616ab38001a7d290782255e925b72b4a3b9440615f2f3c1ac63b2db7a1d6a89a544876439925ea75b167a9b82401cd1bfe8c5ae2489edc84b210f5e550d99027fbc06fd3bc8cbef9f805096cc7d38c8ff026473213bcc4d03e67430f241fda1d598ee75c844d4db4ebcff8d70323fbf8b9041fb18f01b2d6c1b0a4941fb182af6e2c56370b53398ac2ec92e8d1e5b9d8723b5ccb6fedde1b7ab09bfb5818cc1ba22ad098f5da1886ad4ed432a5ed5edb0565074ff1b9ca559d304a72a9e6cb45b2dad252b565c8e353c85eae8aa04e2b321f2c26cf923d93035a01acd6c97021e1fa8951fca534ed031a39c485c6a9a857986a183a732ed97908e849d814a50c7c498fd2127715198f9284f0a78c8923f0883c1757bf3f0f61e1937631f953f3f5cdd7e697345342c76d3baa07b5f153cb5a73f8ee6470d1342f433305ba02a15ee443bfd1fcdda5a45aa788ad49bbe57c96cdfca8cdb5af288405cff7530a0d93f6e589fcc68af561efdc9356280f2731d51e059a535007a4ef824057b78c5eceb7bc1247b79a76631b12804d6657ef6589d3f2d937de329eab6d7edd2704ddfcd6fcabbef71ccf75cdeeaf58374cc014f6d77fc35a257b4b5d0fe4c35c140a5efea382ba00b872cf097d23c32a38a5afbf2c566a47b6a293118de9768ab783dc07d664a6789ffd7460df8286221375c2d0d77119991246eda63c9988ecbfb81815906b35a4417ff005bc299773705d9f4bbc5152230013d245df90f2ca0548b0a4c33f9b7c6d0d0b251b72cb790cf106e7eee4a2e1944952bdbcd0903bc883083c8fc4f7f3b755707143858f07eca95dcc4a2df12c32e9460dcb38e19b0c38bd06de0ee21c37bf157be5592dd67f36859c288057a2aec0fb6d915160b1efa1fffc4190f13d32e6c82bb4d51f75d60aeb22dda25711d74aef02a5d7fd5590eac292b29303c5fc2306a48cdb0947bf6a2bcab792aaf4917ae2762a024610312ca617b80b77b2b37447dbb789e7db60612ef1ec19369afaf233677a75ed4861e75d5d14ec87820be74aac9a4ea11962d13d4f7b00d3b0a4acfd3ff63fda2f253daf867dca1d35254b4580ad1d9e0244f3062e550d935bdad19c0811bd3a8c781deb838819624300ab55f00d74c5db68114b1f8acad12cf01854116a3df1ad92fdfcd3c3af23cf76438029a91b849b57902348425f6b61372f738b18dee55976182e269f321386bd0232fef9924a40cd1f1ad998d3867393bfdfd0fe86200803d4f4ca6472a05fbda01bf00c2fa35d4765f21c1aea3dbb18e03a978d6ee980bbdd3d679454e11d2f1e6e9aa80fa2c19931231dc1e93c74e12daadd315c318686f3f6faa1c97c00922acf20166613702f32c6536a06d0594022c4f7014d545ba59238ed60e6882a5d6542100ac0f677a5102b741b50fae374556e2857dc4226954fdc2ecf524fdc4df2b13f1eb0375e57ccb00fd95eb9c880d1d0435057f8186be77c71785b1dfe83982c7a725f13097b598e9bd688b41175455bccd0a6a97c08e424a77aabf2f904b27d72c8d9f86e40c9ff8c11a69c2635d7b214ecf26142083c7927534e79a782765686615662c2cb07107f4762e2972a459eef3a7975647b22d530813837c84c11e2dd6d1d9899e5da1e4bd1dab0661129a6f6417a768f0bd1807ecdf621d121285e476e1aa69e2e3cb1b7989fc6a2eaeb049a460d7a62d5db494a1849c0c875c5399de8b1df17602089e9925e1be7b3e9cecd17bc7d8896f9c045fada73a4b7d837f9544e47449990766c51f732973b049401f01e5c1a3ca06ca93175cd7f986268a04128afeecb3c42e159f1f28d68233df3b3ec5ff69f5bfbd459df477d1dce269b7d28fe06b09a5fce8be321a45e75f50a7837fbc723e01bc37df0516018d85050c8fa13c1cb09a0d19526f7af889e7fe30a56cf58e0ebc277608d5ad98a3d4cd35e4bee894179389c6c40ffa6cd7d155fd01b14494cbc4a6c66041fee34423bbc03307c0801376bf05340ed09297dfbc375a7d03e4b0d5cc4409cf252579ff7530c6335265d1ded492761468c501e05950c01a615150c3c793e3f10ea99bc10324eb24484aaa4bce6d00d8c5f01521335cf550bd057ac2426efdc2927e4ed17c19cae3764561bb17eae941db1d4dc4dc8bb2acabbb27e760e777e75806fe7304d3871fbee9ad38364438b11133687c34c7b6466eb0ea263f6ed643bd443bfa49e61c56495d9c2c898211e4e890741e4aa230e359089f6c29f08047fbb845332b577c096ee27ddf414ce04973e32c06a69d5743d4e6260ca56d0d0257bc1cd8a05a96ecccb0e8b045612335058f12020d54ea1a929c0a687b00ed4a7a63000e4ff916f71e1429bcbd1be2fe7d3d96758f8b83fee196159b41e1a50a268fcf948abd1f21d200358b8df9f953229d6fdec867ceb54e3f33407cb2997f85e775595f0cbf10e273004c29057512b2df6f0d7387f84e54a420f6b55903e17b83e61eabbadbbddf723b3ad85dd4b6e58edd90b94e1f9c3ce3caac5bc0a1b7c37040f006d8812da4b59f4bac997a74d5ae52f0eae1b3d0db48b171a39190b54df27f9cf1da5645ae1589cf2d7b372821a24af1a633a7827376ba47b5906f25bb207048f228e0992e21f00a6ecae2fdfad5487d84d1c240d72f570498bed27161f016f2c05e39c4f67b42388b35dc4a1fcf31b371c6a459bec5f131de632e912d34e78ebc1f681b5a1a7526ee47b6c12632899636817a37782ab81e53c756f89b406310e152eca94e5790de55206f8ed40ba6a7e9d15cf30c7d7430309d3cf87a2bf09b98efcacf8079912402f017b45d71c32010a5617a8fade3d675db1bf7ef462c0ad1f32aee4b2a8191ee76c5705f3e4e7f43d6614875e421be0744a6feb178474ee26ef459cbbdd40d4198d36664fa1b9b390396de50143b0c531be1cf8287844a8d703105cfa1b3268ca0fbb9c9e4d16c77af21eaf1f702186b23f744d3d325b6717e9e65a6ff2b0dbf0320ed1a0feaa33e04b57eded3957b0dadd5bb9d3194f6d6d95c45c30b6390a44c545a363ee1df9de55b2226ad944672560849eaf6f7fa28f2538a6305b5b35d8b0a68ef36d82955b40897bd5df78c5bf2469e7725b587c14328c14c0277acd954f8524802a1b86cbc5073fd47c02b9754053db5239216bfa10985cee5b515a33633d8f95e87b20e0b488bec5cd8c1e36852ef752d024446ff78788504c61aa2c93c1229dab715dbcefe4c494d28901f84610cf0d7c0fcc2a5ba17e79cd70c05a059e4cfbd2a27031589b825a6159b227ed006b75d5bce7f90cc168d3eef33c63cef64cbd58ebf9e6a0aeb008b826fc574ddfadc5994850d6804220485f8cb26d861a52aca7375981bc3ca64d87d9bbdc2354784b415ea3472b4cf43cb9f29104c5240a2b57f3c9f826c523a6b5ddb2735e422d76f884b20254751ee5a0a802e52fb93171760a43cea797222f400582d0df72743ca9a86e29b9f134cb359ce8faa63dab5a16e14fa4a2621dc096facfb6af89cbe4672add4f2730e78f976a00e2ab454bd4fdbbe3947d730743136cd4cef90902919c582b85e548b42d4d853ccbb085ef2eb4d64d2148d06c60de9eb97841f4d1f6e65592b340a1512efaa8d5429fe3282012ea22aae28b423aea2cd683325f367dc6066ae7b85721d533566c5b5aaeeb3e94e01262b966c57a86abdf64403446be85c79f7ed63b3e24c5853d89d157b1cfc959dd99559e02babf7ef33f42f8bb6af9f4baf4976d2876f32cd591837f172ed3cc77960542115ff15338aa01f77253df5ee2e2e98e4cdb2c16f0c1fcdcf2242312d87c5a7d6b2f1867105850975d57656fc5ffc701f3aa820cdc5695098f240c2eba9102852688606d3eccbcbab66a42f9f5d9ce5f59b428633ded4e6bed88c3ba699796d0cf8b6f16c514f65a8ed8f105f3b99a8a258c5bdaa8bc7c82ab234228f48c802004df2bff74942be5320c4380e90c59d55dcf6398c0110789934a163fb64793b1134cb51aa4343a3ec107ddf561e800eca85ae7db7557456f3b2fff5a912ad785a21a4440f534997c2218261db4ec51893d4569d73a40628c9fc092132420a55145838e0a3797b4989dbf7aace3bc226ac39e5f1f8a9f3583ffd63992b002c5e4fcc9a557c9550971c0d3ac8b2f4de67c995e58fae41d6963733aa4522057ee4c2b2a3a921510f7295f2c6ce1af579c41834dd419e8f7163c1a7e578ba99055f21bbff567a7f09ac190b598acfcd244d5bcf3848166eddae4c578d59888b46e2503d26d3a80dc785b5ca295ea1c461ca9267172a829f1e86b83328da757afc14a016fc87328e68fd0ee5882121c2c90d5f53a5ee625377434307fce6300a7e68cdef2c1f621e47733ddedf5c8924473e9a14f05c5ed8d25e4f78bc2f6dbecfef02c48af87b8ef3b6d16f400999ba994d9b076fd347ec70c8ee8d493708f13cd853ee577dbae20f177c728659e257256f45e3517e1cae32b9a970fae0b76d3c158317a2740a5f6b0bbe16654bdccf0951da074e08fbf73d82862a5cc6fe560b130d9697dcf18c53f602e6f7af97468a817d1c133a49e6e15461a55a8d60a67e36e064a26cf7df270af389acdac2aaca563d12ada0a16005c7b46a1dd49ebb3035ad35de27cac62fe0125611e4f25057290f13469c3e7c04f7f60966d87b8f5123272cf31a6716666472f45422fc4c2d814602467441480073fa9910c04170211fc97fd34cf49bb7cfc5025760b9e02fc3f4403d07d422c48505ce5a1af9a3628316c4fd015d2cb3a57fc7e10c490e58d599e2751c153326831169e73a7b4c472f8bc0faa86b208337fe5b31f7ef3f512d9b0bf96f95ccdcbbef1ddc5ebee6cc444fd4ba19fc3339a55cff946dd10502c119b27a7c701c92c8d8a9a9fd257f6a98e358617743ae30ba246231798cf12545a18f55be9dca720345b328553dc5e08b2e823a1b1c6862cac96a8dcd0d344ec6af5b9c12c8dcf27fba2d0fa12b6a2dab62e85ad0281c5ae8322dbbecdce7f7185dae7d46322c505f461d7bbbb62dbdd111c05d303fd78c52e16aee0e4e94d3ff6c46fd70cfc31999c0827afad2aba2314ac7802ad81222a55beef179f1607f82aebcbea7df8a1e269ee8b472ad8391a7e6cb49e41db84adc7991db0e68e41db5d9f7e02cae2b8b075717ab465f0ab42d8c73ca31ff0bb1195c29b68c83b93cf2620eea1a46f55259c7a8166cdf81e7cfa1823b72a0720da9dc9e0609f053b625a4fc181c0eb42505ac52b0e2da5c29d9a10396d56a71f67550f86bf7f320d7ae04d4b7d333b3baa1e5748a8d0df4cc164a0d0e238e2db0a488c6b1f8062af689850b8fcbcfa8fa11d01fd1c4b859af1c02b2fdf8f98f196f2c19849633915fc40fe6cc49deefaeb775a1facd5acf379f8ae03d38814daeae0c9c3dd8bc0532ff8a841b8094dbe0dade848f6a3b9facc3a184d69e569c8f2744add0386abf1211657ede1089dbbc8472412d5867b9cc15f78053dbd6154542a9a115063e5e325173a5dd14762d76349f1d84ed48c54ce19641c6231aa3eb631fdd73447d0e5c666bef3ffde1bfde750a3e51fa2f3707cbfbc71a373d4d5aa97eac167a0eaabd79e81e4d9c8c163ac845f9f3218b8c0ec8f945db736f58ef6af0e50186b9bc9ce037298c7f888a87f15e4d7c844b74d2224b589c16aea40a6438c6f58a2b26805a8ed17236aa72231e91476fa43161703942cdb7e473268e1c944ece7203e15ec124fb4015b728f718b193ba9cdc33d53ab812cb699040850cb30f2092a1b3687d3dd6833af489a540a1f5af29affcdab7f4f64ca0441c106d6b5dab055a4576e20dcacbfdd9e4bce1134d704ed94cacd95be69d9f0a4d378d29f2ea5833e46d9177e02d1273e34890c57ecdd5b543fcd6ab6bbbd5499b2539ef79855fdbd04cc31a84908192d6a4916eedb5fb6181f9dfc9783f92d278fc3a8a046069d6dcdf04e820ad9f4752b09bd853ad4c41bbe45a2da712b2ca19fdeaf353e81bde4d09a400fd5b293e046b8dbb6f2ac0302e3fac1fcbbc06c663e842c0ef96020c541e093c9ce1e2ecd8be9f8f21f546556c442db02016ace0bbca86a8040626ba544357bd6a716db3f03471c5630ae7250ef4f326b6914cb74ce5654cc6f065c40af7812590b8e220f137b8db6f15f065d4a867fa3f4dff46df70e7580b0500d09007a653cbd6f5c77096ec306c802516a1bac9fa5296cd95de9380655b200cb8d35929bd9c6e25651a21f676d254c24149b01427301fba1c033ef7da16ce20a6a2d74e654e6a8d2b006026050249b0e235bb2131d6700c947ee9ab93379d9637040d64d9fe9ac2affa6cdba2fc983be722f88f7e8dc2</script> <div class="hbe hbe-content"> <div class="hbe hbe-input hbe-input-default"> <input class="hbe hbe-input-field hbe-input-field-default" type="password" id="hbePass"> <label class="hbe hbe-input-label hbe-input-label-default" for="hbePass"> <span class="hbe hbe-input-label-content hbe-input-label-content-default">Hey, password is required here.</span> </label> </div> </div></div><script data-pjax src="/lib/hbe.js"></script><link href="/css/hbe.style.css" rel="stylesheet" type="text/css">]]></content>
<categories>
<category> 量化交易 </category>
<category> 量化交易 </category>
</categories>
</entry>
<entry>
<title>日志:2023年11月</title>
<link href="/posts/2023-11/"/>
<url>/posts/2023-11/</url>
<content type="html"><![CDATA[<div class="hbe hbe-container" id="hexo-blog-encrypt" data-wpm="Oh, this is an invalid password. Check and try again, please." data-whm="OOPS, these decrypted content may changed, but you can still have a look."> <script id="hbeData" type="hbeData" data-hmacdigest="7cbf118ad0e6f9587ccc4672f478e6ceee77711202396680cd894c3e41e6629b">b3dc07a81f6459d120ce338ccca55046116de9af7135eccfa9c61a82756948fc80f892c477e2c8b4f49f1e28d8dc47ae33730d80f767fde161c078e95f5e43f85de758fd4c096799f9d13ad814c1721eccc7799ffc0b0ed8f3323bac374edb4542d4b5ecc52709d59318a85c2a0f698dfa8ed10a11c0c274da6eae32926a611b121fce93d73f6b752644fc68763d978715773fd1b7e6237849b16198cd5c0c5b15bdd5cd8e7e45e8eca0808e869012976a2883cfe76dbff330bb4ff1ebdd04513d1236babe8023bbe1694b45decdb6c8db6cea8dec71e2c7a6a6cd817e6e73052da845f197fdb8f843dc73242b78366ec81ab9ba3e9bf95a12bff3b57eb410246f16ed1cd6d8bfcbba40e7e4682e3528993029a8644e9b67abe062aa8ffe4306fc4f921bd6a091bf4a5bf6659013a45484c289b524d7903d9d5d0ec5d959c29a63a9c08018d7f0ae135aeadf4acdd2b9afc60f40a58862a6fea6c0626ee4a280f88eaacab2d26b69861df931ba6ef033e2fb668eb9400ae60c0942e2f96f3a385f3efd7bb72f186deec0e2589643897cea705f8fc08db94bc42e514e78af72e8b31d09e55ee65414088485eced1fbd231ac522f01371264b3117c5fedb9880f701f9a86a1bb6e937612cea1b0a452f5a1f53e25920da26f59aafca162cc240e111b607181e030cc15d71c672acbaf0d79c8f3a3ff126eec8474dc627e0fd0878320fd7694394be3c982f0a5da5d3b7360480c0e56e4a7ec05e623703dfc939afd5462ba28880385ef8ed0c25cb3eab4b474ca83cc99f29fc401e6208f35586f0d4d60e109f3d9a50b019506b4a7060b7ffef86a59ba815899fa3630ddca779f4f93dcf105aa747254d494a84d4b2262366bea5fae7e0c04e3f77fccb7ff1ea4418911eefe2d2eeca9774c17a4cec9dd55ba9b54e83890fc10a1072d598f6876742170db759c622b43cc111dcc27daa5afece0ba63c0b067ccb4742a2cec7f5a607f17265f0c41ecc926366be95dbd865870584193d9e549e251d42eb0578ad0fe9fa13ccae1b5e8289a8e060ad611f0eb1882a225a108ff2afc8cbe826158431b40de642de32408cb7dcc6448452ea45371d2e4f05e8b2a68cebaea2bd470aef7e655603642c7970198fb5c0eb9fe526a310749a26b810671133919b2b8574e7f83b2a94aa266d5669680c946479b79369494fb1c7519cb6954120adbe40eaeb1f038edea0c28506edffaab1303c9569d8f0b814b4777ebdbe5a37d63ca59d94985f7aab11e90527cfe43074af4c903e98561a6f39e8930dca543dd9b3df0ca9e95de5f35a095cc9d21fed4732a10365e75fd7f308984ddba97123d47dafa7bb779c59439a4e7b3e8ea9b622187e0d56c4378d0b94ef5bfdfa82c93de92780be970a0bedc4e62e5a40fbba68c3da3f4cc5a93fe14dcf0318f96152a7c900caa590623443382efd44dcd2657b15207c066e9af0ada28ae072a7c91b74b63b5b1c0e7c3206050fafc992d66984ce34d87fabf8d89892f006623779b8dce14de9d0f26c779c517b53913630963c2eb718f4edf058159f1d3e3e6073390e1a69e68fcbf93e687de96a44735597e788ef0a5f7ba6abba0d8e1ad2c21d544dd1c249980201b02c098369857310767b4624aaffd1d74925991a7d8be859429ec311374da864f37135144ede9a3da8af642202b33f1cc4f57300849f15624a5a849912b3c9930c4217af34159374166841393e368f2a2c3d551d41e02f52c5c11e21aa8e6cfc5b1cf976c451c832cc78a0d357b16173660127f9bf2ad1a6932249bdb9d5bb44b0a6517f5b62d888e7031cb4c66676b337397b444edd334d6fd53d938adb64d95856c2df12d62433ca777a573dbdca3f10a5a8643d9425194e388a85002b95683aa94b7648a6a2770534b9c6ea47ae802d0ad45c448acef579c52fc62d760e3f07da3ee8705b8a9091c583a14686abb13efb32a388703b9619efa190534e4da50e85db3035e8f870dcda18eae9515cfae2fc1fc30c8e10bdc758581aa6927075793111d6d287ef5616bd516d9b0438ce646c04fec9ed2f3da2566f8819e4c3949f7299b40d3b0bad810fe5b638bbba55832396ff8877f327d22c9bb8004df56c4f7a71eb2fb45b6342120c11902cd4509ac4700ba646ef2b3b1bbda852686bfff90c520cd693e5bff4bf1871fed4f75068144e9a7073d5aaff143879de5e5eb5ad41d6cb450450cf72374ef316b45fbc3fec50a0669f3590c38822d5f587fb11360ba76b9a53177cec356a9b8ae8454663da72442e9d091faf69661e5e5a3434059ed35c211646a559ff96f070299a40fb607c9620eef53dc6c387aad7ab9fff4b1effe2c5ba0f19f33703ff3bb737a5f0297a591aed80112aa925fa7e3da8880a46eee943b22277c45fa39ee72f4b11c0e12a932a210a203603abe97de62f07dfb23a32d5ff24315b2bf7c0fa2d295d818a88b64670ed5113deab7248dccbc19c6c80da13bd8a740a5ff06dd195ae6a5bcc2cab97ddd9eaea84d4740feeffd7513d85d37fb5600e78d298f05c22d4c62147a7d5e4bed8d07dcf5ccca91844b515fb0ecc763b37e029648aa4a1c2481e81c50a3dfbb23da18536cd9ab0b52fcb02462edecd94617dd0e35da4829e277a93724e8065782057e444acf67d393ceafb4a018b23e9491215aa611926f644cd3e0b1686e3cec067391195cd6cf0a7137f9daca16dfe63b8aeb933ee9b85788f92a300b6286409486d6abe37786e1dcca6a0972ad42b852db2de6de059305bbe83c9676fa1b8696cb19e5e07f67a9c7ae50867939da8a2b7a7e6cba12179227f4d77be33afa861b1d9fdcd2f946f8148cc617b52e8fdfb48dba05b695639528e4d84ba41863ae01052ffc2dda4d4b38a3ff91d0fac0a7b338550c2b1df6e2d9cc510231045c57e93bbddd6b1af06a713cf8dcc6092ee77ff11aefd582a68c003bf8a2befbe6ff5a23a5aefbc24d35ffd93ff02057eee22cab81806911845ee7ee3026986f0c9321ccf3d310bb96f744293a342507efaa914af591d57906bd13f560799d1ee7dcb63f49e54f7287609420d47108ddd28db331d786accbc0d88131becd4f45fb139f5c59a88baa5cd39922d3aecb03acec7df2bb2a23b76469f53e7321a4b57f793991c1241a3dfdd7f62c35edb025e184d40b242eb157d6f2a1499df04525824f2703913de359322314e442d111b7943e185c67d15279d470ea1de81a87af6e582853c4148ca7d3d0f445b99e6df72e41aa5ded909868ec4c19581e57f41e3914b6cdf8ceea74ecd9fbaae4250489669e15daeff789c6bcd3283958421623a395eefdb6861f050bc2c038a67d095f8dc194f67ec798002e415789b596af2cb818d4a9228796f908b0a9cbc5989da894aa890e58b33b8d90985faf7fa90fd37327abde5ae619b8a530373fa13b2b96b96c9118ba38799fa6858fcebbf1bbbf67d95ce3c21cf31f2c9d7c6afd0a65d8870a74b1404c5051b4f7ba7797346e711b4d0ec8952eb66cbd857954173279c1a4597ff35f1f572bab6b78834905399656d40a7c46d0f522643a0a7e9f9ceda637b026d161b78e39e2325efa25202ac214039708ee7457ff23df036259185b63e018b58122d564bcedd1f596c179a49079f7ea636a9333ad965e6908dfeed9210f920e55b094d07cb0728944ded731fe3db54c16a20d3dbcda5c4f177ffa5209905889b69607db9e8d0e7ba7ecfcd576fab9a717daf2205318646fcc56329247785c8da4c4315e6706a72c705083569787f5193cfab1c9fe0d7e16da2a1536361edd53016b66db0e99767edcdd039a44362735d3b0dc11a3b569f2b05cc1a39343ca16ff9beedf8b32f994d9b2c29f7a5f38fe98c709ded2541296b7b55336edeafaf32be246a774790065253ca3e6c06445deb5d710703c4608242817664614897ea9929948b0a8762139789b2d649206c64af2b3763017dac97afd001a2590f6feee0fa150b314ab6157088a48f819c0ca172262496b4319a6b65934bc08cd4b4ffe73783c9edbbf6493059af4d3c1eef19a2b7bfe186e813abf2e8fa66f86cc8623930117a8b4b7aae0b40477327aa84f4183c4e89796091dcbec3123629852a3cfc6401a62d54b397a58689056d5644b7879d460b7c4ec8127ede7fe606f547d798a912f2dd4f12e3cb66d67638d9b196034b5c2000b0795d6e69bbfd43d588e5fa55424e8d84f7420571987ed85d8ec7ff6ebd306d8445b6cfa915e74fb2eb45b7ec6a9696e2badf9e6c9da6d076b6f8990c8620b4eb14e306656f012afc0d023e6c712e08090d6d76ace02f0ec8e87952faebe339d85b4ce5ca557b507eaa0affd9cf847800eb53c496e8eb48ada73ad4c394757e0ccc5e58304ecc8c2c558dc8e398e1a605d10640e948f4439e171f3e90a10948010504e03690dc4b600a5c3172010ed8638c22ec9c11be45be3cf433a6beded53ad09d468bd5c165d17b868b231563e2176ada8f01a6206c43b4e4221f8e8f5e9fe8d59779f293064313032bb88512f61866088f141675837774e69e3481d93bae69d461d29b61f255345f3b6ce97113baff07fdf676308ab1cf094eed387a0dbc109d3ad39628cfc2692add385e077cae2fcdc2d72a6913fa7eac4dcd7a8d837aede621aeddf26718710eaee9857672e3ff55e82aadf1098ff553656e4fb658f28bd02946e92735d6886db90e015bda10e5d516cc132070944d16725746c45c6409ce0566f99e38007e97f079c3b51c6b9ef82ce547df437c2f9565d3a0566884add6d05f9a48495b020af6821b1469e2238dcf84af12a8aa85e0dfd6c318f28a6f0bc2f6d44b1b11918fabd9257628071cc27b7138753805b679a462af7988878ae0fcad6dbc73d7ca118b8711b5c0c4a35ffad9af382fca0e2360e53c96671130fe9df3aa2091084c1562c0383582822dc1378369c812829d13f664f712ca75eafa1b7a0d58e556b9704e075e59e04acf2991612dc327dae929989bf6f9173274aa5c04a4084304456cb87c79efb80e26c7f3168da956054ea72661577ca080436f1abd4579587b8495c3b0134675187bd92dbf1390d8c86dc3279ab6d8aa6abdace711160e49a931e72b7f93c2f652fd2df2b8cbf5f82d32b6d410005fa3b762f81f0cbe63c87fb588d7ca1ea51996ffc7ad8840f666d408bb4b42ccc2e3d8bdbebb2e22edc73ad0529b488fb0c7293b7b6c8e03a14eb43d4a3491c76a5bdad00139c31ff222df79e38acd0dc19c85fd500cca9df1839477b52b9afa813397d798f2bde2f01e9b3028f49d28eb10c65def56d5f55ee77215f2b42ffc8dbb0deaffee77f62033a4e1676e41426cf862ff7a944f86352ca2c01f09b94b4cd5d0015d8e92310391fbee1ab8090fc692b24a4f50c1da46c3e3201ac671af6652c2a041638723bc589da0f86b8c77f8f7b1e774e272a36637aba293cb566cb3b88da12b331f048a774cf6542ba626f5fa8bfcc9083caba17e617487cc02c7bf36481d9fe22181eabc9466d68ff5008500e516bc5d7cd0a121e9a871481fbc19718b7e8753fa4d970999843f4ed11b09bef81553f278cb168b1f74121be7cef677bfed112770af728d8447d2d3bfeda9139027459d532e3895b201fa683dcf39fbe26765fa0f0d01685646404e01e583e4c42b09af25b35c907363cb30c9491140d049e5c6e3c2c738728f3c60e3d92bdfce4e09c8af4919090112b3531b3ca91d1898cb0eda16962869c051b5d7d2221a5b6e8ed3a4bad7296973791a491b0fb8f215323ad3d75feb3ccba6d30d2976083d9a13f2a09b25b6f776a32df601935da832e8e72d30196c984e6159bbe2081856ab6c69ccc059b4443d71d8695b917fe4ced80776c175a33911cf26539fc7975e00861bc33fa6ff5cc9474e185ac6dc43be103dfe057c07a3198873a96243be09d5aadb9db9258e19d14b25dad4a7111fcd901f71b26395ed7aea3aec00d59644990a7f57b117080115bec02dc7b59c49c7bbe95337d7909d742133f52381bda06af811306206d28a71bfa9658b41dc619bff64ef4673818bbca1cd9d4e0ac4fb6c56683ecb9d2ae4cd0b4efb88566bd8517428e1ef6c866b7e2d35a92e0f993530d0254ecdd56847f4e301ba4466db9194cc457b6342b8e7d2163940757002893a2b158f15249c0e3b2588a2b614f9b624a6652884f644b10124087ce2c8ee1c3f255943fefa03a1b625eca4e7d253bb7dd0b1af69e833b0b5f47bd5493251687822e549f1078c07f56e9f24d4c7dcceefd9fdeec3396ed384182022543409d4c19e5e75c81b894d96e19208c61fb8543d0a17daad7e9f039fe028b929322fc835e2a42a5f11a0c527f1b7227e1b099be74a9130d2845b9efe58641ec4e2e464d3fe5e86b361f734255a544715e1be6f497147cb09ec363fb429693cf135bf53f08b2c0219e3f9b8456c570418a4ede7b75f8818be2e82b06055f1761a1ceacd69e597374e475930d8e0bfb0ddbecfc390b2179a033336262deb1f6596d9a626ef12650242db6a8c0c53918e1979f70128a3b1293b7ff0bffe833b7ea81316d81fbfe57d0991cf2ebfcd764ca0fae076313338fad6394b15dc3dadc243f7cd1353d582c85827039c75c2ce20b0e251745ea499fc949591f5f3334f0a51344e4f24289e02c9bf2dc8353a83a5f9e8bd22e23498d3b2c056b89d94a51cbad71a45cf4921f694ba6678504cc4559098894fb93dc7a7ea45f98111a9a901357a8f63e99db283a6c0b66acd86866866259ff2e92fdb1e6df5f245da8ed74d6a665974f040a698dd5dd3dbe434b9d424abff2eb91bf10d1f92fa1e3fce4e2ffa8a44e1aa65e22ec140fc88cdcfca428a65d7d033c12d44a233e6b5933a67c77b66bc1e0bda8494e441b3dd13408db37e837378b2dff08aeb464af3ba908d896f56fe2b5c81572b34966ae26b68c42bfdcba79d76a3fe567be1d40560fb2a64c158cd661dad94c81f24e3b1ce2c14ee96eb3d8a588a480e58c9d48f6a9f556420de2b78326a59dce0783fef7337c18575e8d76501dabfd0ff986cd5f24ab5379eb54b35d2ce4783f919734148b2cfa7bcefdc86a2f26f9062fb673496b8ee2f1feac7f6918f4d54a5d2d171be11ffad887efee26d9e1c6ef17e289d314e630e7df257709c6359c7fd37fd3d8ec49533e0ac236a7fd2aa69319a5f72bb21f19b1300822ab23443acc51cf0b4dbb392af842b112529cf175cfb39a404963634a20789dacf69745ea03b9b422af0a97bf40460f50187</script> <div class="hbe hbe-content"> <div class="hbe hbe-input hbe-input-default"> <input class="hbe hbe-input-field hbe-input-field-default" type="password" id="hbePass"> <label class="hbe hbe-input-label hbe-input-label-default" for="hbePass"> <span class="hbe hbe-input-label-content hbe-input-label-content-default">Hey, password is required here.</span> </label> </div> </div></div><script data-pjax src="/lib/hbe.js"></script><link href="/css/hbe.style.css" rel="stylesheet" type="text/css">]]></content>
<categories>
<category> 个人日志 </category>
<category> 个人日志 </category>
</categories>
</entry>
<entry>
<title>Nova Engine</title>
<link href="/posts/nova/"/>
<url>/posts/nova/</url>
<content type="html"><![CDATA[<div class="hbe hbe-container" id="hexo-blog-encrypt" data-wpm="Oh, this is an invalid password. Check and try again, please." data-whm="OOPS, these decrypted content may changed, but you can still have a look."> <script id="hbeData" type="hbeData" data-hmacdigest="a639c100e59a6152cf17f7143698778ed7b4c81623282fa14a3a16f7a81cd308">98b8a949c3f5e1d69458982c9c25fd5a277e65ba1cda03eb6d2ff14892b14fd18dfc2abea7d0dda28454dd441dfcfddf5256ea3ef416f3008451c8ec4c947f50581d8adf8a746cad45debbef0d23ccb525a4257da90ff423f8ca55ce004ca272b74a907fe0ff56e7528111a01a18638d4d83151023295ba2a119975b827cc19ac0bc0848395477022d70d26ac953938d1533b93399aaaa586da713023b7b2b9bcd360cd6723f66b6505843d232111e629554b5730f71cbead394b1a4a2e2ac0b4635f8c31e557bcbe3615ebd7659557debc984516eece166fa2bb07e6096b0424c8e543f1b340c98c20a46f00e12d570b47f3cb47c5a297be2e7c5972127b16d08653009db4bc3c1257c4dc59d60b0072a6fe996f410fd5c002abd1736b32fab0afc00981d40fe487ebc25e0b98c80d078603dda4d2d6bb38742a37cb8e23f706c0cfc7d432bc51409a7a0c886cc431fbdfb3656fe46d9050d4e3994854dcfe5d588f2f97ad74e90b1b7a923f3be92cd81f9903101995e557fcd1b509ef4ec6e4c1a80cf9c2107d244039328b27a46bc8b323ff66049e09bdcac86466fa26e2f86929e2553b5fa5cba6d7efdd0264945c216672e7e6b66bbf7e3e4896bfb31224514935a6a7431cfd7bb00ed7b11bc116202520d192c85cf5d842cc8916ee3b4e5945146e7138d29debddaab33ebe506905103eadc7ccdc104217abbab3a2263a8c8af83f3bdc97128c4ba30b0dae7d7dc849f4978751e8fb1fa4aea07c18f472369c7a1cd16185d09e0a8b0070b381a8cca5c4cac2f257056cd89b3a05dfd0814f8d3e9e7e33331941ce42511d6ef021837037d7ee56388771b9ac46dca33d117f41cc1f4463e9ad718c6cdd72d1ad9d9731fd0c64c888121126104f5a68ba9b9909e1e685745dd39e3989f4c4323576fa2d9ba6f041391b62fb2dda65b674193a9caddad6bb36be5f32ea2a0b295792ce6e88e71448536ca06ae6231e4003f0265bbc97fa58c7d3be1440b73d20aedf631ce798abfa145562c9419b9f5354f4dcc9a2c8313bf17a0e2055f76cd5bec1b393a14ec04db0a52a96eca4645d3d230322dc0b60818c10ea1dcbdb1de4d599fb7310a880523f773dff70669b4c252f1da1880f8a2fd18eaaea212e3c061713893d77e04abae033d6cc45c207be76cf70aafb4651ab5bf11b898cae922d71e240874d95af9b4b3fcdb4ebc71e0c388fb49c785d244065bf381b9a58cc5b7fb81e97663c20d9a233f28c9f14fb5e6d3d6e38b3f74991565dfcbd9068fc8ee8f2ea120e28c8fce730a6297c8c34fb0100ff587902adacdf9d3fff4dbac341dafa0fdcb8f8245def639f0403e1c95dd7cedfdd3d83148eed6a719bab33bb668e25a3b2c5488d62aa8d60a0ebc91698024f97d5a66a1896111f5b4eec3c87cbc3ea45c50923dc4329d567fc10a16dc5c79c3ed9b036afab408067e0dab6245f91e3c667100c6654779a5fcf13dd09944be78522ee20396bba575a8c81f2bb65d2812bddfc46b7bea2af5329f749a4f21b3bbbe182b12324104605d4c8eaa0f69fe33b3d0687bf1893a06e3bd28ba092f808ebb484a89d1fe78cbb2b335b8c41ef7db341c8ac715cab721ab37ce32b587f5385f9b930ac9e27133d5fed5c49067f9a2d6e6d1dc46994f20c26aff1d0fe82852f0555da8d972c404b9f2b0e802bf548a6e126ca7abad02eb22efb13c2d9e4a2ce26c2d8296fe3ee64165ded9fbf2aba4c35ee79beaf732532427f9ec103db9c5e8aca5e9b25066766020702c00d83f7c2a2e489f3f3f52b4191a7a7d3a91a15643774fe89bd8e6db78dfadcb405cfe51b48be0323aba941a9a49113111a63bcfa81a3aac36270e9e9ce8ad76fd8e7778fe422254fe122e9f8398dd030b9056b92f68208456021f727a0fd6c2bc885205527c71d0b06e24a594c146bdcace19c280d5f167020f0729f847ff474ec573794ffc682429cf8a8c32ed20f3e26ebc4a8545a0472eff5de61bbfd699b38facf320f079fae4445b5ba7504489d9f0f6ad272cc00f1ab1a8fc9655c6d278eb58bccf458bc66adf8867a64e22e90880f08ed400f8d7c17e6b694d027eecd081dd9a95c706006010ea64e6f702ad82f38e487c0ce6fe94a94342e712e1908fe575615f457f938ceae07d2401080d34e14a8a86e9485f94ab86920bc4586f83a2321d6ebe66f967c2fb66d5c70cd811aa2adda4ee0a78fde29c0cb62651528eb1667ff21cbb31e2aa0540fd5fcc6e2e16210e3899e776847505870652b414ba7c39120322eab9bbc813ef5b1a7fdaa5b166cd8417fe99f525dbe0226e5775fb6a7ee3924d02cbdd25976cf16fd2d6ec512b3d1266b3c415664174487a8fcd149b34ff6a301b30b59bff9e1ce9d12bc15716060bd9e0a462ab26f93419cb7e67f3f3627d6e134bb879c3eec1a567dd3a05faf991404a176f5bfc2fee949ea2e5c661dba1762d8b388272630c47f052a2049d27163ca5644c9e34c44cbd1930760f54dba94d30e40c6a324ca032788125a952b2ca3e015bbee2f1697d97f7d1a48462bcdcc51b0563b502155fc14de5d2f72e2a03bf4cccffb734f067ec14c37f5831a04cc401adef709da660b8a06d1563fbdd87f849d0784afcaa7817856cd504d6934d1d3affeee40b405ca4dd8527584fea0733ef1c7468fb4e7b12e2674e6acc31f115b47ed03fb2b80566e652507d35f2e1db77704b33821e7caa952a6d8973417113ac1cc625eb96e4310a7f512796c4d4a9a06d579a3026353dab195c98c55302714cd3d63f1a067cb8b8cf53946a1ac1de06f8310c382672c8656f95b4d91efa54ec62ccae180289f2e3071c3106447ed5e463fab2d6d767503f36e45f53401acc6b0dd9c591314d7ab2aafbeb26e3e87b7c187a27e8ef1910853c747ccebad2875a12ab831f21d6ecd9edce1b7ef6de136353ed77986df45e1234f0e0f25e61f0723259493ba08ef477c1a80fa2b00ceac9e8788021e171fcb112bc40a2dd0a9f60a1ddce482349225ecb53b46d5723332d2bc934c483c30af2b2facce2acbb55d6a868568a178ff8d855dbbe3321d7667a0525e6ec80cd56fa196cba9a17f34989bc66610c9dc94079bbcbf45b58b7fc7bc650a30e1187edabec79ff596ae459b34016470e3d9a0881e5f12e279bc02f39730cb6b00ebb14622f006efd63269d03d44c8c4c7974aa30aa3a28b2fab6d6bcc88a3ea891cdfd071aa3ca99a0d5d69b70b9016562d5378f142ed07a086dee2ab3a0dc22c48d4b3d0a3df51a5c6fd54e024a72349b7478ffb93fb4efd01a1650eef8531533b51daba00752411c2df1b1bfe874f7499fd82ada82849fa5cf21e34e333b59e6d8b2d73f45c8fc15abdcff54fd894fb6f6cb71b360b2582fdf231ec24b2f1a6d4b513f76c7ffe56b0f6adeb095946c3011382f7f26c63c00e8a0de5a57e4e9db0341aecfa64e782de8c865878b26bce19517eb4caf19c92f5bcfedbb5f0d83abcedd657c5a2eb1edd0bc00a66afaf7f742de1d10663aab64d87351d29fd6dcc9619e8c23410e72570b413a6652395795ba70684b03a7945ca2191f428b73b2a81883cad269c321ae6e7d16200748fe3a814e54ed0bcac8b900e75ec9fc4d16a37cdcd38b61bbf823964dd590da92f043e444e56eb09e4c786e77ec57c390c80d1fc0d98875b7858e5bf6075b965f720495aaf147ca97dd70987d5e7f1944388548a317ca562a57fa4836305e341a0290c791af398afe160cf1941775658ea653ef506ebaf391249c44e8d6e60eb1b7454aef378f510f62a234d97dea7bb5f23127f3aaff9d54613e4cb6f9ec228923d3226242ea096f32bb88b70b68b0387111f49db2c2647ee2bdbcc74edd8ad1e1bfb6d6e9af3e58bdaa9bfbc0840c9036d9c3228875a0e85a6bd53fa23466acc580cccfa0c7d5ceba672d231e987df6b539703ee2db348d8d9b4d31120341a5540108e125b2e4d5709770757174d08b7bf64cceda2897bc3c70cac7affba939ce1195ce3e12703bff54ae8d5ecb8e2013ca4e33d262fcb4ebe6e561257dec64408b194303d4106f3af4e5f9a50436bee606935f6a0d17449536720b625c4668e251ed18418919829d4d3f893a49c26ed00ee2ada578b399846329ea143235293ece9d94e4924d047fd3e04d23a894dfe3df9196bbb59ef4a8c8888e9ce8c08c1acce553a61dd75af230bec4b3c751cf75f304da1df356d565f092c3ebe31cf5c23eaa05c4151e316a0b85c5c5483e9f783b5b4812858a66f57b9911330876f75a585edc6615ddffed7de51a9903ff080210e538560bbbec7098138760af90166ff14eb1b47b5493c524a11b1d96c9522f14d2456a824edaffd1e5cc351ec4802b037b8a70890723e3ace88a040e6478fb9d9354052336e46ccba1dec475932816eddded95a7eb1f652667542d2996826a481424acaa26b82e5d82da63afefb4c1d71cefb3eb67e73fe9d1d67e0190eb1625eb8c01de4a0d6cb35ce10aba4a5fb0facb308d772bd34830233c307b01c4bd65a706eac26f793d4691ae58826e131b6714eb6e7dd7beabba90f13689d531e03bd5b909d745ebb133b1a22275481eb43265e7f2ad1af0837efb3ac09655159e3762d1f3b2aa47ab05624c48970c8cb068429a95970f537f20843ea29ca2fef63907ee20e2e2c40b3fb003f555802721e8cbfe5afb6a6d71aa8f5bf42a2af81d0295c48b701a85f1f1fc3313bda07cca7551a17893419527eed6db33a4ae153d5dedcce31986dc020cb8cd60f5225174bf966183de6aa0f7c39d8c33e1705ac5b8db1b976af6df34365039b38ce959fffcbfc9c5c8ef3e2a7386646610f419128188cf90707178bf1615cef1b00ecad2a5f9d6cdaf8e92e3a63c646cf69f99202dedb60cd75df52aa874ece68dc49c0668d64878a55a3de6b39a213442e6bd766876b5fad4306a682d15e51b1f5c1c366a7cb8dd48d1d5c63565b86ba79551a23a59948874b95d7c48005206c2501b4d2c4b8f88b0bbe664c03e09a128737db348bdcc5f8e5a28f21948907d3a49a9c5b7d8f69211ce2a81c8c036431cabc954940944f9609cc2de0b05c1eb5129c3564226ba81bfe9fd627e7f611ba451956f3ad05f9291e81bba015baf72708ecc1114d738a37853bdf5c3595a14bbc97258f7125aa04eb0226b7febf5c94e0fc68dc09eace80130cc77168209eec4c71f07c8f57473a1cbde29619715de8401c668f626f773160440898243e8dd63923f7b8e7af04715990164e8f1d8a5f965281677af6350cb3e843d1c022b73387e735ba3ae2618351f96161b7591248a5c97f84fad91f9467780daf07c45eecc098e7929f2ff162848fb4022a79a5d4a39db2c76164a0185aed837857257738ea8714828c6688d85632dd9837d0e1ac904da4d5d74dd2e737ebe1fb835416467ed732bdbd8cea907a0aea3a32e1001da086fb411a866b0bf2815a7d96899dbf802191fbe4be1de30babc3b4f8cce2d72d64e89ef69385c8264e35d9712dabeaeb7f40e71e79ec2a281684bc6abc9007887ea1799850426d2d6eacd01f12b0f6ebb601cf50e95e717aad0d70976bf28dd9251c0c5154beb417245983f38a2c61bf57a3e9d351879e70a6383438f151eb078cf22c71b03baf7db9cc5ffe3c34721fb9aa186d7762ffd48b9b17b3680e0aa8d482bb6b6978ca0fe183579b7b3ef73d010a529243d1decfe59c442efcfc023a8f0ef426fc011bf13e9035a24266f6da832f98adb9cecc7ba76843b048b30a39963a10a83743bc4a8e630276be5d09b7dcb3d0b2822bc37ff683b50a3f5d13d0e49063e5ff9038bb44378efd6d9f77d80ea9cfedab0489c793550de7f480acfffba9983af00f2e774a6be5f2bae994ea9f8f1ed0fe345af9e4f68e3cd70725348668047c8173659a43bdd1faacbc471f52d3959d8cd299a89ef21e2e3ab474ad442f4952e158844921c7d81b041a703d4f6d92c64b3087971a588b16e40ba15ce2fc6e7d46e91b3dd9061637250b2713ba41e36aeda7697573fd729f9698eef4f7bbc2d73374ad8da64aa27a925b58236281a1bb99e038f1621bd9f8443585d16f1cd2b035c6af8b70c650518da5906b86afd5b402d62e71b0d37f0d5e2fa9f27f8094f070b1aef79948bea47c5c88e20dd01a41b7ce76df171cc655791794948e868fd9abb7f2ce0030fb467ae810c2c345863fa0fbf6ab3167a03370d1b4c3a689fc81e9b33840281bcf8884af3cf379ba9fc3c7d8268bbe3438301aae439fb503814b801b929542b739d3312c095109d0254ea10f7e363004e736d4f8d89ab07d048b26e9b297b838ade71877fcbb90991ee3b820de8a374edb520af7e30f97a8cacd6bf1c196c32f242b504122d2ce75c78ebd265cf77cb631ea09d9b83b6922dab5549462b9481343abc8e57ed28baa7b22a3e583e280b992e52f3a40cd5040221597f33ac7ca3ab91f9064c7cea9aae3051ff005ef9ba9c92866de7d24419c1ae10de8c61bca8e80793230be7f40c972e3f1029cea465bd32c67a76f49ac6f44e7b72e4aee529bab0003a0d492abff3be61463e5cba22536558a0db01f180c787a63de28d920f0ee7de795a564b0e9c9a2d2ae9f3e5da36c7d9f0bfa884b1dd024cce9e9497b69bcfb5a69f3a6f773d5d3f83cf61f2b501296f488f3fadc890fdc17207a41880a5b2d4000b9793b88593ab6d2cf0dce2bcba6a33014483e416c90751ceb0388440102dbfb1e4244b91a70f13a28874fd42b28ab70124987557e292a54beb0755f105fd91ded78987a3af2a2d82bbe96f28fc914d219027555829469dc0249e2fa74d5e113f5e1c4fe0b057a2a7278a29c5fed2eea2c6157e7405f2169b9f8810044c814502de9d8740386ffe649278a55feaf81bbae6d19004b284980591871f816c105bf7bbc699cc749a4a0fd659bdff6e42e076f398af4718a82c6bec21ffaf016d1b5b1603e5edafce2e921bd91c14cadfb60c0c92bc7dfa3b495ee280e654a55dd89c8c320d5fb67d456002a7d233e916c15104455227d13a4d0af61f1e2f34f323b63a0ee72d3fba427aae19f46d19732375b113feddbaf7cc2e58da8b9df046bbc71ee0585add1a5e92332f9c26653af9f35622712327c37b3b492a2b2222f67d79bb73ea863f078b88ac206efcaf02aeb0d5b17563162a395c2d603bdc84ba843936c7d167d1069dc26a52f6fb989b64acdc3764635bcf92bef379a0d1bf097337a9ea8183fec05ebaf8c78b60f8beb1643883942574f4c6c8c41140734692f83b4a73601405ed30154126bf785acaf0f8c18d7867ff37ddc8005492ab05cc86671ff7ed129b8bc912ddeb0eb1e6683a1a75f28297276ff2769ece81592e91c1f61eec801eade17ec3ff13db390b737a76c363045cc8317bd79eed503f003e437dea69fc2db39b0c001290b90afab13460ce98a9883ea54b53b9d7a5881f2af028d2fa3e646283002278044e54a44d1202538ef08c18a075cc7f2b5b05b4022b2edf22c7be01e3664a5fd00b6d2c7c90f06648f8ae2b9acd1e51ac22035541d48e9c155038cda6006a0d8a872c20e0259a7cd819f665051a53398c93845668e90b77072228cbbcaa63f33e159f69c12b64218fb1d151d67db47620d7a062f396bd43724aeb7ad940c7135c7e4b25584d3b44103ea75710ebe007b6ce996d3eb8ef9f23292395e2b4776efb1780c62fbb079612a3096211bea0ac99113fe50e26738db59c1af3b89769089fe9c61b8be06707d3aac46bb50118c5ad8a27b78f48fb1fbf980501215f357e8bdae43004917be829db72af2c562abf707bf777d6797347c7fe9ad176696d91157bdf743fc30e976970609f0525ad2a763f5c929c0395d4a55cb443040845bb525430413edca528fea2806b091851e7b60a4c25b7e73915e13fbc06a1a3a5b4e539ef7db686d4cc3e5c6a745571776a81a8d3b97c92b6e1bf83b6f99403630c588812d40ca35d17453703493cdda1227945e0b9e6e0d0702033ade92e1139d99596fb5a230d2055c65770bd0957ee5c58c8dbdbbb0623553907b50878808f7273c023d754b4d830b2d42f5481f665ce202eaa5934f93e443d2ea4c17ea5e91afd348caafc8fd22c2f12e16e60e483688dbb04c10051823fab049cc2f8da194e39de0cf9957a990fb0858000b452e6192a71e1314dca0cf373faf673336615864159c5d908acfb4a1e178f3d74a71a2edf4811cd1c73e6c8c4901231790391e3e3fac17a7fae830082b050db56a6ab7e2a2eed2fa987cc4007e2df7a4a76f2e43070fea66cf09773bda8dd2f4eda03ce0c3f1a06686df1ccc1342b8706071ec361f5d157c37b1346641a7ee9f55b7d4be46f75c1114ba35d2b7ab27db7b9b980c0dd63b4e3553b55dc4f82cc71faf9a881b4ec04392db94ab4a555304a715ff03add3963ff698e3b76d2c6186aa70ad96886cb24193c84db408e384ca00e24e0987a855751baba6534e97ba245fde3014a81692fc167d45f73555eba58c5a6c0649376843c2ee5281d99e941ec43caf39520664da52c1af1f5532301435a71e7cadb590cbefeb2bbd1c65b6ee8340be93745127e1c47457ee7a608dc56f22cdfeac6f6985d4e420b16765308ed0879258ff5ef80719bc3f7139f3a9bc54e9e25dd1db5903675a4426f934c618ead760332eacf97e56fda706dbe3d782113901c1b66497a5405f39409af7cd3c5a8e07e1376bc41dcaeaab89fec7807078f901a8026d1eb4979e3588ffefbf8100602772276231c71ccb50edafd6de2ad2942d6a0fe14d17b676be25deec30858f0a25fadf8950abe6f15169dbe73bbaea411cbe575b052b94c73654369413fa9674675d466d76ba900c9f6d2c8559290c4d475f22b0c7178894077e79416e2c47a240bb90054f471111632609ac2a7ddf045c1a52e0c848ab9f72b3bedf24088d9ce82fdba83befc8d147ef304feb6811b1d61c26a3d3a4f64ca83601c3dff17d2342af07131841e5e2383da5a65d45cb2ea68284c9b3eca23e1e07fc61f9af4e80398b2f7e0f13bfad2fbfaa2b26854044b84ea1bf514a524f7092f45df38b33f6e85022079360df1c2724542dfe06e0bcf4d9d2ab5aca28b87097a11146e6eb3077e5b0bda32ebd76db72bcca1acbcb18d8b7160ffe724398657a9ca5046834e55ef6c5a68e76dad029c6dda8f713197b884d14c1f237ec4e5fb882e7f674d1cee2bc8fb92fb3ef7463b8d95929bd723faecec32be19f7affc294ecadf90caedf05db7876f0287815133d0e88d58dbba780951bd37b1ff99d5985ea6ef115104c900f048d8da6beb3f969001b89c2bda2216fb3b8289791ed6f6aa874bf239d8ab6383bee9e3e2d2367140b6ef683b1f47e48cfa4c93f313a279194939f17896208ae93d37d3e66b6f12cea8aae7a6d1fa5c0d212e573de19ae68e33ac38edcb9378e761a97c974849dd9f33a104a4415980d64c0ed7951e5a68ed839f629916235752166e90cc1dc7d484bab5b1c190509e566817257f6180fb9669680a357a122c78ffb9191ba2e8e17f6dacdca34b5b642d4bfb052c70b64cb7220154f8bd12ba8fc25aa3eb34a14648f4b69b0f16cab1a1b96b50f079bfb7e985a4de3791825e3cd449aacb419c63413d1ec6f369f1454e6120f90866ae2daf31356ff2ce17ad46a358524f3e23a423f3f578a9e2264cce0ec9d0935407c3d0e073612c332d04fb4bd9430ef2dc388436db57149f0fc389463e8e0d4d1033884daa1bfa21b8ceb161b409634a9b1d1a6ed30ac893c71d455f8363e44703cf81f970c2c40bf08a643a6dab1f902a3c1a104a3777be613db15b8e5bbe29617d644f665a8b6a2d77bdefbfbc0dbc705c2ea4f8bff9a10d071f495c</script> <div class="hbe hbe-content"> <div class="hbe hbe-input hbe-input-default"> <input class="hbe hbe-input-field hbe-input-field-default" type="password" id="hbePass"> <label class="hbe hbe-input-label hbe-input-label-default" for="hbePass"> <span class="hbe hbe-input-label-content hbe-input-label-content-default">Hey, password is required here.</span> </label> </div> </div></div><script data-pjax src="/lib/hbe.js"></script><link href="/css/hbe.style.css" rel="stylesheet" type="text/css">]]></content>
<categories>
<category> 3D Engine </category>
<category> 3D Engine </category>
</categories>
<tags>
<tag> Python </tag>
<tag> C++ </tag>
<tag> OpenGL </tag>
</tags>
</entry>
<entry>
<title>【工具】LaTeX教程(附模板)</title>
<link href="/posts/2TXFVDF/"/>
<url>/posts/2TXFVDF/</url>
<content type="html"><![CDATA[<p>Latex 对于学术党的作用是写论文,对于工作党的作用,那便是写简历了!<br>github 上有非常多的优秀简历 latex 模板,可以自己去搜,改起来非常容易。<br>本文简要记录 <strong>Latex 的安装、开发和发布流程</strong>。<br>github resume: <a href="https://github.com/593413198/Resume">https://github.com/593413198/Resume</a></p><h3 id="安装">安装</h3><p>只讨论 Windows 环境下的安装使用,Linux 实在很难搞定中文字体(你都没好用的图形界面用个der的Latex啊…)</p><p>下载下面的安装包,直接一路点到底(就像你小时候装盗版单机游戏那样…)</p><ul><li><a href="https://www.tug.org/texlive/windows.html#install">latex easy install</a></li></ul><h3 id="开发">开发</h3><p>Latex 每次编辑,依赖于<strong>重新编译</strong>生成可视化文件(如pdf),这是非常影响开发效率的(对比markdown)。<br>聪明的 VSCode 爱好者没有错过这样的机会,勤劳的他们开发出一款 <code>LaTeX Workshop</code> 的插件,真正做到了 <strong>所见即所得</strong>。</p><ul><li><a href="https://marketplace.visualstudio.com/items?itemName=James-Yu.latex-workshop">LaTeX Workshop</a></li></ul><p>使用方式,在VSCode市场安装该插件后,将如下配置写入到 <code>setting.json</code>:</p><pre><code>{ "latex-workshop.synctex.afterBuild.enabled": true, "latex-workshop.latex.autoBuild.run": "onSave", "latex-workshop.view.pdf.viewer": "tab",}</code></pre><p>此时每次修改完 latex 文件,按下 <code>ctrl + s</code> 保存键后,VSCode会<strong>自动编译生成</strong>同名的pdf文件。<br>接着使用 <code>LaTeX Workshop: View LaTeX PDF file</code> 新开一个窗口预览 pdf 的实时修改即可!</p><h3 id="发布">发布</h3><ul><li><code>pdflatex ${filename}</code><br>其中 <code>$filename</code> 是 tex 的文件名(不需要输入后缀)</li></ul><h3 id="模板">模板</h3><p>附github上一些优秀的简历模板:</p><ul><li>中文简历:<a href="https://github.com/dyweb/awesome-resume-for-chinese">Awesome Resume for Chinese</a></li><li>英文简历:<a href="https://github.com/posquit0/Awesome-CV">Awesome-CV</a></li></ul>]]></content>
<categories>
<category> 工具 </category>
<category> 工具 </category>
</categories>
<tags>
<tag> linux </tag>
<tag> markdown </tag>
</tags>
</entry>
<entry>
<title>【SIGGRAPH23】Large Scale Terrain Rendering in Call of Duty</title>
<link href="/posts/siggraph-23-terrain-of-cod/"/>
<url>/posts/siggraph-23-terrain-of-cod/</url>
<content type="html"><![CDATA[<p><img src="../../images/siggraph23-cod-ppt-demo.png" alt=""></p><div class="admonition note"><p class="admonition-title">导读</p><ul><li>这篇 SIGGRAPH 主要描述 COD 中的大规模地形渲染,并大量引用了之前GDC的技术分享</li><li>NETEASE WARNING: 已脱敏、与工作无关</li></ul></div><h3 id="技术参考">技术参考</h3><ul><li><a href="https://advances.realtimerendering.com/s2023/index.html#CODTerrain">SIGGRAPH 2023: Large Scale Terrain Rendering in Call of Duty</a></li><li><a href="https://research.activision.com/publications/2021/09/boots-on-the-ground--the-terrain-of-call-of-duty">GDC 2021: The Terrain of CoD</a></li><li><a href="https://www.gdcvault.com/play/1025480/Terrain-Rendering-in-Far-Cry">GDC 2018: Terrain Rendering in Far Cry 5</a></li><li><a href="https://www.gdcvault.com/play/1021761/Adaptive-Virtual-Texture-Rendering-in">GDC 2015: Adaptive Virtual Texture Rendering in Far Cry 4</a></li></ul><h3 id="大纲">大纲</h3><ul><li><a href="#why-new-terrain">Why New Terrain</a></li><li><a href="#render-process">Render Process</a></li><li><a href="#virtual-texture">Virtual Texture</a><ul><li><a href="#adaptive-virtual-texture">Adaptive Virtual Texture</a></li></ul></li><li><a href="#cliff-shading">Cliff Shading</a><ul><li><a href="#%E5%8E%9F%E5%9B%A0">原因</a></li><li><a href="#tri-planar-mapping">Tri-Planar Mapping</a></li><li><a href="#%E9%97%AE%E9%A2%98">问题</a></li></ul></li><li><a href="#stiching-fix">Stiching Fix</a><ul><li><a href="#1-lod%E4%B9%8B%E9%97%B4%E7%BC%9D%E9%9A%99">1. LOD之间缝隙</a></li><li><a href="#2-%E4%B8%8E%E6%A0%91%E6%9C%A8%E7%9F%B3%E5%A4%B4%E4%B9%8B%E9%97%B4%E7%9A%84%E7%BC%9D%E9%9A%99">2. 与树木、石头之间的缝隙</a></li></ul></li><li><a href="#%E6%80%BB%E7%BB%93%E5%B1%95%E6%9C%9B">总结、展望</a></li></ul><p>TODO:</p><ul><li>Displacement Decal</li><li>One Material Per Vertex (OMPV)</li></ul><h2 id="Why-New-Terrain">Why New Terrain</h2><p>作者阐述,之所需要开发新的地形系统,是因为传统的地形系统无法满足 现有的游戏需求(即策划不满意)。而传统地形主要有如下两个特征:</p><ol><li>地形范围小(如室内地面装饰)</li><li>依赖手动编辑(如魔兽争霸地图)</li></ol><p>作为对比,新的地形系统提出如下三个要求和挑战:</p><ol><li>Lerge-Scale(水平 + 垂直两个维度) -> 解决性能问题 runtime</li><li>画面多样性、细节保证 -> 解决表现问题 bugfix, tradeoff</li><li>依赖程序化生产 -> 解决工具问题 PCG</li></ol><p><img src="../../images/siggraph23-cod-new-terrain.png" alt=""></p><h2 id="Render-Process">Render Process</h2><p>作者展示如何在forward管线下绘制一个朴素的地形:</p><ul><li><p><strong>VS Input</strong><br>Vertex Shader 的输出就是一些平铺状的mesh顶点,它是没有任何高度信息的<br><img src="../../images/siggraph23-cod-vs-input.png" alt=""></p></li><li><p><strong>VS Offset</strong><br>这一步通过采样 Height Map 的高度信息,对 VS 顶点作出一些偏移,从而模拟出地形高度的形状<br><img src="../../images/siggraph23-cod-vs-offset.png" alt=""></p></li><li><p><strong>PS Shading</strong><br>Pixel Shader 阶段采样各种 Diffuse、Normal 贴图进行着色计算,为了弥补地形的细节,美术会叠加多层的 Layers 进行混合。<br>通常还会引入底层API支持的 <code>Texture Array</code> 技术进行优化<br><img src="../../images/siggraph23-cod-ps.png" alt=""></p></li><li><p><strong>Quad - Tree</strong><br>因为地形只考虑平面结构,所以想到利用四叉树进行遍历、剔除的优化。<br>以COD的地形为例,<code>10km x 10km</code>的总规模,<code>64m x 64m</code>的单个地块尺寸,所以约有2w多个地块,即使经过视锥剔除等优化手段,还是有成百上千个Chunks需要渲染,因此<strong>性能压力非常大</strong>!<br><img src="../../images/siggraph23-cod-quard-tree.png" alt=""></p></li></ul><h2 id="Virtual-Texture">Virtual Texture</h2><p><code>Virtual Texture</code> 是 GDC-2015 提出的一个技术,在弄懂改技术之前,先了解它在尝试解决什么问题。</p><p>继续上一节的结论,当开发大世界游戏逐渐兴起,每帧需要渲染的 Chunks 数量急剧增长,每个 Chunks 都拥有自己的贴图,<strong>这无疑给硬件内存带来巨大的挑战。</strong> 但是如何解决呢?</p><p>参考 <a href="/posts/virtual-memory/"><strong><code>Virtual Memory</code></strong></a> 的提出(即物理内存无法满足需求时,计算机抽象出了虚拟内存这样的中间层),计算机科学有一条非常重要的公理:<strong>即软件(或硬件)层面无法解决的问题,往往可以引入一个中间层。</strong></p><p>因此 <code>Virtal Texture</code> 基于此思想,它将所有贴图试做 内存 意义上的贴图,不会一次性加载到显存,而是使用的时候才会去加载。参考 GDC 的图片:</p><ul><li>Virutal Texture:内存(磁盘)意义上的贴图</li><li>Indirection Texture:寻址结构,类似与虚拟内存中的 页表</li><li>Physical Texture:实际采样的贴图</li></ul><p><img src="../../images/siggraph23-cod-vt.png" alt=""></p><hr><p>VT 建议单独列一篇文章讲解,可惜缺少实战落地经验,这里先贴一些有价值的参考链接:</p><ul><li><a href="https://zhuanlan.zhihu.com/p/138484024">浅谈Virtual Texture - 知乎</a></li><li><a href="https://zhuanlan.zhihu.com/p/143709152">游戏引擎随笔 0x14:UE4 Runtime Virtual Texture 实现机制及源码解析 - 知乎</a></li></ul><hr><h3 id="Adaptive-Virtual-Texture">Adaptive Virtual Texture</h3><p>VT 技术有一个非常明显的缺陷:<strong>不论 Chunk 距离相机远近如何,但在 VT 中的像素比重却相同</strong>。</p><p>基于这点,AVT 提出了一种基于相机距离的 VT优化技术。其中红色框表示距离相机较近的 Chunk(像素也较高即 64k x 64k),而绿色框距离相机较远(像素分辨率也很低 16k x 16k)</p><p><img src="../../images/siggraph23-cod-avt.png" alt=""></p><h2 id="Cliff-Shading">Cliff Shading</h2><p>COD游戏中有大量山坡和悬崖的渲染(参考PUBG),因此开发人员在 GDC-18 中花费大量笔墨讲述了 <code>Cliff Shading</code>,Cliff又翻译成 <strong>悬崖、峭壁</strong>。</p><p>先看看 Cliff Shading 首要解决的是什么问题,当 Chunk 的贴图使用在平坦地面时,表现是完美。但因为山坡峭壁的y轴是非常陡峭的,因此会出现严重的拉伸 tiling 现象:</p><p><img src="../../images/siggraph23-cod-cliff-bug.png" alt=""></p><h3 id="原因">原因</h3><p>将世界坐标 (x, y, z) 映射到 贴图空间 (u, v),首先考虑如下几种朴素的方式(即选取任意两个轴采样):<br><img src="../../images/siggraph23-cod-cliff-xy.png" alt=""></p><p>因为丢失了某一个维度的信息,因此效果是不尽满意的。</p><h3 id="Tri-Planar-Mapping">Tri-Planar Mapping</h3><p>COD团队首先尝试了业界著名的 Tri-Planar Mapping 方案,即分别从 x, y, z 三个方向投影得到映射效果,然后根据法线与三个轴的夹角关系,将三个结果作融合得到最终的效果:</p><p><img src="../../images/siggraph23-cod-cliff-triplanar.png" alt=""></p><p><img src="../../images/siggraph23-cod-cliff-shader.png" alt=""></p><p>关于 <code>Tri-Planar Mapping</code> 参考阅读:</p><ul><li><a href="https://catlikecoding.com/unity/tutorials/advanced-rendering/triplanar-mapping/">Triplanar Mapping</a></li><li><a href="https://gamedevelopment.tutsplus.com/use-tri-planar-texture-mapping-for-better-terrain--gamedev-13821a">Use Tri-Planar Texture Mapping for Better Terrain</a></li></ul><h3 id="问题">问题</h3><p>采用 <code>Tri-Planar Mapping</code> 方案后有明显的两个问题:</p><ol><li>性能非常差(采样数 x3)</li><li>远处 Texture Tiling 非常严重<br><img src="../../images/siggraph23-cod-cliff-tiling.png" alt=""></li></ol><p>性能问题先不考虑(因为是PC端游戏),开发团队后面主要描述如何解决山坡上的Tiling问题。<br>关于如何消除重复,最简单的方案就是<strong>引入随机数</strong>,这里采用了 Nividia 发表的一篇论文中的噪声函数,最终的做法是将 正常的Blending值 + 噪声值,</p><p><img src="../../images/siggraph23-cod-cliff-random.png" alt=""></p><p>观察黑色框中放大的部分,可以看到明显的噪声值,但是整体的效果反而更好的。引用GDC中非常经典的一句话:<strong>“单个像素是错误的,但是放到整体(平均下来)又是正确的”</strong></p><p><img src="../../images/siggraph23-cod-cliff-improve.png" alt=""></p><h2 id="Stiching-Fix">Stiching Fix</h2><p>新的地形系统采用将不同 Chunk 拼接渲染的方式,那么是否会导致渲染的缝隙、不连贯呢?<br>答案是:会的。</p><p><code>Stiching Fix</code> 重点讲述了COD如何修复地形渲染的一些缝隙,而 <code>Stiching</code> 的中文翻译便是缝隙。</p><h3 id="1-LOD之间缝隙">1. LOD之间缝隙</h3><p>如下图,不同LOD层级之间的 mesh data 差异较大,会出现同一个顶点在两边的信息不共享,这在渲染时会出现明显的错误。<strong>(具体是什么错误?)</strong><br><img src="../../images/siggraph23-cod-stiching-1.png" alt=""></p><p>修复的方式比较朴素,即 <strong>将边缘的顶点,移动到另一个lod最近的顶点</strong></p><ul><li><p>跨一层LOD的情形<br><img src="../../images/siggraph23-cod-stiching-2.png" alt=""></p></li><li><p>跨两层LOD的情形<br><img src="../../images/siggraph23-cod-stiching-3.png" alt=""></p></li></ul><h3 id="2-与树木、石头之间的缝隙">2. 与树木、石头之间的缝隙</h3><p>主要是做一个贴地处理,即根据 Chunk HeightMap 的高度信息,对树木、石头的高度在 Vertex Shader 中做一个高度插值处理,从而实现将它们 <strong>贴在地表</strong></p><p><img src="../../images/siggraph23-cod-stiching-4.png" alt=""></p><h2 id="总结、展望">总结、展望</h2><ul><li>地形系统主要细分两个方面:<ol><li>卷细节: pcg, texture layers …</li><li>卷性能: avt,gpu pipeline …</li></ol></li><li>从 GDC 2015、2018、2021 再到 Siggraph 的集大成者,任何一项技术的发展都需要持续迭代、集思广益</li><li>了解一个技术,和实现一个技术,两者相差 1~2 个数量级</li></ul>]]></content>
<categories>
<category> Graphics </category>
<category> Graphics </category>
</categories>
</entry>
<entry>
<title>csv, hdf5, feather 数据性能对比</title>
<link href="/posts/data-perf/"/>
<url>/posts/data-perf/</url>
<content type="html"><![CDATA[<div class="admonition note"><p class="admonition-title">导读</p><ul><li><a href="/posts/quant-data/">这篇blog</a> 介绍了金融相关的数据特性,它对于读写和存储性能有极高要求</li><li>本篇blog 会结合跑测数据,分析三种格式的性能</li></ul></div><h2 id="测试结论">测试结论</h2><p>结论放在最前面,测试数据见 <a href="#%E6%80%A7%E8%83%BD%E6%B1%87%E6%80%BB">文末</a></p><ul><li>数据量小,无脑使用 csv</li><li>数据量大,如果坚持 csv,请使用 <strong>zip</strong> 参数压缩(尽管这会降低读写速率)</li><li>百M级别以上数据,推荐使用 hdf5(而不是feather)<ul><li>性能强于 csv,<strong>读写快5~10倍</strong></li><li>hdf5 对于 Python/C++ 的API支持较好</li><li>feather 虽然性能更强,但限制更多 TODO</li></ul></li><li>切记! <strong>此结论并不适用于所有类型的数据样本</strong>,最好自己针对性跑测,找到最适合自己数据的格式!<ul><li>对于期货 tick 数据(由于大量重复值),csv.zip 压缩比率能达到 15%,feather 能达到 30%</li></ul></li></ul><h2 id="基本介绍">基本介绍</h2><h3 id="csv"><a href="https://en.wikipedia.org/wiki/Comma-separated_values">csv</a></h3><p><code>csv</code> 全称是 <code>Comma-separated values</code>,<strong>即以逗号分隔的纯文本格式</strong>,常用后缀是 <code>*.csv</code>。<br>正因为其纯文本的性质,常见的编辑器(或者excel)都可以预览csv文件,所以它的优点是 <strong>简单直观</strong>。<br>然而事物都具有两面性,就像 <code>json</code> 存储格式,直观的代价往往是性能的损失。</p><h3 id="hdf5"><a href="https://en.wikipedia.org/wiki/Hierarchical_Data_Format">hdf5</a></h3><p>全称是 <code>Hierarchical Data Format version 5</code>,<strong>即高度层次化的二进制格式</strong>,常用后缀是 <code>*.h5</code>。<br>hdf5格式从设计之初,就是服务于大型数据。</p><h3 id="feather"><a href="https://arrow.apache.org/docs/python/feather.html">feather</a></h3><p>feather 是一种用于存储数据的 <strong>快速地、轻量级的二进制格式</strong>,常用后缀是 <code>*.fea</code>。<br>它早起就是为 Python(Pandas) 和 R 这两种编程语言所设计的。<br>目前广泛使用的其实是 feather v2 版本,它区别于早期的 v1 版本,这个不用过多了解。</p><h2 id="测试标准">测试标准</h2><p>主要从四个维度测量性能:</p><ul><li>1.写入速度</li><li>2.读取速度</li><li>3.磁盘大小</li><li>4.读取内存 (TODO)</li></ul><p>同时考虑到金融数据的存储格式,大多是 <code>int64</code>、<code>float64</code> 和 <code>timestamp</code>,因此也会分别考量 在这三种格式下的性能表现。</p><h3 id="压缩性">压缩性</h3><p>因为写入的csv文件较大,所以考虑 csv 结合各种压缩算法测试(压缩本质是牺牲性能、换取空间)。<br><code>df.to_csv('', compression)</code> 可以传入 <code>zip</code>、<code>gz</code>、<s><code>bz2</code></s> 等等参数。</p><h3 id="benchmark">benchmark</h3><p>使用 800w x 10 的DataFrame数据,取值范围是 0 ~ 16亿,格式是 <code>np.int64</code>。<br>数据大概长这样:</p><pre><code class="language-csv">0 d0 d1 d2 d3 d4 d5 d6 d70 5680658661046001 6886564689964211 777947290120004 6223515736992396 2823728071993317 8416657213663291 3500805963228465 10097481526053971 8952870328278778 306015862731108 9726241400443289 4237512935832667 2875425479333067 6311517969042662 878720088161354 87871182730650332 6858523177136352 1733196075769152 840572662722070 8438133907754012 6671944540650125 1147224095855703 1583106816125259 27980685687731413 2940819554234759 3142545317839947 4641209159206074 8194125756197731 4958881218032026 4405637321734842 500692399773906 35316179424621364 8149680042981168 6793238579260437 3891639455885689 3690167863144449 7552248224604567 6411717840330018 9556078695826276 4851668202438122......[8000000 rows x 9 columns]</code></pre><h3 id="测试源码">测试源码</h3><pre><code class="language-python">class DataPerf(object): """ 数据 性能测试 """ @staticmethod def get_random_datas(rows, cols): """ 随机生成 rows行 x cols列 的DataFrame数据 :param rows: 行 :param cols: 列 """ data = np.random.randint(low=0, high=pow(10, 16), size=(rows, cols), dtype=np.int64) df = pd.DataFrame(data, columns=[f'd{i}' for i in range(cols)]) return df @staticmethod def perf_write_speed(): """ 测试写入速度 """ df = DataPerf.get_random_datas(800 * 10000, 8) PERF_TIME() df.to_hdf('perf.h5', 'data') PERF_TIME('hdf5') df.to_feather('perf.fea') PERF_TIME('fea') df.to_csv('perf.csv') PERF_TIME('csv') df.to_csv('perf.csv.gz', compression='gzip') PERF_TIME('csv (gz)') df.to_csv('perf.csv.zip', compression='zip') PERF_TIME('csv (zip)') @staticmethod def perf_read_speed(): """ 测试读取速度 """ PERF_TIME() d1 = pd.read_csv('perf.csv') PERF_TIME('csv') d2 = pd.read_csv('perf.csv.gz', compression='gzip') PERF_TIME('csv (gz)') d3 = pd.read_csv('perf.csv.zip', compression='zip') PERF_TIME('csv (zip)') d4 = pd.read_hdf('perf.h5') PERF_TIME('hdf5') d5 = pd.read_feather('perf.fea') PERF_TIME('fea')if __name__ == '__main__': dp = DataPerf() dp.perf_write_speed() dp.perf_read_speed()</code></pre><h2 id="测试数据">测试数据</h2><h3 id="1-写入速度">1. 写入速度</h3><p>注意:因为 追加、覆盖 等模式会影响性能,所以重复测试前,记得删除已写入的数据。</p><table><thead><tr><th style="text-align:center">format</th><th style="text-align:center">write time (s)</th></tr></thead><tbody><tr><td style="text-align:center">csv</td><td style="text-align:center">33.5</td></tr><tr><td style="text-align:center">csv (gz)</td><td style="text-align:center">135</td></tr><tr><td style="text-align:center">csv (zip)</td><td style="text-align:center">128</td></tr><tr><td style="text-align:center">hdf5</td><td style="text-align:center">1.8</td></tr><tr><td style="text-align:center">feather 👍</td><td style="text-align:center">1.3</td></tr></tbody></table><h3 id="2-读取速度">2. 读取速度</h3><p>测试接口,全部选择 <code>pandas read_***</code> 系列,会全部转化为 DataFrame 格式。</p><table><thead><tr><th style="text-align:center">format</th><th style="text-align:center">read time (s)</th></tr></thead><tbody><tr><td style="text-align:center">csv</td><td style="text-align:center">10.8</td></tr><tr><td style="text-align:center">csv (gz)</td><td style="text-align:center">15.0</td></tr><tr><td style="text-align:center">csv (zip)</td><td style="text-align:center">14.1</td></tr><tr><td style="text-align:center">hdf5</td><td style="text-align:center">4.2</td></tr><tr><td style="text-align:center">feather 👍</td><td style="text-align:center">2.0</td></tr></tbody></table><h3 id="3-磁盘大小">3. 磁盘大小</h3><table><thead><tr><th style="text-align:center">format</th><th style="text-align:center">file size (GB)</th></tr></thead><tbody><tr><td style="text-align:center">csv</td><td style="text-align:center">1.10</td></tr><tr><td style="text-align:center">csv (gz)</td><td style="text-align:center">0.53 (48%)</td></tr><tr><td style="text-align:center">csv (zip)</td><td style="text-align:center">0.53 (48%)</td></tr><tr><td style="text-align:center">hdf5</td><td style="text-align:center">0.55 (50%)</td></tr><tr><td style="text-align:center">feather 👍</td><td style="text-align:center">0.49 (44%)</td></tr></tbody></table><h2 id="性能汇总">性能汇总</h2><ul><li>以下统计的是 <strong>相对得分</strong>,数值越高说明性能越好</li></ul><iframe src="/html/data-perf-echart.html" 替换这里的html="" height="600" width="100%" 修改高度即可="" frameborder="0" scrolling="yes"> 支持滚动条</iframe>]]></content>
<categories>
<category> 量化交易 </category>
<category> 量化交易 </category>
</categories>
<tags>
<tag> Python </tag>
<tag> C++ </tag>
</tags>
</entry>
<entry>
<title>【量化】爬虫获取东财数据</title>
<link href="/posts/spider-easymoney/"/>
<url>/posts/spider-easymoney/</url>
<content type="html"><![CDATA[<div class="admonition note"><p class="admonition-title">导读</p><ul><li>这部分主要讨论<strong>基本面数据</strong>,获取<strong>行情数据</strong>看<a href="http://localhost:4000/posts/quant-data/#%E6%95%B0%E6%8D%AE%E6%8F%90%E4%BE%9B%E5%95%86">这篇文章</a></li><li>详细的爬取标准文档见这篇:<a href="/posts/cores/datasource/#Eastmoney">cores/datasource</a></li><li><strong>NOTE</strong>: <a href="https://akshare.xyz/index.html"><strong>akshare</strong></a> 已收录所有内容,不要重复造轮子啦</li></ul></div><div class="markmap-container" style="height:190px"> <svg data="{"t":"root","d":0,"v":"","c":[{"t":"heading","d":1,"p":{"lines":[0,1]},"v":"<a href=\"#爬虫基础\">爬虫基础</a>"},{"t":"heading","d":1,"p":{"lines":[1,2]},"v":"<a href=\"#爬取规则\">爬取规则(东财)</a>"},{"t":"heading","d":1,"p":{"lines":[2,3]},"v":"功能实现","c":[{"t":"heading","d":2,"p":{"lines":[3,4]},"v":"<a href=\"#功能实现\">源码</a>"},{"t":"heading","d":2,"p":{"lines":[4,5]},"v":"<a href=\"#处理json\">处理json</a>"},{"t":"heading","d":2,"p":{"lines":[5,6]},"v":"<a href=\"#数据存储\">数据存储</a>"}]},{"t":"heading","d":1,"p":{"lines":[6,7]},"v":"<a href=\"#数据应用\">数据应用</a>","c":[{"t":"heading","d":2,"p":{"lines":[7,8]},"v":"<a href=\"#示例一:寻找-“A股大鳄”\">示例一:寻找“A股大鳄”</a>"},{"t":"heading","d":2,"p":{"lines":[8,9]},"v":"<a href=\"#示例二:财报公布后股价走向\">示例二:财报公布后股价走向</a>"}]}],"p":{}}"></svg></div><hr><h3 id="爬虫基础">爬虫基础</h3><p>本章要爬取的东方财富,数据结构非常简单,在爬虫领域中属于入门级别,使用 <code>request</code> 库即可。<br>下面代码示例,爬取 贵州茅台<code>600519</code> 的一些基础操盘信息:</p><pre><code class="language-py">import requestsimport jsonurl = 'https://emweb.securities.eastmoney.com/PC_HSF10/OperationsRequired/PageAjax?code=%s'res = requests.get(url % 'SH600519') # 贵州茅台info = json.loads(res.text) # dict</code></pre><h3 id="爬取规则">爬取规则</h3><p>东方财富的数据(<a href="https://emweb.securities.eastmoney.com/PC_HSF10/OperationsRequired/Index">示例链接</a>)主要有两个特点:</p><ul><li>优点:链接条理清晰,便于爬取</li><li>缺点:采用拼音缩写的命名,贼坑(例如 <code>yjbg</code> 表示<code>研究报告</code>)…</li></ul><hr><p>先分析信息页的基本结构,<strong>第一层级</strong>是下图红框部分,<strong>东财将它分为十六个板块</strong>。<br>我们会挑选需要的数据板块来爬取。</p><p><img src="../../images/eastmoney-head.png" alt=""></p><hr><p>以 <strong>股东研究</strong> 为例,<strong>内部还会细分为多个第二层级</strong>,例如:</p><ul><li>股东人数:每隔一段时间公布股东数量</li><li>十大股东:前十大持股的对象,包含增减比例</li><li>机构持仓:有哪些公募、私募基金的持仓</li><li>…</li></ul><p><img src="../../images/eastmoney-partners.png" alt=""></p><hr><h3 id="功能实现">功能实现</h3><p>源码: <a href="https://github.com/593413198/Alpha-Bet/blob/master/cores/datasource/Eastmoney.py"><em>cores/datasource/Eastmoney.py</em></a></p><p>先实现一个基础的爬取指定 url + code 的函数:</p><pre><code class="language-py">def crawl_base(self, code, url, fields): """ 根据指定规则爬取 :param code: 股票代码,如SH600519 (str) :param fields: 爬取的键值,映射到中文 (dict) :param url: 爬取的链接,股票代码用%s代替 (str) """ url = url % code res = requests.get(url) info = json.loads(res.text) info = { fields[k] : v for k, v in info.items() if k in fields} return info</code></pre><p>后面依次实现爬取不同模块的函数,并选取有价值的字段 …</p><ul><li>操盘必读 板块</li><li>股东研究 板块</li><li>其他略 …</li></ul><pre><code class="language-py">def _crawl_cpbd(self, code): """ 【操盘必读】 需要字段如下: √ tszb: 特殊指标 √ ssbk: 所属板块 √ zxzbhq: 最新指标 https://emweb.securities.eastmoney.com/PC_HSF10/OperationsRequired/Index?type=soft&code=SH600519# """ fields = { 'tszb' : '特殊指标', 'ssbk' : '所属板块', 'zxzbhq': '最新指标', } url = 'https://emweb.securities.eastmoney.com/PC_HSF10/OperationsRequired/PageAjax?code=%s' return self.crawl_base(code, url, fields)def _crawl_partner(self, code): """ 【股东研究】 需要字段如下: √ gdrs: 股东人数,通常股东数越少,代表股价越集中,则更容易上涨 √ sdgd: 十大股东,包含持股数和变动比例 sdltgd: 十大流通股东 jgcc: 机构持仓 jjcg: 基金持股 https://emweb.securities.eastmoney.com/PC_HSF10/ShareholderResearch/Index?type=soft&code=SH600519# """ fields = { 'gdrs' : '股东人数', 'sdgd' : '十大股东', } url = 'https://emweb.securities.eastmoney.com/PC_HSF10/ShareholderResearch/PageAjax?code=%s' return self.crawl_base(code, url, fields)# 其他省略 ...</code></pre><hr><h3 id="处理json">处理json</h3><p>写入 <code>json</code> 文件时,需要注意中文编码问题,下面是一个万能模板:</p><pre><code class="language-py">with open(json_path, 'w', encoding='utf8') as f: # infos是要写入的 dict content = json.dumps(infos, ensure_ascii=False, indent=4) f.write(content)</code></pre><h3 id="数据存储">数据存储</h3><p>结合多进程爬取五千多只股票池,注意处理空数据、网络错误等情形,最后分别以 <code>json</code> 格式存储在本地。</p><p><img src="../../images/eastmoney-json.png" alt=""></p><hr><h3 id="数据应用">数据应用</h3><p>量化金融追求一个实用注意,那么获取这些数据究竟有什么用途?<br>除了常见的<strong>用作策略因子</strong>外,下面展示几个与众不同的用法:</p><h3 id="示例一:寻找-“A股大鳄”">示例一:寻找 “A股大鳄”</h3><p>股票市场的股权拥有者,一般有两种,要么是个人,要么是企业/地方政府/国家。</p><p>下面我们尝试统计 5000 多支股票的十大股东(从东财爬取的数据),然后稍作拟合,再按照持有数值(也可以是持有公司数量)排名,便得到如下图表(绘图来自 pyecharts):</p><blockquote><ol><li>带 <strong>香港结算</strong> 字样的主体(及HKSCC),一般香港/外国投资者通过港交所购买的股份,即所谓的 “北向资金”</li><li>实际持仓最多的是 <strong>中国财政部</strong>,他基本持有了各大银行的股份</li><li>其他排名高的主体,基本分布在 <strong>石油、保险、电信、证券</strong> 等国有行业</li></ol></blockquote><iframe src="/html/sort_by_amout.html" 替换这里的html="" height="500" width="100%" 修改高度即可="" frameborder="0" scrolling="yes"> 支持滚动条</iframe><hr><p>看这些国有巨头的数据没啥意思,下面尝试筛选个人持股的排名。(筛选条件很简单,长度 <code><=3</code> 的便当做个人)</p><p>排名第一的大哥叫 <strong>魏巍</strong>(竟然是28家上市公司的前十大股东…),冲浪查一下,发现他位列 <strong>牛散F4</strong> 之一。<br>而排名第二的 <strong>徐开东</strong>,也是A股赫赫有名的个人投资者,跪了!<br>靠一己之力的买买买,坐拥如此多的上市公司(虽然只是前十股份),其艰难程度可想而知!</p><iframe src="/html/sort_by_num_personal.html" 替换这里的html="" height="500" width="100%" 修改高度即可="" frameborder="0" scrolling="yes"> 支持滚动条</iframe><hr><h3 id="示例二:财报公布后股价走向">示例二:财报公布后股价走向</h3><p><font color="#FF1E10"><strong>TODO</strong></font></p>]]></content>
<categories>
<category> 量化交易 </category>
<category> 量化交易 </category>
</categories>
</entry>
<entry>
<title>日志:2023年10月</title>
<link href="/posts/2023-10/"/>
<url>/posts/2023-10/</url>
<content type="html"><![CDATA[<div class="hbe hbe-container" id="hexo-blog-encrypt" data-wpm="Oh, this is an invalid password. Check and try again, please." data-whm="OOPS, these decrypted content may changed, but you can still have a look."> <script id="hbeData" type="hbeData" data-hmacdigest="70633d927d709480bbed1900c6022d41ab8aac0dc51eaccd23f7c75881c20b45">b3dc07a81f6459d120ce338ccca550463faa708b9b4d89df9ab14ba4edd809f60b32d15b5672ce84c5d84744f7f475de0b1305a637ea913974dff7410a14ed4dbbb40a5278eb14bbac34e59e12a284c7a294ab69a495d47e11e78ad50feb1070946a0084538613a8500fde76ee182cdc92156f5cc629c04691a80beeb70e034667984a29cb1db27ea11974521ef316310dd65dbfbb2a0d60f2a8ce6cab9f6cd825044604fed45aadefb22ba2321e19dee0566e6736947a45ad09061320fc246487e86167f696ce0b59ebd851781c74c35b8570834a7fb16ccddf85c541da4399962ab09e1e1ffa50d762e18e62c34615594554095f4effae7f3173f3242f7d285478fc1e6d0eb779e3ad11f1012997a7691d132e80e6dbc913b732d2137b6b8f57f694a4bbc70f994c2da6ae0c94e2f6c8c831f133927494996ba5792fd93c2fdfe0eb6f6f9331eea96358ec38968250bac0d7c070ca45382255703402e14b1c12f4b6575d19e98b134e304b397b7aae8ea19642fe8f01427dc67ab652cf56b4ef5303ef984d86a43e75e38f31fa2ee88ecf4338d7b0525c22f6a0239dae2eaa2bd1f594c7ec15c9ecc638c89d4a07eedaacb68410825e683c07c0953c99245fdb7567893d747df61cd912a11503c3fd4da64a36e87856618b775733cabcdeaad064836cce2c64d7777e31bffbf3b39f702ab793910862d380501b3be50aa526265eeffb3b510802d4dad98e511ae0ffed757f91e649b5fcc0f8cde1f9bcf6054be8c2434c50711ad3d54cefc96c0776fd334c84f3108aa4b6417393d4310021ade3ea87c841c093886cd78524a429172dd29da1e7d5002c8e3f28656021a6591edce5666dd9ebe1ebb1576689139f329ef1b3c741bfaec6a1b6de1d6f8c143382824e1ddd5744cb9ad223885f43f7541ff956a5041c29f957d7769b29b067fa9e16305dddeba77fd3d04a2555f5c4ce9a76454a000b2b4a15d2a70892d99bad0d2d90ec4b4d60dc1a6d7b8985a49136637a08f639935102fa843b0443b7e75c9c5d7d86d28a203937faaef4dbd7c0ec8cdb379bd118e686bf5b65b558f325b738f12bc4c1652b0e1d2b65cf17afadec738ae39b244ec2208fe3e1bdd5177f6d94070e482093d1d5978e52145163cd7d94c8778e1a680be7ba2f3f264ffd2a5bf487519e45a4623775b22a2e801bb986fbe8a6133c32a786e9c66149f540ca4273f922926e6460a0cd2caa5e13d3652f2fe9a9c7e7ce8681b3f5c0b404fc870b0c61404454d36dfb3b1bf7b3909136046e8290be28e6c82d5e974abd3f0d207ce677d455bb5652fe53927ebb2e50a3ad6dc1a77f5e10e74070ff59cadad82fdecffee5f60f8805f4cddc270416c343e126325b9b49963b9df72ea0f287ab445fc1043cb9aef960efaa7a3d0a07f47737e5fafc5f31872ae381de5d018a507ccf9c5448f48bcd96516a314284643c241f2f5bbb525829025833bcec3e927b1c932b24826c601d2601d2e6146f3c8e46aaec0820ffab38a31c0f50b209b3b7c70c50e09c4467049ed8336723a3f20d97294072fabad5419102c589a6f6f3c4d5468ec67265c88039e30424385ca8fe9b51a50cc77e21b4d73cc71e4c5754762c60d83470e3ce0022755d7630bf3ee009c2fd32128da5d155cd35950b47870348dcb92e52402d73f450b4ae56ca94748c88787f93e5733555a57ca77c19fba83dae595d97246d435444a4727be3dcad505593a74a4ade670032088934539d924c3421e219af2ac2a2958ccfcc5fff60a1e47c6af06e0774055f0c0d2de9438dae41dfe9c6bd0a89788d32a233d77987ac3980510444340c6f8d11750631d3175bde177b8de7dbfb0ff25056c396cd9dc597cdbcecff5483b543d0b61141bdacbc76edc296657bf3ed88730ffe741ef90d1ccbb96fc265249afd200e7d8c9c08c45033cb197d713a054d6acd59cda40c54b0b030a60902e1b3d56fec60e709de21d706604e83333058be491185d29eafc2e09269503fa7652ad30dcddd84707bb35ec91aaa80614753e2914023a70c015ac6523427291a18d2ce214643949fd34bee0c7041cae1560f10499faecfb8ad48b8a3dcd4d9ef69e1f5c3787935147bc2eee9148843ccad1e9c8432650d5264bdaec7a30567126e75d40df65d85805a1ba9ca65819c9d7243d222074570717b270263fe110785473367e29bfc344acfa6db173b1c493eaa081c002380484c10a20b9f2e547e499c7b962e6d525bafea81b322aa5e3107ea01f095e0ccbfac0dce867ed1ba0574b012b3f644314c22cfc0f5004c7ae06ac7169c5fc35d711e9e577b18bcf4c2d02ac44aa220d1ad5849852e9e6790786b0fbb57e3a0433492067ab8b272b275ec9f645c0553a4531feb03bb9a34e876c7b5fac23f3fd63ad4236d5eceddb7f5368d2d3379b657affc6722f46140b31e719d1aa60ffc182c06bda7b7132e9e95fd229cd2fa6be754dacd1fc7380a2cdeeeecf1652731694b935280793a66f55e84abc0939d3dfc37f9376c817a69db0f9aec926c62dd7ed73c18934e8464071e2438cdd41df20875220542ac80c0e42446050e34ac6c2b82c48b1a09d827963ceabad89fe0d2f27e706e994698edefd67f6c731fa454e864e439894ed3f2b2822bded480a79fd89b9b1eb2aa88f756bf833ad0e5f0b6cca0e6acc7b3905b17bb51b7a36ab6e64552515ede61d857ef4d9deb9216f1045d88fe20f12916c13c2d35c3c8160bcea7475ee055dab046ec85d6451b72ef6b746f25b48f246a1ef0bd8f6c187f40f362e3e3bfaed162ee482e4e90eef9c761b03c24d21e8428e34753a3c59f332ff38300514805d1e0439e0c31526fd95aa8057fcbeeecb21191d65c71f881b62d2d32c0932abcce78ce06c196b69d3a52235a2e111fa91844fc2e4a35c2109adf47f8fc7514e7cf9d6d952193955c9f5015782be51be7f6b865e246671950599d9622f8a5caa07087852873ea794853e026b52d9ec8ac97c906157dcf22fc0ab0a9ad96d13235511031257462db138b40592cce32641b7c7e9d4437346a223b8cc84aeecce2e0c567110c3c7359caf7af45a859cbb4cf5db22849eacc9e4f646b87b104be5bf2cf19090b637e0b6def1c42aeb4df72c802237f84bfc02f8a89ee7c752f5899fde421962f2d161ef0d496e5322d06c132bb9233feda99aa5bc0d40c6614a199813f4516ba9a09b7815c9d36f4dd75d0583b59616dece07380381a0bffb0f4208893603e9c7cdc2a58a4046229b5b2ae349b57ad4c4ebc859bae8660c9f6d70e473334eb93620444d39b496d16055ff13139cc45967df24fa7ed83020f1d6582cd0514e20e0e5c223ccbb248b3a49949748009f9cae16d0444ba5cbece68e2f363c963781a3ebdcf8bb7cae8104cde698040da97ca058b1142b22317c6239955e3a6f64fa652ae3fea57ab30d924a176db399fe01e7ac5cc5b2a9c9eb6c3bc23c349ad434598e1629f47cd7ef8b26261dc0e7d5381188dfc84da3333ce06cf53365a116f82fb3c923b358de4d71f172c13e2f63e05032e53bed7aeb24d2cf8c9139e4ef654d7d8fdd96b1eaefa19551f4251866c5d64e50b36e958872f784a33f962e9e3461a100b69e1f582ad56c157cb03ee3555ff810ee77674ad863fc441d16faf4b5ede8bd9a27a448dd1b56ca0beb534821532a0a8f94abfae979025eedf79bf09d46edcb3945aec4d39814ff9496abe7d7290b5a7ec2805aba7f1aa2cd1372ee01b08ec80d4e261be65e56706d3cdcad34ee48cab6cbf3759f572bf37be3fbe073e5f8a1231a893cb5de0fbf3a263bf81f83099e65266a4c7001e491126d6dba91dba73066b885e816269dafa4dcf3ce0001629a66f84c9ac6745da2c500c3b7adf669b0e58ea9d1e1079986e48a3f8f4e1f13cb652a3133dcb987c9bc3389b3fcc768100d993a757f4b4bdb8afd938b52ac7c5d61b0f6ddff386f2e4d912d83a2912fd2706023f40c46e7aca9ab04bfcd28d32dc021f556f640c51f7e4ebf359f952f05632aa849d97ecacc4f530f41264dafd11ad98db8023ba1a37ba3b7e85506739a52bef589a5d31559729be39b0f9e907c42ff7baeb03dff2d9aed0f0b648992aca08d58d5a74edc69d3892b3f2da6b719411b8adf55e8d8e6b24e40476c28a66bf4b9993920c4054c63c17a27451c1590bdb1330b1406f0e527bc9d9aae5ccfe0a023c7226f4b4c241db936a8c1f1002aa6d0d53da8b7e3a9af8b3a03015678abb07cabf06ccc9412fa3a4a6d1ba80a70841591bd300a9cf0044a3ad2c80a0ead283cb856137b6625c8e7dba6b5435085ebc2f82015dc60e17260e58a1edc7f11456780b6d2cff008b1d39a4c712c9eaf0841dbce5f3afa0512c4ff1009124e78e624a60760ab864c4dc1bbac03d2a3514b95cc9dd0930f6342aa895253cfd566221379818e52acf7cd1bd81102f03bc746717c90da817ad3512cd04223c8c0264e1c132c1ba99bdc393c6bf6f1e0c3fd9606392a586aeb832d47ef3443f471e8aadc53940ee7af5bd09d4cdb902bec979494423797ceb224e3835aab7d83dd3ff30e9ddd2bfb21f3c875011e3d057ea216fe67c429def8efbde1cbbc4a817d2eff6515c8d41fc4437e4c557fed3e11f301a2176d9a9975dfa7bb26b5a01ddb2dd2a029ad768b35b9ffd0c0182b1363715aa3712e2ac4d9e4639dbe1706c87ac633520a33f561bff60b47c5348e444f31dcb5f29f8c91b472abbbe11bba21d0708cc00c295baaff6deb696b6a51b17929ea32968f135e3cdbb8eb2409389106ff82a9d16449e99f5ca66a08fb647245f1c67860a1af24b2dabafd651f8e465f7cb724505aa6af55d00f8c24b7e4abaa09b8ba863880df79122b3a1f278c3612ae87d893cc9e64c38efa5600f9b88d01139a5fb90dbcf5853da2bf6f277d1bd7307b60f45e0286761364fde5cf15df78df87cbc6d84d8f42aac9582c64de72c710f3ae897eede27990d1821c8b0e8f66bbe58ebca1483ba5532e9cc84139961597f385b73f71673215c1df7dc53ad7a7cda73358bc2b79c7679ace10c262de19a6ee8e268b892b55b5dcf7215e9aa0947415b7060a85d5e4474ed98b5109452a8ad06f1afbf3b9330d66e2b5f2a17551d78d7f00f2dd3154074770f7b2a15bd82750a57ee1e75f8ea299ca50ff1ad4b05b5761b2cf7aca695cfb4789ec59d7ad60cb68d5d4b589c3d98784313d7b739ba6aa7f87bfec47b5631b9b4b5538f79347f9a5810b5dc06f35a91fe03b4119c8a34ec656fa66795717976edf0e3139a379b97def36bc47e7d6396724459212cc2fcd7dd7bb937939ab2c9b8c60996d534b2161920a6c3b1e006104080646182867139100115ff5ad598967e90337e31be254ab62c643cc38c43d9caafe25c15b831c0360cbd6e981a7a9ebcb32b16cc9d1f8942cd64d060823a54f4ff446342c2ba1c85b4787f5a3f838710751ed280d1de49f2f511707bdde67b9d7e61d21d6708bdaa2a962f0b16c6067320a6b2d6b6348abdf9adf4ca9ff0adad3709a4495835811869970b1028864c971b44eb4f5209b461dc916d627329bcac943329b759578bf7eea4e5fb7162565759ccc157100d37efeb0a6590e1807d29b1a6db569eceb58768a6bcce0988eee5441b362db819b545bbfa13af723aec5e9ef9ffad8ab78371d7c962c6a08519dc841fc0390804f45cb168c98fabbc1aa7ed77091c9f971497faddc6c1f81038f5ef585c191d162ada31e1c6086dca1ea0b7eceb1d4740765241de5baee0fce2dd819f39ab54bf20af4fe49cc8b96d5d0159ab8a292233ceb7619ebf2cb01d984f5e5565e9706e039c0dd83c4d0796e85008d8a24f271d0b43713a7ff8817a8d9ea726e01033d6a314a34a1d7386ab9a9338b8570370a279d2a9d729ec24d19627a693ca47f8864574b329e55e560fd19860ccf024280af1803a4b1b9303a072eccdcfc319337ac7ed1b4003f7589949493c0f9019da27d9006674093882a9a61f5a29c19581d50df32d9407f1c1ab1b9e02a27657e547861f4b7916ffe6b86cb8ba96a05826d250cb88b1184e28a7dd9620217740ac1c697b8033c8cb7f13d65c178ace97ba8f772c14f094d1c99740498881bca7820709de94c2b452b87c7922bff7c4626ff58841b0f6096f1e5fe1b6e7dffc6e196e7defa94b26c204cf7216c2e061985a38ee880897b65dffd29a3e18c532b74d9eefd9ab47ba1882969a2db6460d7116a97da69d7fcc096cc6ad06d858814d58d3d75a2e29d4b109e6cac09e80c7586e1e6dd94e01d9a555a533aa0679dd504fca015aa43c6d8e2cea29ac4b3b4dddbfd87634033cf254c2584ca6010e6bc885cd13000ea8d2a1fdc3fd867834d374571c52670be4950e9a811627126a36feed5e51ef85fa57474b64e8ab072a3359b0307a9b18f9e891777b2fe1c6a8959c07d109c80499d1d20fdfb6b7e3fef3a073c4549fe1a38d645cdb699c9030ffd390e8016fed7b7198a8c7393313199822a665048aa979fb005ada5a52c492b3ba58486ec967325e212bf499ca26e6d1df4dba2e40d9622e45e58b282858dbf2836518dc9afe5b08769bf5f311</script> <div class="hbe hbe-content"> <div class="hbe hbe-input hbe-input-default"> <input class="hbe hbe-input-field hbe-input-field-default" type="password" id="hbePass"> <label class="hbe hbe-input-label hbe-input-label-default" for="hbePass"> <span class="hbe hbe-input-label-content hbe-input-label-content-default">Hey, password is required here.</span> </label> </div> </div></div><script data-pjax src="/lib/hbe.js"></script><link href="/css/hbe.style.css" rel="stylesheet" type="text/css">]]></content>
<categories>
<category> 个人日志 </category>
<category> 个人日志 </category>
</categories>
<tags>
<tag> Python </tag>
<tag> C++ </tag>
</tags>
</entry>
<entry>
<title>【AlphaBet】cores/datasource</title>
<link href="/posts/cores-datasource/"/>
<url>/posts/cores-datasource/</url>
<content type="html"><![CDATA[<div class="hbe hbe-container" id="hexo-blog-encrypt" data-wpm="Oh, this is an invalid password. Check and try again, please." data-whm="OOPS, these decrypted content may changed, but you can still have a look."> <script id="hbeData" type="hbeData" data-hmacdigest="0b1b8f656acec1ff98a93abbfc04f797fa7ebf713f291bb92451c4ace903fa56">b3dc07a81f6459d120ce338ccca5504625d42614b93020ffae3fe33410b4b3b7b132d51143295cdac39b893eaf193f19c019943d7e59177912cd230786c73130f76d9215c2bfeb61246a440ea10f1093f8813644e0539c6bf43af0a466ef4a3869f03545f2371c4406e374164f0141df491302831c878c9bcf684755d710c17cb4b0117718299701c3e92d7e680e2d2efa6064966556690e77b81a309d85d8062be1c5b16f52b901b914a1111d41228d16b637e33b3fb8dfd796dd947c22ca30563139d3e404d882931db783f4126db99a7310744b338a985b524a9dac8083a2a5a5aa404a7c2aba3842b8f7170f7f93a7593facb8e80f8a87e073ace10df1f6817abad52038b81e62d6a1a3b5bc4d762953c1267b1f408b00733262f7146a94e3cc80d2df634050dee46a6355ee9af83995a7b5f4b80a5627ca71066f60f41aae74b02944fe44fae3e3890718da71a40dac85b3a4877d7fcc4da2b4de3a90168081f652bad4aa16c8811955fe8cc5f31d48e9e6b338f3a16d72afc12c1b30979360689fc8ffa09031579ecc5c27ead8281c37a80b03115545b47cd91d5c57bc6503fcf138d0842fb48ab0e4172b9d4698818bfb1ceb274eac3a0d6b9c3588d345b8728b191f6c2a76bc31b7cfd2696bae0070bdb02e358907df38197b78dcfe73ee8453a3b0e5fe968017f26b66fc424a40812601fc222a55e5611d4a8588216b604291816ee56365c21979ea5309e05fbe4406a199a103f743cac2b4f5490ed210d312f7892b3cd2200a2d21dfdb5da61de40922326ab8ba2ad850c74083ed76534b200289b9d29bce4d5386cdf542920306aebdb9c3f8c6298c1ae30e68a9c24dd7117c5df3fd9c7deea388325380c97ce330b91d3ebb1fb0b033257940b7290eb571146644e8a93ac55e78aff64e321aef22ec9b15358bec56c2b3fd8da25dcf16af0bce5d57d453df12ea5406d0a74b0eedaeb1a9c7e47305f5b02c5446a0c9e4274cfe77954f49a64ba12cf0bc45a1a2dbc0afbeec373af4d6afa3495d4a6b0ecae0de608b00b884fb1f557d13f85725e01671ea88d5ae21a0b9330dacc81cfae76a95a04b0f2f1b2571d53274d9bd3ced52a666e7cb23ad553ee8208fdb807b4bca86092ed2c75bf74b1aa541e634cfdaf7324570274d5a956d43d0c9c8acceb5fb4ae741343a1541baec4798a1b90e46785bfb6776edf372f608809bb214bb938602dcad522bc85947299cfccbb3b82ae0bbe06a228ec5f5a51745a8d47a7b49213278d2a1a69f87deec6e6333cdc71635161ad51d49a3da3b09b8cb0192c4b44ced1a57ec859f6c767cf78e72532397dfa8711c1f4183761fd657018ed7ced1c41bf6a38aa2e17b945ec39a9d6c89bb4ee46ce084c5b3330350c1bd3181a8e43fdfa5c469f32b52d500899ce836250a75f55b36164f858641f417acb902d14ac00d4d578249fcca2cf13f02610a0ccd46ced8d1cc6b877443681997f635223b29c19f432eec63583a9cc76947bbb9aede27e3627873d2b3d8610ce976af5f09a85868ca27eadfd0b221d308a5eb00f98807aa234033dfeae4cb79d123d75a928c28d987d08d220b6ed82f922dee7f6ed63a927892e7d05c9cde9eda1910da379fa307677b2db814089e95fcee0ffa8fc77f67c28e89f4ed744b36c0074dcb5aa5970394a2edde2b6b372e5428552027fd5e152f7eea267dbfbc5fabdf6fd30a482cf5405a855497613698139b650e96e3697703aaccabc94e9238ac7df7aede7ceae407823a6caf5b164c88e88229e5976781c65d137c5292b3f212cd62548e6d26e38d0bbbe1ab74922bdae5cbf75d438082e7c9728d810d48a1b33cc1caec7f360c988efe754987db66faf5fd146d5765c046498601d43150f3757bd9c7d9b181f65e9c8c89a3a3c9e56788a8602a30f688e5c8ccb610f82d8433b46b8f0f503e92b6911a4d6633471246ac12e1e5f62f11f01c986fb06b5addbc2de2ce804ae602539298e728b3f0e32374a24f62117947617e2cfa584c4bca77296877f07271b4939fcba9d133e20b241aad65c20bf40012e53e045794079658b7f7a220d47d109cb8b96949cc37b3ed39be79c9cb1392049b476c81b62080e3de8759f1bf32b32ee4e9ee66804d3e4aeb7553126894666cde4fd36d5f2ef3bad7a9e634d680eb8ff3d3042813fbc24c5c4b945a115c949a473a24bfbaec6ff4cbf93a93f5bf8cc293a2dceb77e9f3628928510109302f70147ea35c95213168f585545d129ccdb24e6716774cfb7eb12a259c0df7814a4a60fbc879d91c15a3270a11c72a5a96e1672a3e910525e270d2c0b754261d7098c285ddf9632614465392df09cf6f2a51de2971f6c69fd925e139876990cb1bcf76af3cfb7e21390d3b7016115f853ce4e841a31fb157c0d117be05c030ad3112efb7942eb8d3dbe53e43d7184449b7ccd32d8436d758f449ff4a043d6532db51be35aaa720278150f8e6eb37932a71ed5139e001a99c48facbf8611cd32567bd0e753900ff548157b6ce628ba80d2c8ade57a43c3ea1bcd95aebee89bf3f707aa6664778cf378e97f171404f3ee2e545cc9bf5d7c232bd72830d02c078f09170e8b574a9b5a42cab62d77e5232b4b999990bb37577904b3cf30e0c679148384c355ea7fb3ce2d675f8de4d94caf6823840f508367ae512364309d5dffb4898c5b3acf9df38621fbe5138c62ce20e1c633c9f4ae8bc31ec8d1fe3960bb88f3e22b9d0eba64144c14abde3e14d2002abd7ec57ca4be265d935e3ae86333db06108373e7190f261c4f2ad274a39f8149302e5075d6a3075ba6f7caee39e60187a3c264520801622fddc943619b4e5c4f498208b58b00028dcaf273a431f46a4bfbeca5634567593b711b9f97d99e86c9c6830c9b23d00b7ba8994c780ebd5bf6fbfcc8d9896f86fe27b7c5a6a229bac38dd0c2143c8c7bb0f36a9f1d84f08577692a18a63a89c0ac402fbc820fbc5f2ca4f77adc3b37d5967ead5548042170e890818b097ea0a4c69894fa848ee39ae14ed4f25af1260c4639f7f6fcf1043476872034a194616834c15f92abb40240298b77d34744b032b51a3b6f9aa52bde8064954929e2866f2ddb45daf630b7e7bd664b9b68cd9b860753fa59d2ca39a8d881f293b8d66cf49e683716a976cee7cf7f2a2d3fa744d024980e15f97ee49a5747b530c4f4278563e0bec4e6304f9b44dc894f1968f59ff5604d76b33c9ff92d225554572f31459f5d87de3ca513eebca5df10843581438779d165326cb5d35828017a6f52c3e377a484348a67e05db4fab09e77f6a3feef9656696e69a77a6ecacbdae0c7195a2642c21080fa630f6e583fb6c11583aba3447986b0d6df732d96454955b759f154245c0e5e1367cfb9d8a40081f153af35403a042f64ae92c75055c9e1f03660c6fb602907bd2eae4eee7cf4c39970d6671016ec3b630cbb48106f4eaeb756b0d2363ec2adfd62ae770df9b89bf2d0ede1ae709cc8575e9fac8c0cd910006b240843190fcf320163afb1421a16e9c45ac152f9710427344d399639ced68ef07bae2ed4532489e64cc44ee12c18a034a29a6ab44588297e462533cda884585696884aeb5d5bc96b4c268d84af086b462dda95a296b2314dcd3d2b8d5a0dbf6835cc9890b23a3ea8d10d9cc426a5988b60c01a78761c618e67010b6804107b0f19286d974fb2ef58a43f7afcd8d04c018bc8e85696dbb9471b33f8cf0759ae8ee3584f31e317f62d56c94759ce59d0bfd7a85eb2e2970030b682b1a0cc66aa8a5939ff4ec137cb3f5b4b39dbf861bdf1e47df54dcb75b9d2c8e351baf67505f4c5faa964e6dffd043bb756c1df8fada50df8aaa4aebf68a59d96830840bc6f21baa66c77ba968a8d808f62d3ff99684371737a11fd15189a59960682831f4b542f0e8e2e9db8ddbeb6bf8209bb1aee6fac64a7f86695a394b750d0fa9cdf30fd6eca3fe769e5dfcaac87e3db0ce32dfa790becf81afe76974b440265c8e0d4a5f74ac3fba72bd63184ab603fb0f83a17bf7e3ec04fb2e63dd72ae99865418a42a0d8ab8f89dd286a58d3388eb89cd501bfa28504fbf2a372883d1da58c20f001964d472c9fb278adf2e50910d455d0c513900afb020780b044f930ba9903c79ddb8fd0b8273faa9a2a577dd020410a56f951844b5b76c6e9dd7e8e0f80b7978fe8f98f94936ea06ebd420de5a02a402afa6ace3de20bedecefee37398980e251984eaf0f6779aeddb7fc88ac76b9f3c67832ad6a56f0420d01aa75883ceb5dac50d64d0c158500ba22d12e8075665da3e407c19661a8818a7ad7a0191cd6d316bf292e09bd1932a8a13299a5bf07fc683e33dd2c5bf9eaf8dc8dc2383e719180758e38c7a3e1e0ea3dfbd047026d6993060cf03eebb117a814fd115c98f6806dfc1a1a7cf322f1f53125723ab68cd06305de0b0e06b2f538a57c2cbb18ce5945ee5cf15fb357bf97317d634e94f18f01267e147</script> <div class="hbe hbe-content"> <div class="hbe hbe-input hbe-input-default"> <input class="hbe hbe-input-field hbe-input-field-default" type="password" id="hbePass"> <label class="hbe hbe-input-label hbe-input-label-default" for="hbePass"> <span class="hbe hbe-input-label-content hbe-input-label-content-default">Hey, password is required here.</span> </label> </div> </div></div><script data-pjax src="/lib/hbe.js"></script><link href="/css/hbe.style.css" rel="stylesheet" type="text/css">]]></content>
<categories>
<category> 量化交易 </category>
<category> 量化交易 </category>
</categories>
<tags>
<tag> Python </tag>
</tags>
</entry>
<entry>
<title>泡芙成长日记</title>
<link href="/posts/paofu/"/>
<url>/posts/paofu/</url>
<content type="html"><![CDATA[<div class="hbe hbe-container" id="hexo-blog-encrypt" data-wpm="Oh, this is an invalid password. Check and try again, please." data-whm="OOPS, these decrypted content may changed, but you can still have a look."> <script id="hbeData" type="hbeData" data-hmacdigest="c78bd27c6bd0046deeaabc5c71061689e5d203c29ed3504ca7fdaf20daa5382b">b3dc07a81f6459d120ce338ccca55046d81ca386354419b6b77d6b55182b8ed301b3c2040d78f9d2f509a2f04ee45763854c7c389b18ca9301f9504a65415536caed1f6078a5933a1e86da75ab9d764257ebf175c8703c4a30d15bbbf7d92b3e403a1caef391b1614091ab770b4e4595b54cd1b310f8dbe1536409c8de8f79bc4348c1bc29c770dfa2315e36ce54739a172780106782350060c52eb57d286eb75fa04878f3cda90fb70f7cea791758878e87bd9cea4101225d90f42573e9709ba039eafbf4ac3e239721445477b2bafdcfe803cc6391fae439e7f1a958ddfc372c8c5d3d8d141fd4a6afb2f9e54b7b3b41df860024fd18e08c38f6e52b445e4d939b03c40370f1e6b774e3b96f4516cdaecfb256a2cf30b57347eb8cb5e55a971b8c485bb317b25d86f0bb77d12436a0c4580debbf73cb1ef658a04fa8e00e45bb46dfb81abd531bbbcb397b64220491dfceafb37390ae4c9f9163510289192df6b4a6344439d771050ceebbdcc97c64a6d10bd96f462781511c3392a795ce2ea09d63547e217b36f148524427d0e0479768f0474dd3236f3a4511c3af35a1ee4b63bb6a81ef4c7f23117aa444ed51ce8893dbb891ed914fbc62b39350da2622ac8cb9d5848e745e8ab58c01e69db620d0f75f64a2fe19aa7c1f1215ef04e78a6f32e4cc5d829e6e3e3a9ea1f833531299902c6be1ac12b99b1afde30b3718a25a9e5db3d02f6fbf37bd04f66720b67d5e989718f121b016ecc5dab29b54b1b92946fc4540d0ba419f5c57b90c085211c31391e5c10b025cd4fd6e3e9b3207259b277f1edc6437cc367f06b2a49aef1458648d2afd281e27ba5a3d143e5925818800faf5e622bc250d3d3a60f518d6583a63b4c7e16d65b4ef39a65e4a057a2c3b6e370a408964621073ba6f300667b6f1575d60f5d322cf642ef70e5cc1f1d7981482633e3b29eb99d2c2eaf62abce6c1c61cd3735ce383cac93be0ec97311acfded14c1ddbec0f5ef581f35ff2ac3d361086e3d5f93e8a370a7ed8953a4173</script> <div class="hbe hbe-content"> <div class="hbe hbe-input hbe-input-default"> <input class="hbe hbe-input-field hbe-input-field-default" type="password" id="hbePass"> <label class="hbe hbe-input-label hbe-input-label-default" for="hbePass"> <span class="hbe hbe-input-label-content hbe-input-label-content-default">Hey, password is required here.</span> </label> </div> </div></div><script data-pjax src="/lib/hbe.js"></script><link href="/css/hbe.style.css" rel="stylesheet" type="text/css">]]></content>
<categories>
<category> 随笔 </category>
<category> 随笔 </category>
</categories>
</entry>
<entry>
<title>AlphaBet Engine</title>
<link href="/posts/alphabet/"/>
<url>/posts/alphabet/</url>
<content type="html"><![CDATA[<div class="hbe hbe-container" id="hexo-blog-encrypt" data-wpm="Oh, this is an invalid password. Check and try again, please." data-whm="OOPS, these decrypted content may changed, but you can still have a look."> <script id="hbeData" type="hbeData" data-hmacdigest="75e4a5b276026043a1c5219bf1b8ceeabd47858b13d2b048386240334003d627">b3dc07a81f6459d120ce338ccca5504603780459e0306826441972681af56d621023a3b1e87082893d7f9f5a9cfdcbb2d60925685910ad1c3f4c7c6703be17e884d98345b93e039e1a24d1a8ebed568715753ccb25d7df8c5c7bae546cb9dd2d7867d1d67996d19fd8b59c04551be490a583a1a45c65d3695787a511f866d3303b0cb5e321ad68da8c1ef4da903d3e648ede58035c1885789bf30ba933ca7e4f14435d2f066a07bf5e0a85980ce41972a9957a1b08d33989c6bbb481850b08c8011435cf746a31c7aa1c540ceb73d5f324e874511482d50b76c6fdeb25c7cc40b23444dfa998fe7d941d124645dcef994f823caa949e964f6c6ca7dd790db5d6273289337aa3cf4e96020fabb36c413aa93d7f8dbada47cc068065082033da5bc32a18b7f9043518cce839f66e6f1da77013912881d1c9da15b53c7edb06fb745bf474543fd299749c4db849f1f39d49a0d52044cf4f305220b72b0f7a0c8df6d39fc0b2f883f756e1ef9b8baa252f48fbd2a56901ced22b0cdec9099b37f293bd34f4ccff931396d57b0fc61a4535fe3c8bbc0ef66fcafb23d87de0be56947237d9bb89ddb050fa33288866c5e8c1a1d51d5d6c74f152e0fa6a7cacb824dc10370b850c541884aa4eb486b6430a92fd0d761cb9d9ca005754aced8c48ca64a0cfc4d690b3dbdf8e94d305ddfe0ce93b3a7b4ed5c5cd41fb2455a611c0f265aec0729761616b8c9bfc6eb1898eec51f3ef378e79a6b3e9add02a4052a05881b50cca21d04bce7158a0547ae526e5f5036bb6219215baf73a9c0613194c202f6e1bc28b62bbc857203209779ad53d641fb8089b40a3d4014f7b99d85866e3b1fb934c28f4f44ee7a07bf62b193b6f68a946d13e61a24a591499ebd4d983bd891b5111cd749957a85822ab7cecea20964c86fb79d298956a8f8956ee85f1385caeec00ec1502e68f9f0696adc88c7466688161c7e01e427acabd1fb584dc52e8becb8736ce328dfaf20732c8bfb0cfead620f7a445684e8ee43159b10a2ed172222c548cb70fc24ad7f48a95bf61603a41677e58f60452d16edc4703afeced57360e37545447936078f32a673c562e62f837bdd5f391c5038b30f13ef14fc5c13be8b9eb2b084ee3526e9859c5da33eddbb639ba349178ec46e48737c41644b6150e4d0337a3fadea4cf15c6e335a2eaf6fef3ec7036f474c0b6bc87f181846bdfa2b8add16e1ff55e0876f2f80df750516dc21175093a79e75f401c04f7a6b5dcbfa622d6713854b33e89ab18e7192cf473be58bbe91dce95b0fbd9190945a648af318acd6365bef13fc74cf6e1c316ca4a18ef3bb44c0bedb330f9b238cd822ea8a128002d405c85f9682efba2b9dbf8e7dcb9cfb2ef2087159f42f59ae0977813e1414d74e04f85a24937d24e8a9f5239b450183ef81e496cd6b099ebfe8fe101ad51f0b66d998a1580ffbcb3aa029e504456aee6c15fd66efcd8b2357479e16a0d049c731a761e6c99da2d7ad6b7d6afcafe8fce034182ac6d53ee37658e6799d05d58a0fae43c83fbcaa4c5be77a7a8e17c6e8724d1b3580556a46987132aeb834015a1615e6130b0748c34af6e6c192b77fe6edeb5f2c9a85235b48e22f0c4835458c30173697b2cbcaf71cf5297ac5c5e474b81e5b8221a4c323b180cfa5822d554af7330272880352ed6686e40f2327a14bdde8d0087bb74e285c5f1d7081a75771a58d2c980671f82b98cdbcdf3309ae3550ba3a30684252fbc2c3095ede8ed2be54a1203652b517041ffd79d186bb52d348a614564085d5400df9c856f736b6bd2c0b594912fd604833dd3d7d74b512a71f391198a00fef547bec560bebe46c0c62c76d38f7e79b8c702fd6fc4d6fa9c27de8f1adbf21f7e4173a10f2e8d087ac680c7edac98d474d89b4f5d21a8cb50d5363b601ebe2155ac376451</script> <div class="hbe hbe-content"> <div class="hbe hbe-input hbe-input-default"> <input class="hbe hbe-input-field hbe-input-field-default" type="password" id="hbePass"> <label class="hbe hbe-input-label hbe-input-label-default" for="hbePass"> <span class="hbe hbe-input-label-content hbe-input-label-content-default">Hey, password is required here.</span> </label> </div> </div></div><script data-pjax src="/lib/hbe.js"></script><link href="/css/hbe.style.css" rel="stylesheet" type="text/css">]]></content>
<categories>
<category> 量化交易 </category>
<category> 量化交易 </category>
</categories>
<tags>
<tag> Python </tag>
</tags>
</entry>
<entry>
<title>日志:2023年9月</title>
<link href="/posts/2023-9/"/>
<url>/posts/2023-9/</url>
<content type="html"><![CDATA[<div class="hbe hbe-container" id="hexo-blog-encrypt" data-wpm="Oh, this is an invalid password. Check and try again, please." data-whm="OOPS, these decrypted content may changed, but you can still have a look."> <script id="hbeData" type="hbeData" data-hmacdigest="cbf1ecc07e1f6a61d57168f47d8b137e542242d8be7f083ef46933f12f70f3f2">b3dc07a81f6459d120ce338ccca550463faa708b9b4d89df9ab14ba4edd809f60b32d15b5672ce84c5d84744f7f475de0b1305a637ea913974dff7410a14ed4dbbb40a5278eb14bbac34e59e12a284c7a294ab69a495d47e11e78ad50feb1070a468302317925759b63d81d1a890d91bb247f88a2922744d34b0dccccece2ea7b156e500c0039cfd827310258914cf02387296f49579859a12f70fb57cd977715c82a3748c321a9a025e128a2cce7f1de3dc03b281972aa543ebeddc996b02a417d9199a94914424bfe81c44ba3eabf2c80e4e270af2ca8407ae2a85ff69b0c1fc536f95c6144a12dfa7ffb8ccdccd84134a688fc70c762745ffb27c1d7f5c04309f944cf5a21383babeb4809120af766173cd3913c8397f6e4c96eb7384f89954fa660cd52a67795cc60cbbdc39c1b58ae0a544dfa11a95645a948f05dae5f8e4a73672048fdc14fc952a5849dd401a3784b6c89bd951f0ab835ac1a4a9f70d444ae242f4dea85b2f9d8d9a7e9305be6ab28cad14ba0c2b7a571fa964999e0ffaa760a2fa7600eff40d891f5e0c65b1fc925a7af5120ff3379ed5731a4194270d519619feed43fd2b6a44c393002a33c3d67c746dee94be0af2ebd54e04a4b20731890f02c5682483b33ee0f28b7da3574f7d08c5f02bdfcf451a36a3333061975b30b4fe8d031b74c782518cab023906e9989aa95522ae03c37da39cda6fde3a576407660e733a7059c3a0f80e97991159b711d583d54f3289b252d70ab0038a7b288566f8903e8861af824a96f2fd0e681502bb84c68c7b026dbc1cdd48f77049005108be76f62fb72f817997c97f0a8f300c100eded8b35f427afd0b17a654dbfa3b8664dd933d00c755cdf46020a148ce26e82760da79b1455507d496518b8d707ff0bd13b0f0cae9e6b9150e6bd32060a04054c6c41140d97ff93653793ffe18ffdf7701d29c0df7b70ac5bd75df406b5d979447871f37bf928d3a76d46f5a28d7d625b444c0ce0e2f3cd6051830a7e1248d45e254e2e93a3f8570ffa7ecdb11e0084db744bbff2c61f2a851c84517e281db403919018b838efe44a580270d83c4ac3adf7aca236dfdb85b7fd93aafc866dc3671e7f15d4182c17043fd91ab23a517451a5e71a719c1324e7e3dfc13edca5bcbdd2c9b5d44300593869ea5e70b4eb2ff3631497ea992cf96107bb632236d00fbdb9c878d90beaaebbc8f45e1759d1ba4c940d200eb660b09a2d7a68b42600a23764e4725996059cd22d90473009bb50ef579822bb481b46b4cea1cf58ae2935fcb9ecbb2a88171e006ff6590583cdd66434631bd5139a8ed79443bb5c9801f184c9c03197e895989864722c465a8c92d88993a6ba26faac43ceac0f9d2522580fc5a24994c852b0e1d47360ca5dc0d1acdbff9c25629eade6fa687009be6397ce3048cf795071d7508e2648a2eb9a3ddb9d5be23570d89bed72fda368d04f427c23915628a2f5059728f3ff6e6c3a87c545465a0faefe26a2386232928017e481e7430a618cbd7c9103d7af6370f88110a6b8b9d62268d1cbff439e51921719ea9251416d227e8ba25874da2394926b328b29b3faca6fca6302468c78a87ef6bc4c01cb9f62a094255ea8246c27b6ce8a36f16e163ca3dc533f8a5f48fb144bffc910c17fc5a85963b6e1b858ac3a3132a05be5a976d78a9e6fb95f5f84845679039f951011c1f52454b4b1cbcc3f8c68549ac3b256431b44b945be0e8717e3a978636dbb454c22a3ba7ee90acc36290bf6dbcc507973d85ee7cce3db7ee3c55b722f813b509f5583b81643b46d9dae38a1eb98a81fe3165d046dc0038d9a2b098352bb655fbb1a7c6ee868de2e0d357748abd976dc89d3d9516473dea8143f4db516ef8c6266fb05d16357670eecf6f2214745bd9aed35dd3bd1612b1840885f22aac29f36731cba2b7c2756c3fdee8b3eda90fbf4b4d487b41128e61d60cf12927750a4b28cf4722a8c936fb7cf1f39a16ba7f0beffe31079dd0f0da462ec44d62270ce4b56fa66293ca95c218faff7b72ef70ef4a42d4ce50a65df10ed61eb32189e6d63bd178a84bef850852aeb50458b826c3fd2857b49428b1b630c83a660b88ed767bc7bd30cca9051f4f1d10ba700e3c969403170fb1372c610cb89a7e247e23f6e17a4a19fb93a0ac559f54e5860c613f6cbf5f52a99ab5493d8fb016cbd948c890e0ce001c3d8649f8a9f6a7f1e9965566fb2da0e5d5147152467dc3441b953aa054ce38a234ba257ddcbeb17f1403224f03a5e68b5ac4036f51b43530a457d19fbb98265858b4822281d07af0a2f205395da54b88dd11529fff47cfad68d842268b66497d90178bf85c43cd1b90048a8c35c8fc177d01f8eb428ae05768673d2dace3e426e2d193aa40c273702653ba8523fa752413ffb2ed2635efbd05be85aa69665531381c78f9a1242f6d49bf8829459b60da404bdcf775465d01daaf39b7b02288a68cae6531459d4cb43673baab5bb4eaa4431fcbd599542f45ef3cba579d3ac2a0beee72ee6143bf869a92a9ed20546b02da8f8824741a7f1e9c6365e19521183e0c3cd6d174e3ce008e0c732d88055f11eaa1efaf15d4c5ff8f0f8b0e441a3496936a181830cc1624f2c4b6a3a7fcaa4f2365297e7c14a9b5f111a8f317120a249afe2dcba85e096c54e561a8031184a8bfda19ee31a5783193dc26ef82f4c8f874638f3136272303f5b52e698f3c116c3e551d4cd811bfb16b2fec613152056dd53548cad1eed03474e6eae3b9f1edf61e2563d8a088bd8819beff6246d5ad831cf1a7a0e5d0cc297a09c71168c074f83a0f11369ee0907b819b86cc3eb91024f97e39b3e2ec1a0bf22cf1cd40bc9dbc86c06ca2a383b92be92c9d74589d4c966e0b6aff543d472add9881655e6559591ffe23738fded6d6bc322a1c127bbf88316ab83357c026e16036d9256869038d1d8bf344203421b3022affd4ded033c4d93d7c57f90ec36c5a7b8cf820bdc8f7f22047068bbcd6c71568ac32bcd20b7c41ce3cc026f8c6c756ed6e45ad43ba20ad54f370019d5ec550d3d4f07ceabc8e020fc37a95efdd85c5a3ed55d822f7441abe3c3fc70d2232fde2dfd7bc0ec50c274b62bc6d97a8931c289af2546e1e9f1fa217b0558fcd5a51133eadda1af7044ef317c241ecee30918013d92f8ee6d26da319a50adb30032d14c7a675c7126b857031bb01ab3896bf291c9f0313d8f0626b7b6e87234ca7c3f02e80b6983a156322dcb24997ff4b057b782058a6bb085b240aabd8186ee9c7e194bbd39639fe548f77a66f02b9f02d74db5246c85798d729bca96eb78e2205520c0fb216f26961e238d47db3bcbb2fd251296bbdaca80b2d83076954021e49a50d59698ebe9cb871a58212dc4b16fe25a926c5aa9b709f74b7f4fb8050eecd589c61ae60bc5abeb4f12ae3159f79596d30f8f3015b3fa8a5c50dc01527284da248747592689bbbad3d8de41bbc2de72772a3152009847e702a1fe23c8375822c9cc9375506fbcc5194c028d57a04f468c3bdc16c19af651e4c825fad715874e0408efe9f89b1ec866d87324c287685820291228168c903c9bc151e6163a4a466f6d16712080916781157dd02d1fe9d0e59c9f0741ad046afec2b2f4dc3c3c526ce3248326501e06aff3574c5d3a5bac9479b3e3584b738c748f53e29dfd7d999134ae922a425eecd06c2c0fb709f1aef9d9df125ddc07c9d02676dc6a705ffe7fa9672f57380fe88c9595ee6e341ffbc7b38b1cfe37c87a2bf1d35c7a770c1608bd1e4448542270913c880a56d372816a30bf8556edf8391bcecb8c89602e996e9958ec84c168e447117a650fba0ab3a297b566d3d5b58751be39798437f2fbb3eedbc03d8bb886f2a03b3e879e5db11708a8930ded16ad669b3dc19e811fecc967da05ee0b2a0b628da60abb45611736c790241679ee46a6bbfd7234aa86cf49f41f984971fe8c23e4e5296748c245ff7dc6711a3813139f69677a74385089d7f5a8ba439be63a72ed81caaed1891586b5ab4dcfea75a252bda347489b172babde966483bc78f8fda30b15edcc176311091f72dc7c6e73172658813784dfbc837e3a283c3cca3ea0d5f41718ade096db607377118b75381d2d9759f210790b8d1f6e5214aaa61d8bb0a4d0a3256aef437eada410c0d34563bcfd4678af647c9d4380af2e82b9e2b3924ff01715d6167e238ac868b5151c2cf48eb3e3c6b11b8369dc99be04c2653eedfc5711f21b48ba8996167b922b512b5d56f484d311251eccc94b7735c75c6735513c6e8cbc0f343a7cfe485dce5af6a7206980141a69dd81aefbbd0c9858428990567e3e2397fc22db911897c79e30de9ec0ed698357598ffd73c5674549d588a091a4377d73ef21201b319ad28ea3798ec1bf2cb3ca074d76ffff0aee2ad816041e6bb381acc79114215db200a8e7c724ecaffb92c20363763841662dd2a7161a9cac3a818d7375a0b4d83217f2056b0e3a1a52e98ced53619aaf537ce552ee0f4c433736752cc26c50ff86a7c402b3ce926cf6a27d2f003168c6e8b46003c967b854817d635c494cfaa2c53be2dbbaf32142dd503b1a45ec027d5314cf92988e3a8fbcd975a0e0c7ee04654</script> <div class="hbe hbe-content"> <div class="hbe hbe-input hbe-input-default"> <input class="hbe hbe-input-field hbe-input-field-default" type="password" id="hbePass"> <label class="hbe hbe-input-label hbe-input-label-default" for="hbePass"> <span class="hbe hbe-input-label-content hbe-input-label-content-default">Hey, password is required here.</span> </label> </div> </div></div><script data-pjax src="/lib/hbe.js"></script><link href="/css/hbe.style.css" rel="stylesheet" type="text/css">]]></content>
<categories>
<category> 个人日志 </category>
<category> 个人日志 </category>
</categories>
<tags>
<tag> C++ </tag>
</tags>
</entry>
<entry>
<title>【AI】chatgpt入门</title>
<link href="/posts/3GDXTCB/"/>
<url>/posts/3GDXTCB/</url>
<content type="html"><![CDATA[<p>OpenAI注册闭坑、GPT Api调用指南<span id="more"></span></p><h3 id="注册-OpenAI">注册 <a href="https://openai.com/">OpenAI</a></h3><p>跟这个教程操作即可:<a href="https://www.awyerwu.com/9829.html">ChatGPT最新注册教程</a></p><p>核心问题是两个:</p><ol><li>借助 VPN 绕过 OpenAI 的IP检测封控,直接挂美国</li><li>借助 <a href="https://sms-activate.org/">sms-activate</a> 接收外国手机的验证码</li></ol><h3 id="调用-GPT-Api">调用 <a href="https://platform.openai.com/docs/api-reference">GPT Api</a></h3><p>开发环境使用 <code>python3</code>,通过 <code>pip3 install openai</code> 安装依赖包。<br>注意生产环境也要挂VPN,下面是一段测试代码:</p><pre><code class="language-python"># -*- coding: utf-8 -*-import openaiopenai.api_key = "***********************"completion = openai.ChatCompletion.create( model="gpt-3.5-turbo-0613", messages=[ { "role": "user", "content": "解释厄尔尼诺现象", } ], temperature = 0.7)print (completion.choices[0].message.content)</code></pre><h3 id="开源推荐">开源推荐</h3><ul><li><a href="https://github.com/zhayujie/chatgpt-on-wechat">chatgpt-on-wechat</a></li></ul><p>微信聊天机器人,支持GPT3.5/GPT4.0/文心一言/讯飞星火模型,支持个人微信、公众号、企业微信,支持文本、语音和图片的处理。</p>]]></content>
<categories>
<category> 工具 </category>
<category> 工具 </category>
</categories>
<tags>
<tag> Python </tag>
</tags>
</entry>
<entry>
<title>【量化】数据专题</title>
<link href="/posts/quant-data/"/>
<url>/posts/quant-data/</url>
<content type="html"><![CDATA[<div class="admonition note"><p class="admonition-title">导读</p><ul><li>交易需要与 <strong>哪些数据</strong> 打交道?</li><li>如何获取 <strong>可靠、实惠</strong> 的数据?</li><li>如何高效地 <strong>存储、读写、计算</strong> 数据?</li></ul></div><div class="markmap-container" style="height:300px"> <svg data="{"t":"root","d":0,"v":"","c":[{"t":"heading","d":1,"p":{"lines":[0,1]},"v":"数据管线","c":[{"t":"heading","d":2,"p":{"lines":[1,2]},"v":"Tick"},{"t":"heading","d":2,"p":{"lines":[2,3]},"v":"Bar"},{"t":"heading","d":2,"p":{"lines":[3,4]},"v":"K-line"}]},{"t":"heading","d":1,"p":{"lines":[4,5]},"v":"<a href=\"#数据类型\">数据类型</a>","c":[{"t":"heading","d":2,"p":{"lines":[5,6]},"v":"基本数据"},{"t":"heading","d":2,"p":{"lines":[6,7]},"v":"市场(行情)数据"},{"t":"heading","d":2,"p":{"lines":[7,8]},"v":"第三方数据"}]},{"t":"heading","d":1,"p":{"lines":[8,9]},"v":"<a href=\"#数据提供商\">数据提供商</a>"},{"t":"heading","d":1,"p":{"lines":[9,10]},"v":"<a href=\"#数据格式\">数据格式</a>"},{"t":"heading","d":1,"p":{"lines":[10,11]},"v":"金融常识","c":[{"t":"heading","d":2,"p":{"lines":[11,12]},"v":"<a href=\"#复权\">复权</a>"},{"t":"heading","d":2,"p":{"lines":[12,13]},"v":"<a href=\"#财报数据\">财报数据</a>"}]},{"t":"heading","d":1,"p":{"lines":[13,14]},"v":"<a href=\"#Pandas\">Pandas</a>","c":[{"t":"heading","d":2,"p":{"lines":[14,15]},"v":"<a href=\"#性能相关\">性能相关</a>"},{"t":"heading","d":2,"p":{"lines":[15,16]},"v":"<a href=\"#内存相关\">内存相关</a>"},{"t":"heading","d":2,"p":{"lines":[16,17]},"v":"<a href=\"#时间相关\">时间相关</a>"}]}],"p":{}}"></svg></div><h2 id="数据管线">数据管线</h2><p>交易数据可以分为三类(从左到右):</p><p><img src="../../images/quant-data-chart.png" alt=""></p><p>换一个角度理解:Bar是Tick数据的重要性采样(有点像光栅化),K-Line是Bar数据的可视化展现(有点像Pixel-Shading)。<br>在这个处理流程中,信息的原貌是不断被丢失的,因此<strong>越原始的数据,价值含量越高</strong>,就像《舌尖上的中国》所说:高端的食材,往往只需最简单的烹饪。</p><p>同时也不能忽略 图形化展示的意义,因为:</p><ul><li><strong>主观交易</strong> 依赖 K线图、技术指标 等作出趋势性、预测性地判断</li><li><strong>量化交易</strong> 往往需要借助 Tick数据 去解读更多的市场微观信息。</li></ul><p><strong>如何理解Tick数据?</strong></p><ul><li>交易所收发交易数据的<strong>最小间隔</strong></li><li>可能是每一笔撮合成交(A股),也可能是每500毫秒的交易快照(商品期货)</li></ul><h2 id="数据类型">数据类型</h2><p>狭义理解的金融数据,大概只有 成交量 和 价格 等关键值,但真正的金融市场是错综复杂、影响纷繁的,需要从如下几个领域考量:</p><ul><li><p><font color="#ef6d3b"><strong>Fundamental Data</strong></font><br><strong>基本面数据</strong>,主要是企业的营收、财报等宏观信息,传统投资领域中的分析师,往往是对着海量的财报作出投资决策的。</p></li><li><p><font color="#ef6d3b"><strong>Market Data</strong></font><br><strong>市场数据</strong>,主要是市值、市盈率、股价、成家量等金融数据,特点是 频率高、时效性强、噪声大,提取有价值信息的难度也非常大。</p></li><li><p><font color="#ef6d3b"><strong>Analytics Data</strong></font><br>(第三方)<strong>分析数据</strong>是很宽泛的概念,可能是机构的研报、社交舆情的数据,甚至是相关政策的颁布、天气信息的变幻等等。特点是获取难度大、归纳提取有效信息难度更大。</p></li></ul><hr><p>考虑到数据获取的难度因素,我们一般基于 <code>Market Data</code> 的数据进行提炼和研究,这部分信息获取公开、透明、平等,且能得到的数据量也是最大的。<br>下面介绍一些常见的市场数据的提供商(获取渠道)。</p><h2 id="数据提供商">数据提供商</h2><p>这里推荐几个具有一定性价比的渠道,相较于个人投资者(爱好者)而言:</p><table><thead><tr><th style="text-align:center">渠道</th><th style="text-align:center">价格</th><th style="text-align:center">准确度</th><th style="text-align:center">覆盖度</th></tr></thead><tbody><tr><td style="text-align:center"><a href="http://baostock.com/">baostock</a></td><td style="text-align:center">免费</td><td style="text-align:center">中</td><td style="text-align:center">A股</td></tr><tr><td style="text-align:center"><a href="http://www.jinshuyuan.net/">金数源</a></td><td style="text-align:center">中</td><td style="text-align:center">高</td><td style="text-align:center">期货、A股</td></tr><tr><td style="text-align:center"><a href="https://shinnytech.com/">天勤量化</a></td><td style="text-align:center">部分免费</td><td style="text-align:center">高</td><td style="text-align:center">期货、A股(18年起)</td></tr><tr><td style="text-align:center"><a href="http://www.juejinshuju.com/">掘金数据</a></td><td style="text-align:center">高</td><td style="text-align:center">高</td><td style="text-align:center">期货、A股、数字货币</td></tr></tbody></table><p>如果你的策略有所起色,甚至扭亏为盈了,后面可以考虑向专业的数据提供商(如<a href="https://www.wind.com.cn/mobile/WDS/zh.html">Wind</a>、同花顺)购买昂贵但准确的市场数据,有句话说得好:贵的东西总有贵的道理!</p><p><strong>再按照股票、期货细分来看:</strong></p><table><thead><tr><th style="text-align:center"></th><th style="text-align:center">频率</th><th style="text-align:center">数据体量</th><th style="text-align:center">推荐数据源</th></tr></thead><tbody><tr><td style="text-align:center">A股 ①</td><td style="text-align:center">5档逐笔</td><td style="text-align:center">1T /年</td><td style="text-align:center">tqsdk白嫖</td></tr><tr><td style="text-align:center">1990年~2023年</td><td style="text-align:center">1 min</td><td style="text-align:center">25G /年</td><td style="text-align:center">tqsdk白嫖</td></tr><tr><td style="text-align:center"></td><td style="text-align:center">5 min</td><td style="text-align:center">5G /年</td><td style="text-align:center">baostock免费</td></tr><tr><td style="text-align:center"></td><td style="text-align:center">日k</td><td style="text-align:center">累计 2G</td><td style="text-align:center">baostock免费</td></tr><tr><td style="text-align:center">期货 ②</td><td style="text-align:center">tick</td><td style="text-align:center">单品种 1G /年</td><td style="text-align:center">taobao购买 / tqsdk</td></tr><tr><td style="text-align:center">2016年~2023年</td><td style="text-align:center">1 min</td><td style="text-align:center">累计 1.5G</td><td style="text-align:center">tqsdk</td></tr><tr><td style="text-align:center"></td><td style="text-align:center">5 min</td><td style="text-align:center">累计 0.3G</td><td style="text-align:center">tqsdk</td></tr><tr><td style="text-align:center"></td><td style="text-align:center">日k</td><td style="text-align:center">-</td><td style="text-align:center"></td></tr><tr><td style="text-align:center">数字货币</td><td style="text-align:center">tick</td><td style="text-align:center"></td><td style="text-align:center">掘金数据</td></tr><tr><td style="text-align:center"></td><td style="text-align:center">1 min</td><td style="text-align:center"></td><td style="text-align:center">掘金数据</td></tr><tr><td style="text-align:center"></td><td style="text-align:center">5 min</td><td style="text-align:center"></td><td style="text-align:center">掘金数据</td></tr></tbody></table><ul><li>① <strong>国内主要上市股票约 5000 只</strong>,平均上市时间 11.9 个年份(截至发文日期 2023年10月)<ul><li><a href="http://www.sse.com.cn/market/stockdata/statistic/">上交所 2288只</a> 主板(1727),科创板(561)</li><li><a href="https://www.szse.cn/market/">深交所 2827只</a> 主板(1506),创业板(1321)</li><li>首支上市日期为 1990年 平安银行</li></ul></li><li>② <strong>国内商品期货、金融期货等,约 80 个种类</strong></li></ul><h2 id="数据格式">数据格式</h2><p>推荐阅读:<a href="/posts/data-perf/">csv, hdf5, feather 三种数据性能对比</a></p><h2 id="复权">复权</h2><p>在理解为什么要复权之前,先理解几个金融市场的基本概念:</p><ul><li><p><strong>分红:每10股派发6元</strong><br>本质是将股票市值中的6元兑换成现金,发放到你的账户,等同于套现</p></li><li><p><strong>拆股:每1股拆分为5股</strong><br>本质是因为股价过高作拆分,单只股票价格也会变成五分之一</p></li></ul><p>金融数据中的市场价格(包括开盘价、收盘价),往往都是不考虑分红、拆股的背景条件,因此经常见到股价突然腰斩 <code>90%</code> 的情况,其实并不是股价跌这么多,而是因为该上司公司拆股了。</p><p>因此,复权价格就是为了<strong>抹除非市场因素带来的涨跌,让价格保持平滑、连续性</strong></p><div class="admonition note"><p class="admonition-title">前复权和后复权</p><ul><li>前复权:以 <strong>第一天</strong> 的价格为基准,推算后面的价格</li><li>后复权:以 <strong>最后一天</strong> 的价格为基准,推算之前的价格</li><li><a href="https://www.zhihu.com/question/31004373">知乎: 通俗易懂的解释前复权,不复权和后复权有什么区别?</a></li></ul></div><h2 id="财报数据">财报数据</h2><p><strong>核心是区分毛利润和净利润</strong></p><ul><li>毛利润(gross profit) = 收入 - 生产成本</li><li>净利润(net income)= 毛利润 - 销售/管理/研发/财务成本 - 税收</li></ul><p>以白酒行业为例,假设一瓶售价为880的白酒,其原材料成本是80元,则其毛利率为 <code>800/880=91%</code>。观察国内相关上司企业,就能够发现 <code>90%+</code> 的毛利率是普遍现象。<br>但由于销售成本(如广告)和人工成本的存在,其真实的净利率往往在 <code>50%</code> 以下。</p><p><strong>指标的含义?</strong></p><ul><li>毛利润衡量的是<strong>产品价值</strong>,毛利率高,说明这是一门好生意(白酒、互联网)</li><li>净利润衡量的是<strong>企业价值</strong>,净利率高,说明其赚钱能力强(九安医疗 …)</li></ul><div class="admonition warning"><p class="admonition-title">留几个没想明白的问题</p><ol><li>对于没有实体的 <strong>互联网行业</strong>,如何衡量其生产成本?</li><li>毛利润是否扣除 <strong>员工工资</strong>?</li><li><strong>为什么要统计毛利率?</strong> 光有净利率不足够吗?</li></ol></div><h2 id="Pandas">Pandas</h2><p><a href="https://pandas.pydata.org/">Pandas</a> 是 应用最广泛的 Python数据处理库,在量化交易、数据清洗中非常重要。</p><h4 id="性能相关">性能相关</h4><ul><li><a href="https://numba.readthedocs.io/en/stable/user/5minguide.html"><font color="#1A9BFF"><em><strong>numba</strong></em></font></a></li><li>读取较多个csv文件耗时较长, 如何用 <font color="#1A9BFF"><em><strong>multiprocess + pandas</strong></em></font> 读取?</li></ul><pre><code class="language-python">from multiprocessing import Pooldef read_csv(file_name): return pandas.read_csv(file_name)file_list = [...]with Pool(processes=6) as pool: df_list = pool.map(read_csv, file_list) df_all = pd.concat(df_list, ignore_index=True) """ 推荐在read_csv里将数据写到一个全局的dict """</code></pre><h4 id="内存相关">内存相关</h4><p>为了节省runtime内存, DataFrame默认读取的是<code>float64</code> & <code>int64</code>格式, 占用内存大且浪费</p><ul><li><code>np.dtype('int32')</code>: 表示int32类型</li><li><code>np.iinfo('int64').max</code>: 获取int64的最大值</li><li><code>np.finfo('float64').max</code>: float64</li><li><code>int64: 64 bits = 8 byte</code></li></ul><p>建议如下:</p><ul><li>不考虑负数, 用uint代替int</li><li>能用<code>int16</code>, 就不要用<code>int32</code></li><li><font color="#FF1E10"><strong>为什么<code>float64</code>比<code>int64</code>表示范围大? 占内存是一样的</strong></font></li></ul><h4 id="时间相关">时间相关</h4><p>这里要写很多篇幅, 先介绍Pandas自带的转化:<br><font color="#FF1E10"><strong><code>TODO</code></strong></font><br><code>pandas.to_datetime(df)</code>: 返回类型是pandas的timestamp, 可以访问.date(), .day</p><h4 id="注意项">注意项</h4><ul><li><p>读取中文报错: <code>pd.read_csv(file_name, encoding = "gbk")</code></p></li><li><p>UserWarning: Pandas doesn’t allow columns to be created via a new attribute name</p><ul><li>正确写法: <code>df['name'] = xxx</code></li><li><s>错误写法: <code>df.name = xxx</code></s></li></ul></li><li><p>Pandas Warning:</p></li></ul><pre><code class="language-python">SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame</code></pre><p>一般是链式赋值时会报,根本原因是尝试对 copy 出来的 DataFrame 尝试去赋值。<br>下面是一个典型的例子:</p><pre><code class="language-python">df.A.loc[df.B > 100] = 0 # ×df.loc[df.B > 100, 'A'] = 0 # √</code></pre><h4 id="复杂操作">复杂操作</h4><ul><li><p><strong>groupby</strong></p><ul><li><a href="https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.groupby.html?highlight=groupby#pandas.DataFrame.groupby">Pandas Groupby</a></li><li><a href="https://zhuanlan.zhihu.com/p/101284491">知乎</a></li><li><code>DataFrame.groupby(by=None)</code>: 按照by这一column筛选</li><li><code>Group.get_group()</code>: 获取指定的group, 返回DataFrame</li></ul></li><li><p><strong>merge</strong></p><ul><li>DataFrame, Series之间任意合并</li><li>注意<code>left</code>, <code>right</code>, <code>outer</code>等几种方式, 底层就是<code>SQL</code>的逻辑</li><li>merge完赋值如果不对齐,可以去重: <code>mf = mf[~mf.index.duplicated()]</code></li></ul></li></ul>]]></content>
<categories>
<category> 量化交易 </category>
<category> 量化交易 </category>
</categories>
<tags>
<tag> Python </tag>
</tags>
</entry>
<entry>
<title>【名人访谈】BBC采访马斯克</title>
<link href="/posts/2YS1Y71/"/>
<url>/posts/2YS1Y71/</url>
<content type="html"><![CDATA[<div class="admonition note"><p class="admonition-title">导读</p><ul><li><a href="https://www.youtube.com/watch?v=donC2VuVTtM"><strong>Youtube采访视频 2023年4月21日</strong></a></li><li><a href="https://www.bilibili.com/video/BV18F411y7ac"><strong>B站解说视频</strong></a></li><li>马斯克的核心技能:屌爆的思维逻辑 + 烂熟的辩论技巧</li><li>这一派还有个大佬是罗永浩,可以看他锤王自如的视频</li><li><font color="#FF1E10"><strong>TODO:</strong></font> 文章排版不满意,等录个视频锻炼口头表达</li></ul></div><h2 id="采访背景">采访背景</h2><h3 id="马斯克方面">马斯克方面</h3><blockquote><p>阅读材料:<a href="https://zh.wikipedia.org/zh-hans/%E5%9F%83%E9%9A%86%C2%B7%E9%A9%AC%E6%96%AF%E5%85%8B%E6%94%B6%E8%B4%AD%E6%8E%A8%E7%89%B9%E6%A1%88">维基百科 埃隆·马斯克收购推特案</a></p></blockquote><p>马斯克与 <strong>2022年10月</strong> 以 <strong>440亿美金</strong> 价格,收购美国社交巨头 <em><strong>Twitter</strong></em>,其过程经历三个阶段:</p><ul><li>【提出】马斯克提出收购,遭到Twitter和市场反对</li><li>【后悔】马斯克发现Twitter用户数据造假,尝试放弃收购计划</li><li>【被迫】马斯克迫于法律诉讼,被迫收购Twitter</li></ul><p>收购 <em>Twitter</em> 后,马斯克主要实行四大措施:</p><ul><li>大量裁员(8000人->1500人)</li><li>开源 <em>Twitter</em> 内容推荐算法</li><li>删除垃圾机器人</li><li>退出收费认证服务</li></ul><h3 id="BBC方面">BBC方面</h3><p>其次,BBC的采访素来以 <strong>尝试刁难采访者,角度狠辣,制造爆点话题</strong> 为主,围绕收购案本身,(从BBC角度)有如下几点值得 <strong>埋坑</strong>:</p><ol><li>【道德角度】接手公司后大量裁员</li><li>【媒体角度】Twitter充斥越来越多的虚假信息</li><li>【决策角度】收费认证被所有人吐槽</li></ol><p>所以,这次采访不是一个歌颂丰功伟绩的 “单口相声”,而更像是互相博弈的 “双人对线”,可以说火药味十足。</p><h2 id="采访正题">采访正题</h2><ul><li><font color="#ef6d3b"><strong>❓BBC: 聊聊Twitter收购案</strong></font></li></ul><p>这里马斯克谈了两点,【1】为什么停止收购,【2】强调最终收购是迫不得已。</p><blockquote><p>在谈【1】时,马斯克举了一个非常形象的例子:</p><ul><li>你想购买一袋大米,本来约定允许10%的米是坏的,最后发现30%的米都是坏的。这显然无法接受。</li></ul></blockquote><ul><li><font color="#ef6d3b"><strong>❓BBC【进攻三连】:1. 你解雇大量员工,2. 你裁员的行为很随意,3. 你毫无同情心</strong></font></li></ul><p>这里有三个观点,分别是【事实】->【表象】->【推断】,层层推进,层层致命。</p><blockquote><p><strong>马斯克的回复很经典</strong>,其实核心是马保国的 <strong>接化发</strong> (太极):</p><ul><li>【接】:部分认同,但留有余旋 (Musk:确实裁员了…)</li><li>【化】:转移到对自己有利的话题 (Musk:公司账面只能4个月,不裁员所有人都死…)</li><li>【发】:用刁钻假设,反问对方(Musk:换做你怎么做? <a href="https://zhuanlan.zhihu.com/p/338604183">有轨电车难题</a>…)</li></ul></blockquote><ul><li><font color="#ef6d3b"><strong>❓BBC【换角度进攻】:你是世界首富,为什么不自己掏钱帮自己公司?</strong></font></li></ul><blockquote><p>这里马斯克没有技巧,全靠<strong>真诚+卖惨</strong>:我贱卖了很多特斯拉的股票才能买下Twitter(别再道德绑架俺…)</p></blockquote><ul><li><font color="#ef6d3b"><strong>❓BBC【问句埋坑】:你是否后悔裁员?</strong></font></li></ul><p>这是记者经典的疑问圈套,不论回答是否都是下策。</p><blockquote><p>回答是:马斯克认错了!亲口承认裁员不明智<br>回答否:马斯克心狠手辣!裁员毫无愧疚</p></blockquote><p>因此马斯克直接不回答该问题,而是侧面讲了两个自己的观点:</p><blockquote><p>【1】公司有自己的运转规律<br>【2】卖特斯拉股票很困难,它还导致其市值暴跌(寻求弱势低位)</p><ul><li>但如果光说第一点还不够,因为是 <strong>贱卖股票</strong> 让马斯克把自己放到了一个弱势地位,有效阻止记者继续纠缠。</li></ul></blockquote><ul><li><font color="#ef6d3b"><strong>❓BBC【聊政治,埋坑】:Twitter被你收购后解封了Trump,他何时回归?</strong></font></li></ul><p>政治是敏感话题,在西方也是如此。因此马斯克直截了当地说 <strong>不知道</strong>。<br>到这里还没完,高手厉害之处就是,<strong>抓住任何机会宣传自己的企业</strong>,于是他说:</p><blockquote><p>我在选举投了Biden,但解封了Trump,说明 <strong>Twitter是自由发声的地方</strong></p></blockquote><p>牛逼!但BBC也是高手,顺着自由发声的话题,立刻谈到Twitter的一些负面问题 ↓</p><ul><li><font color="#ef6d3b"><strong>❓BBC【开始抨击】:Twitter强调言论自由,是否助长错误信息(言论)?</strong></font></li></ul><p>再一次经典的疑问圈套,马斯克作为高手,自然不会落入俗套。他直接反问记者:<strong>谁定义错误信息?</strong>,<strong>BBC难道没有发布过错误信息?</strong></p><ul><li><font color="#ef6d3b"><strong>❓BBC【开始抨击】:Twitter裁掉整个内容审核部门,是否助长仇恨言论?</strong></font></li></ul><blockquote><p>这里介绍一个背景,大部分的社交媒体,都是通过人工(为主)+AI(为辅)过滤仇恨言论(如政治、宗教、法律),但马斯克背其道而行之(裁掉部门)。</p></blockquote><p>马斯克仍然 <strong>以反问起手</strong>:什么是仇恨言论?你用过Twitter吧(必然用过)。那举一个你见过的仇恨言论的例子。</p><blockquote><p>【若记者举了】可以逐点反驳击破,因为很多仇恨言论是片面的,如LSBT,且没法在公开场合说<br>【若记者不举】根据 “谁主张 谁举证” 的规则,其不攻自破</p></blockquote><h2 id="马斯克传达的观念">马斯克传达的观念</h2><p><strong>俗手总是去证明自己,高手往往是在表达自己</strong></p><p>抛开辩论技巧,大佬传达的观念也是值得学习的,马斯克在一个小时的采访中主要传达了这几个观点:</p><ul><li><p>【方法论】想生存? 降本增效</p><ul><li>一方面裁员,一方面裁设备(虽然导致服务崩溃)</li><li>拉回旧的广告商,提高收入</li></ul></li><li><p>【方法论】开源核心算法</p><ul><li>就像餐厅把自己的后厨公开到幕前。</li><li>只有公开透明的算法,才能让民众感到安心(尤其是社交领域)</li></ul></li><li><p>【价值观】社交媒体的意义</p><ul><li>马斯克不在乎赚钱(前提是企业能活下去)</li><li>好的社交媒体,是人们信赖的真相的来源,且人们会自发去评判和追求事物的真相</li></ul></li></ul>]]></content>
<categories>
<category> 随笔 </category>
<category> 随笔 </category>
</categories>
</entry>
<entry>
<title>【C++11】lvalue & rvalue (references)</title>
<link href="/posts/rvalue/"/>
<url>/posts/rvalue/</url>
<content type="html"><![CDATA[<p><code>C++</code> 左值、右值引用<span id="more"></span></p><div class="admonition note"><p class="admonition-title">导读</p><ul><li><a href="https://www.internalpointers.com/post/understanding-meaning-lvalues-and-rvalues-c"><em><strong>Understanding the meaning of lvalues and rvalues in C++</strong></em></a></li><li><a href="https://www.internalpointers.com/post/c-rvalue-references-and-move-semantics-beginners"><em><strong>C++ rvalue references and move semantics for beginners</strong></em></a></li><li><a href="https://www.cprogramming.com/c++11/rvalue-references-and-move-semantics-in-c++11.html"><em><strong>Move semantics and rvalue references in C++11</strong></em></a></li></ul></div><div class="markmap-container" style="height:200px"> <svg data="{"t":"root","d":0,"v":"","c":[{"t":"heading","d":1,"p":{"lines":[0,1]},"v":"左值和右值","c":[{"t":"heading","d":2,"p":{"lines":[1,2]},"v":"<a href=\"#左值-→-右值\">左值 → 右值</a>"},{"t":"heading","d":2,"p":{"lines":[2,3]},"v":"<a href=\"#右值-→-左值\">右值 → 左值</a>"}]},{"t":"heading","d":1,"p":{"lines":[3,4]},"v":"右值引用"},{"t":"heading","d":1,"p":{"lines":[4,5]},"v":"<a href=\"#move语义-🔥\">move语义</a>","c":[{"t":"heading","d":2,"p":{"lines":[5,6]},"v":"<a href=\"#std-move\">std::move</a>"},{"t":"heading","d":2,"p":{"lines":[6,7]},"v":"<a href=\"#std-remove-reference\">std::remove_reference</a>"}]},{"t":"heading","d":1,"p":{"lines":[7,8]},"v":"<a href=\"#三种传参\">三种传参</a>","c":[{"t":"heading","d":2,"p":{"lines":[8,9]},"v":"<a href=\"#1-const-T\">const T</a>"},{"t":"heading","d":2,"p":{"lines":[9,10]},"v":"<a href=\"#2-const-T\">const T&amp;</a>"},{"t":"heading","d":2,"p":{"lines":[10,11]},"v":"<a href=\"#3-T\">T&amp;&amp;</a>"}]}],"p":{}}"></svg></div><h2 id="前言">前言</h2><p>从接触、学习、运用 <code>C++</code> 至今,左右值引用一直是自己困惑的点。伴随着现代C++的发展,它们开始扮演越来越重要的作用(如 <code>std::move</code>、<code>std::remove_reference</code>…)。<br>这篇争取彻底搞懂他们。</p><p>先看 <code>gcc</code> 一个编译报错,为什么 <code>666 = x</code> 的语法是错误的?<br><em><font color="#FF1E10"><strong>error:</strong></font> lvalue required as left operand of assignment</em></p><p>编译器是在说:<strong>赋值符号 <code>=</code> 的左操作数,必须是左值 <code>lvalue</code> ! 换句话说,这里的 <code>666</code> 不是一个左值。</strong></p><pre><code class="language-c">int x;666 = x;</code></pre><h2 id="左值和右值">左值和右值</h2><p>如何区分 左值 和 右值?</p><ul><li><strong><code>lvalue</code>:指向明确的内存地址</strong>,又称 <code>variable</code></li><li><strong><code>rvalue</code>:没有明确的内存地址</strong>,又称 <code>literal constant</code></li></ul><p>下面看几个示例:</p><ul><li><code>int x = 666</code>:<code>x</code> 是 <code>lvalue</code>,<code>666</code> 是 <code>rvalue</code></li><li><code>int* y = &x</code>:<code>x</code> 是 <code>lvalue</code>,<code>y</code> 是 <code>lvalue reference</code></li></ul><p>编译规则,赋值<code>=</code> 和取地址<code>&</code> 的左边必须是 <code>lvalue</code>,不然会报如下错误:</p><blockquote><p><em><font color="#FF1E10"><strong>error:</strong></font> lvalue required as left operand of assignment</em><br><em><font color="#FF1E10"><strong>error:</strong></font> lvalue required as unary ‘&’ operand`</em></p></blockquote><div class="admonition warning"><p class="admonition-title">区分 左值 和 左值引用</p><ul><li><code>int x = 1</code>:x是左值</li><li><code>int& y = x</code>:y是左值引用</li></ul></div><h3 id="function-reference">function reference</h3><p>函数的返回值可以是 左值,也可以是 右值。</p><ul><li>右值 ×</li></ul><pre><code class="language-c">int setValue() { return 6; };setValue() = 3; // error: lvalue required as left operand of assignment</code></pre><ul><li>左值 √</li></ul><pre><code class="language-c">int x = 100;int& setValue() { return x; };setValue() = 1;</code></pre><h2 id="左值-→-右值">左值 → 右值</h2><p>左值 经常会被转化为 右值,如下示例:</p><ul><li><code>x, y</code> 都是 左值</li><li><code>x + y</code> 被转化为 右值</li></ul><pre><code class="language-c">int x = 1;int y = 3;int z = x + y; // ok</code></pre><p>上面经历了一次 <strong>lvalue -> rvalue</strong> 的隐式转换,很多操作符(<code>+, -, /</code>)都会提供。</p><h2 id="右值-→-左值">右值 → 左值</h2><p>右值 到 左值 的转换是被禁止的,如下代码是非法的:</p><pre><code class="language-c">int& x = 10;// error: cannot bind non-const lvalue reference of type 'int&' to an rvalue of type 'int'</code></pre><h2 id="右值引用-🔥">右值引用 🔥</h2><p>C++ 的一条重要编译规则是:<strong>你无法绑定一个 右值 的地址,除非绑定到一个 <code>const</code> 类型</strong>,例如:</p><pre><code class="language-c">int& x = 666; // error: cannot bind non-const lvalue reference of type 'int&' to an rvalue of type 'int'const int& x = 666; // OKstd::string s1 = "Hello ";std::string s2 = "world";const std::string& s3 = s1 + s2;s3 += " luhao"; // error: no match for 'operator+=' (operand types are 'const std::string'</code></pre><p>但是上面的写法有个弊端,<strong>即无法再修改 <code>s3</code> 的值</strong>。<br>为了能够修改右值(即临时变量),<code>C++11</code> 正式引入右值引用(<code>rvalue reference</code>),其符号是 <code>&&</code>:</p><pre><code class="language-c">std::string s1 = "Hello ";std::string s2 = "world";std::string&& s3 = s1 + s2;s3 += " luhao"; // OK</code></pre><p>上面的示例看出来用处不大,<strong>因为 <code>rvalue reference</code> 真正大展拳脚的地方,是在 移动语义(<code>move semantics</code>)。</strong></p><h2 id="move语义-🔥">move语义 🔥</h2><p>阅读资料</p><ul><li><a href="https://www.cprogramming.com/c++11/rvalue-references-and-move-semantics-in-c++11.html"><em><strong>Move semantics and rvalue references in C++11</strong></em></a></li></ul><p><strong>移动语义 是一种利用右值引用的技术,来避免拷贝临时变量的优化手段。</strong></p><h3 id="为什么需要-move-semantics-💡">为什么需要 move semantics? 💡</h3><p>假设 <code>class Holder</code> 是一个(内存)非常繁重的类,考虑到如下的构造和拷贝构造函数。<br>当调用 <code>Holder h1(h)</code> 时,因为 <code>std::copy</code> 造成巨大的内存拷贝开销,如果后文中 <code>h</code> 也不再继续使用,为什么不尝试将 <code>h</code> 转交给 <code>h1</code> 呢?</p><pre><code class="language-c">class Holder{public: Holder(int size) { m_data = new int[size]; m_size = size; } Holder(const Holder& other) { m_data = new int[other.m_size]; std::copy(other.m_data, other.m_data + other.m_size, m_data); m_size = other.m_size; } ~Holder() { delete[] m_data; }private: int* m_data; size_t m_size;}int main(){ Holder h(10000); Holder h1(h); // 调用 std::copy 带来非必要开销 return 1;}</code></pre><p>借助移动语义,可以优化掉上面的拷贝。注意到下面使用了 <a href="#std-move"><em><strong>std::move</strong></em></a>,它能将左值转化为右值,是C++标准库的成员函数,后面有介绍。</p><pre><code class="language-c">Holder(Holder&& other){ // 赋值 m_data = other.m_data; m_size = other.m_size; // 清空other的状态 other.m_data = nullptr; other.m_size = 0;}int main(){ Holder h(10000); Holder h1(std::move(h)); return 1;}</code></pre><h3 id="std-move">std::move</h3><p>阅读材料:</p><ul><li><a href="https://en.cppreference.com/w/cpp/utility/move"><em>cppreference</em></a></li><li><a href="https://gcc.gnu.org/onlinedocs/libstdc++/libstdc++-api-4.5/a00936_source.html"><em>libstdc++: move.h</em></a></li></ul><p>阅读 <code>std::move</code> 的源码,其实只是作了类型转化,将 任意形式的<code>_Tp</code> 转化成右值:</p><ul><li><code>std::remove_reference</code>:去掉引用</li><li><code>static_cast</code>:隐式转换</li></ul><div class="admonition note"><p class="admonition-title">std::move</p><ul><li><code>move</code> 右值:直接返回</li><li><code>move</code> 左值:转成右值,并返回</li></ul></div><pre><code class="language-c"> /** * @brief Convert a value to an rvalue. * @param __t A thing of arbitrary type. * @return The parameter cast to an rvalue-reference to allow moving it. */ template<typename _Tp> constexpr typename std::remove_reference<_Tp>::type&& move(_Tp&& __t) noexcept { return static_cast<typename std::remove_reference<_Tp>::type&&>(__t); }</code></pre><h3 id="std-remove-reference">std::remove_reference</h3><p><code>remove_reference</code> 顾名思义,就是去除任意类型的引用,借助模板实现。<br>核心是对于 <code>_Tp&</code> 和 <code>_Tp&&</code> 这两种带引用的传参,需要去掉其引用的部分,只获取其类型(通过 <code>::type</code> 获取)</p><pre><code class="language-c"> template<typename _Tp> struct remove_reference { typedef _Tp type; }; // 特化 template<typename _Tp> struct remove_reference<_Tp&> { typedef _Tp type; }; // 特化 template<typename _Tp> struct remove_reference<_Tp&&> { typedef _Tp type; };</code></pre><h2 id="三种传参">三种传参</h2><h3 id="1-const-T"><code>1. const T</code></h3><p>常量值传递,默认有一次拷贝开销。<br>如果是 <code>builtin-types (int、float ...)</code> 推荐使用这种传递方式</p><h3 id="2-const-T"><code>2. const T&</code></h3><p>常量引用传递,<code>&</code> 避免拷贝带来的开销,<code>const</code> 避免被修改。<br>但会将生命周期延续</p><h3 id="3-T"><code>3. T&&</code></h3><p>右值传递,避免拷贝带来的开销,推荐复杂结构体如 <code>std::vector ...</code><br>缺点是调用者必须传入右值,否则编译期间报错,如果是通用接口比较难受</p>]]></content>
<categories>
<category> C++ </category>
<category> C++ </category>
</categories>
<tags>
<tag> C++ </tag>
</tags>
</entry>
<entry>
<title>【C++17】refl-cpp</title>
<link href="/posts/refl-cpp/"/>
<url>/posts/refl-cpp/</url>
<content type="html"><![CDATA[<p>品读<code>C++</code>经典反射库<span id="more"></span></p><div class="admonition note"><p class="admonition-title">导读</p><ul><li>源码:<a href="https://github.com/veselink1/refl-cpp"><em><strong>veselink1/refl-cpp</strong></em></a></li><li>blog:<a href="https://veselink1.github.io/blog/cpp/metaprogramming/2019/07/13/refl-cpp-deep-dive.html"><em>refl-cpp — A deep dive into this compile-time reflection library for C++</em></a></li><li>这篇博客大致是英文版的 直译 + 自己理解,旨在提高对 <code>templates</code> + <code>reflections</code> 的掌握</li><li>精读blog ➜ 理解源码 ➜ 上手仿造</li></ul></div><h2 id="目录">目录</h2><ul><li><a href="#%E5%89%8D%E8%A8%80">前言</a></li><li><a href="#compile-time%E5%8F%8D%E5%B0%84">compile-time反射</a></li><li><a href="#%E6%9E%84%E5%BB%BA%E7%B1%BB%E7%9A%84%E6%88%90%E5%91%98">构建类的成员</a></li><li><a href="#%E4%BD%BF%E7%94%A8-macros-%E7%BB%84%E7%BB%87%E4%BB%A3%E7%A0%81">使用 <code>macros</code> 组织代码</a></li><li><a href="#%E5%87%BD%E6%95%B0%E5%8F%8D%E5%B0%84">函数反射</a></li><li><a href="#%E9%81%8D%E5%8E%86%E7%B1%BB%E7%9A%84%E6%88%90%E5%91%98">遍历类的成员</a></li></ul><h2 id="前言">前言</h2><p><code>refl-cpp</code> 的设计初衷是:</p><ul><li>支持 在 <strong><code>C++17</code></strong> 及更高版本 <strong>提供编译期反射(Compile-time)</strong> 的方法。</li><li>支持 <code>enumeration</code>,<code>introspection</code><ul><li><code>enumeration</code>:类似 <code>Python dir()</code> 枚举对象所有的属性</li><li><code>introsection</code>:类似 <code>Python getattr</code> 访问对象的指定属性</li></ul></li><li>支持 类型模板、成员模板</li><li>支持 <code>attributes</code> <font color="#FF1E10"><strong>TODO</strong></font></li></ul><p><code>refl-cpp</code> 的设计避免如下:</p><ul><li>避免 使用宏魔法</li><li>避免 <code>Private</code> 私有成员的反射</li><li>避免 运行时 按名称查询类型信息</li></ul><h2 id="compile-time反射">compile-time反射</h2><p>首先 <code>refl-cpp</code> 是一个 <code>compile-time</code> 的反射库,这意味着它不会维护一个 runtime 的数据结构来实现反射目的,例如下面是不可取的:</p><pre><code class="language-c">struct TypeInfo { std::string name; // 对象的类型名称 std::vector<?> members; // 对象的所有成员 std::vector<?> attributes; // 对象的所有成员取值}// ↓ 维护一个全局的反射数据结构std::unordered_map<std::string, TypeInfo> s_typeRegistry;</code></pre><p>相反,<code>refl-cpp</code> 的做法是,通过 cpp模板特化 以一种类相关的方式(<code>type-dependent</code>)来存储 <code>metadata</code>,例如下面的做法:</p><pre><code class="language-c">template <typename> struct TypeInfo {};// ↓ Point类的编译期信息template <>struct TypeInfo<Point> { static constexpr char name[] = "Point"; ??? members = {}; ??? attributes = {};};</code></pre><h2 id="构建类的成员">构建类的成员</h2><p>上一节提供了存储 类信息 的方法,但是如何存储 其成员变量(和方法)呢?<br><code>refl-cpp</code> 使用一种新颖的方式来存储:</p><pre><code class="language-c">template <size_t N>struct MemberInfo;/* 第0个成员的模板特化 */template <>struct MemberInfo<0> { /* ... */};/* 第1个成员的模板特化 */template <>struct MemberInfo<1> { /* ... */};static constexpr size_t MemberCount = 2;</code></pre><p><code>MemberInfo</code> 是类成员的模板特化,因此将其添加到 <code>TypeInfo</code> 的作用域(如下)。<br><code>typename Dummy</code> 是因为C++不允许成员完全模板特化,而部分成员的模板特化是允许的。<font color="#FF1E10"><strong>TODO</strong></font></p><pre><code class="language-c">template <>struct TypeInfo<Point> { template <size_t N, typename Dummy> struct MemberInfo; /* 第0个成员的模板特化 */ template <typename Dummy> struct MemberInfo<0> { /* ... */ }; /* 第1个成员的模板特化 */ template <typename Dummy> struct MemberInfo<1> { /* ... */ }; static constexpr size_t MemberCount = 2;};</code></pre><h2 id="使用-macros-组织代码">使用 <code>macros</code> 组织代码</h2><p>上一节提供了粗略的 类 + 类成员 的反射方案,那么如何声明它们呢?<br>答案是借助 <code>macros</code> 实现(作者不是不建议使用宏么😂…)</p><ul><li><code>__COUNTER__</code> 是非标准库的宏,每次调用增加<code>1</code>,且从<code>0</code>开始</li><li>宏展开后的代码,可看示例:<a href="https://gist.github.com/veselink1/f4e2fa94bda0514631753f13a9b93f9b"><em>refl-cpp-deep-dive-5-generated.cpp</em></a></li><li>另外每个 <code>TypeInfo</code> 和 <code>MemberInfo</code> 还应该包含如下内容:<ul><li><code>static constexpr char name[] = …</code></li><li><code>static constexpr std::tuple<…> attributes = {…}</code></li><li><code>static constexpr auto* pointer = &Type::MemberName</code></li></ul></li></ul><pre><code class="language-c">template <typename T>struct TypeInfo {};#define REFLECT_TYPE(TypeName) \ template<> struct TypeInfo<TypeName> { \ template <size_t, typename> struct MemberInfo; \ static constexpr size_t MemberIndexOffset = __COUNTER__ + 1; #define REFLECT_FIELD(FieldName) \ template <typename Dummy> struct MemberInfo<__COUNTER__ - MemberIndexOffset> \ {}; \#define REFLECT_END \ static constexpr size_t MemberCount = __COUNTER__ - MemberIndexOffset; \ };// Usage:REFLECT_TYPE(Point) REFLECT_FIELD(x) REFLECT_FIELD(y)REFLECT_END</code></pre><div class="admonition note"><p class="admonition-title">Tips: 借助 VisualStudio 查看宏展开</p><ul><li>鼠标悬停在宏上,点击 <code>Expand Inline</code><img src="../../images/vs-macros-expand.png" alt=""></li></ul></div><h2 id="函数反射">函数反射</h2><p><code>refl-cpp</code> 还提供反射函数的功能。</p><p>为了区分成员(是变量还是方法),每个 <code>MemberInfo</code> 有一个公共的 <code>typedef</code>,它等同于 <code>refl::members::field</code> 和 <code>refl::members::function</code> 两者之一。而考虑到 函数的重载和模板,这部分功能(相对于反射成员)会更加复杂。</p><p><code>refl-cpp</code> 通过如下方法:<font color="#FF1E10"><strong>TODO</strong></font></p><pre><code class="language-c">template <typename R, typename... Args>auto resolve(R(*fn)(Args...), Args&&... args) -> decltype(fn);/* Imagine 12 more overloads of resolve for different pointer-to-member combinations (plain, &, &&, const, volatile qualifiers)*/template <typename... Args>static constexpr decltype(detail::resolve(&Type::MemberName, std::declval<Args>()...)) pointer { &Type::MemberName };</code></pre><p>↑ 上面这段代码理解起来较为困难,我们可以将其功能拆解一下,它是为了解决什么问题?</p><p>想象类型 <code>A</code> 具有两个函数重载:</p><ul><li><code>f(int)</code></li><li><code>f(const std::string)</code></li></ul><p>当拥有一个指向f的函数指针(<code>&f</code>)时,编译器怎么知道调用哪个?<br><code>refl-cpp</code> 实际会帮助编译器 推导出正确的重载函数(通过将 <code>&f</code> 作为参数传递给另一个函数的方式,来直接触发函数)。<br><code>resolve</code> 没有任何定义,它只是一个 <code>prototype</code>,作用是作为编译器的一个提示。</p><p>这种方法总结起来是:<strong>传递函数性质的参数(由 <code>std::decalval</code> 产生)</strong>。它的好处是:所有的参数类型转换都适用,即我们可以通过 <code>MemberInfo<?>::pointer<int></code> 并得到一个 <code>void(*)(long)</code> 类型的指针作为结果。</p><h2 id="遍历类的成员">遍历类的成员</h2><p>前面讲了如何创建和存储类成员的 <code>metadata</code>,这节介绍如何遍历它们(<code>compile-time</code>)。<br>核心思想是 借助可变参数模板,创建一个 <code>TypeList</code> 的类型成员列表,并提供枚举的方法。</p><pre><code class="language-c">template <typename... Ts>struct TypeList {};</code></pre><p>!!! NOTE 这块讲的不是很细,没搞懂…</p>]]></content>
<categories>
<category> C++ </category>
<category> C++ </category>
</categories>
<tags>
<tag> Python </tag>
<tag> C++ </tag>
</tags>
</entry>
<entry>
<title>【cpp】Templates</title>
<link href="/posts/templates/"/>
<url>/posts/templates/</url>
<content type="html"><![CDATA[<p>C++模板、meta-programming<span id="more"></span></p><div class="markmap-container" style="height:300px"> <svg data="{"t":"root","d":0,"v":"","c":[{"t":"heading","d":1,"p":{"lines":[0,1]},"v":"Function Templates","c":[{"t":"heading","d":2,"p":{"lines":[1,2]},"v":"<a href=\"#模板基础\">模板基础</a>"},{"t":"heading","d":2,"p":{"lines":[2,3]},"v":"<a href=\"#编译检查\">编译检查</a>"},{"t":"heading","d":2,"p":{"lines":[3,4]},"v":"<a href=\"#参数推导\">参数推导</a>"},{"t":"heading","d":2,"p":{"lines":[4,5]},"v":"<a href=\"#多参数\">多参数</a>"},{"t":"heading","d":2,"p":{"lines":[5,6]},"v":"<a href=\"#函数重载\">函数重载</a>"}]},{"t":"heading","d":1,"p":{"lines":[6,7]},"v":"Class Templates","c":[{"t":"heading","d":2,"p":{"lines":[7,8]},"v":"<a href=\"#模板特化\">模板特化</a>"},{"t":"heading","d":2,"p":{"lines":[8,9]},"v":"偏特化"},{"t":"heading","d":2,"p":{"lines":[9,10]},"v":"<a href=\"#模板特化-代码示例\">代码示例</a>"}]},{"t":"heading","d":1,"p":{"lines":[10,11]},"v":"SFINAE <font color=#FF1E10><strong>TODO</strong></font>"},{"t":"heading","d":1,"p":{"lines":[11,12]},"v":"std","c":[{"t":"heading","d":2,"p":{"lines":[12,13]},"v":"<a href=\"#⭐std-max\">std::max</a>"},{"t":"heading","d":2,"p":{"lines":[13,14]},"v":"<a href=\"#⭐std-pair\">std::pair</a>"},{"t":"heading","d":2,"p":{"lines":[14,15]},"v":"<a href=\"#⭐type-traits\">type_traits::is_integral</a>"},{"t":"heading","d":2,"p":{"lines":[15,16]},"v":"<a href=\"/posts/rvalue/#std-move\">std::move</a>"}]},{"t":"heading","d":1,"p":{"lines":[16,17]},"v":"反射 <font color=#FF1E10>","c":[{"t":"heading","d":2,"p":{"lines":[17,18]},"v":"<a href=\"/posts/refl-cpp/\">refl-cpp</a>"}]}],"p":{}}"></svg></div><h2 id="Function-Templates">Function Templates</h2><h3 id="模板基础">模板基础</h3><p>下面是一个最简单的函数模板示例:</p><pre><code class="language-c">template <typename T>T max (T a, T b){ return b < a ? a : b;}</code></pre><ul><li><code>T</code> 是定义类型的变量,它可以是 <code>int</code>、<code>float</code>、任何class…</li><li><code>typename</code> 是关键字,<code>template<class T></code> 是兼容 C++98 的一种正确写法</li><li>上面有两个潜在约束:1. <code>T</code>必须支持<code><</code>操作符,2. <code>T</code>必须支持拷贝构造函数,为了<code>return</code></li></ul><p>下面是简单的模板使用实例:</p><pre><code class="language-c">::max(7, 8); // 8::max(1.2, 1.5); // 1.5::max("abc", "abcd"); // abcd</code></pre><p>当调用上者时,模板会自动实例化为:</p><pre><code>int max(int, int);double max(double, double);char const* max(char const*, char const*);</code></pre><h3 id="编译检查">编译检查</h3><p>模板的编译检查分为两个阶段(<code>Two-Phase Translation</code>)</p><ul><li>定义阶段</li><li>实例化阶段</li></ul><pre><code class="language-c">template <typename T>void foo(T t){ undeclared(); // 未定义函数,定义阶段报错 undeclared(t); // 引用了T,所以实例化阶段才报错}</code></pre><h3 id="参数推导">参数推导</h3><p>编译器会根据传入参数的类型,自动推导 <code>T</code> 的取值</p><ul><li>若引用传递:不允许类型转化</li><li>若值传递:只允许退化(<code>decay</code>),<code>const</code>和<code>volatile</code>会被忽略。引用会被转化成引用的类型。</li></ul><pre><code class="language-c">int const c = 42;int i = 1;::max(i, c); // OK: (int, int)::max(c, c); // OK: (int, int)int& ir = i;::max(i, ir); // OK: (int, int)int arr[4];::max(&i, arr); // OK: (int*, int*)</code></pre><h3 id="多参数">多参数</h3><p>模板允许定义多组不同的参数,以如下函数示例,其<strong>返回值的类型是不确定的</strong>:</p><pre><code class="language-c">template<typename T1, typename T2>T1 max (T1 a, T2 b){ return b < a ? a : b;}</code></pre><ul><li><font color="#ef6d3b"><strong>返回类型推断</strong></font></li></ul><p>从<code>C++14</code>开始,允许使用 <code>auto</code> 声明函数的返回值,即让编译器自己决定。</p><pre><code class="language-c">template<typename T1, typename T2>auto max (T1 a, T2 b){ return b < a ? a : b;}</code></pre><p>在<code>C++11</code>中,<code>auto</code>必须配合 <code>trailing return type</code> 使用,否则编译报错如下:</p><div class="admonition error"><p class="admonition-title">error: 'xxx' function uses 'auto' type specifier without trailing return type</p></div><pre><code class="language-c">template<typename T1, typename T2>auto max (T1 a, T2 b) -> decltype(b<a?a:b);</code></pre><ul><li><font color="#ef6d3b"><strong>类型萃取</strong></font></li></ul><pre><code class="language-c">#include <type_traits>template<typename T1, typename T2>std::common_type_t<T1,T2> max (T1 a, T2 b)</code></pre><div class="admonition note"><p class="admonition-title">Trick: C++如何获取变量x的类型?</p><ul><li><code>#include <typeinfo></code></li><li><code>typeid(x).name()</code></li></ul></div><h2 id="Class-Templates">Class Templates</h2><h3 id="模板特化">模板特化</h3><ul><li>这篇中文资料说得通俗易懂:<a href="https://sg-first.gitbooks.io/cpp-template-tutorial/content/jie_te_hua_yu_pian_te_hua.html"><em><strong>深入理解特化与偏特化</strong></em></a></li><li>源码 推荐阅读: <a href="#%E2%AD%90type-traits"><em><strong>type_traits</strong></em></a></li></ul><p>模板特化的作用是,<strong>针对模板的参数类型,从而定义不同的实现</strong>。<br><font color="#ef6d3b"><strong>只要你教得好,它可以 “见人说人话,见鬼说鬼话”</strong></font><br>(有点类似 函数重载 和 虚函数继承 的思想)</p><p>模板特化实现思路是:</p><ul><li>先定义基本模板(能说话)</li><li>再针对每种参数实现特例(能见人下菜碟)</li></ul><p>下面仿照 <code>Python</code> 实现 <code>C++</code> 的 <code>type</code> 函数:</p><pre><code class="language-c">#include <iostream>#include <string>using namespace std;template<typename T>class TypeId{public: static constexpr char const* type = "NULL"; TypeId(T t) {}};template<>class TypeId<int>{public: static constexpr char const* type = "INT"; TypeId(int t) {}};template<>class TypeId<std::string>{public: static constexpr char const* type = "STRING"; TypeId(std::string t) {}};int main(){ ::cout << TypeId(1).type << "\n"; // INT ::cout << TypeId(std::string("abc")).type << "\n"; // STRING return 1;}</code></pre><h3 id="模板特化-规则">模板特化 规则</h3><p>模板特化 符合 函数重载 的两个条件之一:</p><ul><li>参数数量相同、类型不同</li><li>参数数量不同(<strong>特化只能少于等于</strong>)</li></ul><p>否则出现报错:</p><div class="admonition error"><p class="admonition-title">error: too many template arguments for class template xxx</p></div><p>示例如下:</p><pre><code class="language-c">template <typename T, typename U> struct X ; // 0 // 原型有两个类型参数// 所以下面的这些偏特化的实参列表// 也需要两个类型参数对应template <typename T> struct X<T, T > {}; // 1template <typename T> struct X<T*, T > {}; // 2template <typename T> struct X<T, T* > {}; // 3template <typename U> struct X<U, int> {}; // 4template <typename U> struct X<U*, int> {}; // 5template <typename U, typename T> struct X<U*, T* > {}; // 6template <typename U, typename T> struct X<U, T* > {}; // 7template <typename U, typename T> struct X<U, T, T > {}; // Error</code></pre><h3 id="模板特化-代码示例">模板特化 代码示例</h3><ul><li><a href="/code/refl-sum.cpp"><em><strong>refl-sum.cpp</strong></em></a></li><li><a href="/code/refl-factorial.cpp"><em><strong>refl-factorial.cpp</strong></em></a></li></ul><h2 id="std">std</h2><h3 id="⭐std-max"><a href="https://gcc.gnu.org/onlinedocs/gcc-4.9.2/libstdc++/api/a01226_source.html">⭐std::max</a></h3><ul><li><code>_GLIBCXX14_CONSTEXPR</code> 在 <code>C++14</code> 会被替换为 <code>constexpr</code></li><li>实际可以展开为:<code>constexpr inline const _Tp& max(const _Tp& __a, const _Tp& __b)</code><ul><li><code>constexpr</code>无实质作用,重点是参数使用 <code>const &</code></li></ul></li></ul><pre><code class="language-c"> template<typename _Tp> _GLIBCXX14_CONSTEXPR inline const _Tp& max(const _Tp& __a, const _Tp& __b) { // concept requirements __glibcxx_function_requires(_LessThanComparableConcept<_Tp>) //return __a < __b ? __b : __a; if (__a < __b)return __b; return __a; }</code></pre><!-- ### [⭐std::stack](https://gcc.gnu.org/onlinedocs/gcc-4.8.3/libstdc++/api/a01566_source.html) --><h3 id="⭐std-pair"><a href="https://gcc.gnu.org/onlinedocs/gcc-4.9.2/libstdc++/api/a01240_source.html">⭐std::pair</a></h3><ul><li><a href="https://en.cppreference.com/w/cpp/utility/pair/pair"><em><strong>[cppreference] std::pair</strong></em></a></li><li><a href="https://stackoverflow.com/a/9270585/16823597"><em><strong>[stackoverflow] What is the purpose of std::make_pair vs the constructor of std::pair?</strong></em></a><ul><li>c++14及之前,<code>std::pair</code>需要显式指定类型,<code>std::make_pair</code>不需要</li></ul></li></ul><pre><code class="language-c"> template<typename _T1, typename _T2> struct pair : private __pair_base<_T1, _T2> { typedef _T1 first_type; ///< The type of the `first` member typedef _T2 second_type; ///< The type of the `second` member _T1 first; ///< The first member _T2 second; ///< The second member _GLIBCXX_CONSTEXPR pair() : first(), second() { } // ... }</code></pre><h3 id="⭐type-traits"><a href="https://gcc.gnu.org/onlinedocs/gcc-4.7.4/libstdc++/api/a01417_source.html">⭐type_traits</a></h3><ul><li>以 <code>is_integral</code> 为例,判断是否为整型</li></ul><pre><code class="language-c"> template<typename _Tp> struct is_integral : public __is_integral_helper<__remove_cv_t<_Tp>>::type { };</code></pre><ul><li><code>__is_integral_helper</code> 是一个标准的模板特化,<strong>非常简单</strong>!</li></ul><pre><code class="language-c">template<typename> struct __is_integral_helper : public false_type { }; template<> struct __is_integral_helper<int> : public true_type { }; template<> struct __is_integral_helper<char> : public true_type { }; // ...</code></pre><ul><li><code>true_type</code> 相关定义如下,其<code>value</code>变量就是一个bool类型的 <code>true</code></li></ul><pre><code class="language-c"> /// integral_constant template<typename _Tp, _Tp __v> struct integral_constant { static constexpr _Tp value = __v; typedef _Tp value_type; typedef integral_constant<_Tp, __v> type; constexpr operator value_type() const noexcept { return value; } // ... }; template<typename _Tp, _Tp __v> constexpr _Tp integral_constant<_Tp, __v>::value; /// The type used as a compile-time boolean with true value. typedef integral_constant<bool, true> true_type; /// The type used as a compile-time boolean with false value. typedef integral_constant<bool, false> false_type;</code></pre><h2 id="反射">反射</h2><p>先看看什么是 <a href="https://en.wikipedia.org/wiki/Reflective_programming"><em><strong>reflection</strong></em></a>:</p><blockquote><p><em>reflection is the ability of a process to examine, introspect, and modify its own structure and behavior.</em></p></blockquote><ul><li>通俗解释,反射就是从一个对象(<code>object</code>),能够反推其类型、成员和方法</li><li>以<code>Python</code>为例,<code>getattr</code> 就是经典的反射功能</li></ul><div class="admonition warning"><p class="admonition-title">为什么cpp没有反射?</p><ul><li>反射会导致编译后文件过大</li><li>cpp很少用到元编程(相对于C#)</li><li>cpp有模板,足够应付大部分需求...</li></ul></div><h2 id="阅读材料">阅读材料</h2><ul><li><p><a href="http://110.42.228.178/pdf/CPP_Templates_2nd.pdf">CPP-Templates-2nd 英文</a></p><ul><li><a href="https://github.com/wuye9036/CppTemplateTutorial">CPP-Templates-2nd 中文版翻译</a></li><li><a href="https://github.com/r00tk1ts/cpp-templates-2nd">cpp-templates-2nd 中文版翻译</a></li></ul></li><li><p><a href="https://github.com/wuye9036/CppTemplateTutorial">C++ Template 进阶指南</a></p></li><li><p><a href="https://veselink1.github.io/blog/cpp/metaprogramming/2019/07/13/refl-cpp-deep-dive.html">refl-cpp</a></p></li><li><p><a href="https://bartoszmilewski.com/2009/10/21/what-does-haskell-have-to-do-with-c/">What Does Haskell Have to Do with C++?</a></p></li></ul>]]></content>
<categories>
<category> C++ </category>
<category> C++ </category>
</categories>
<tags>
<tag> Python </tag>
<tag> C++ </tag>
</tags>
</entry>
<entry>
<title>日志:2023年8月</title>
<link href="/posts/2023-8/"/>
<url>/posts/2023-8/</url>
<content type="html"><![CDATA[<div class="hbe hbe-container" id="hexo-blog-encrypt" data-wpm="Oh, this is an invalid password. Check and try again, please." data-whm="OOPS, these decrypted content may changed, but you can still have a look."> <script id="hbeData" type="hbeData" data-hmacdigest="088c707709b995861e4e58c94a61b527368d8a83cfc58edb93edc304acbc9c56">b3dc07a81f6459d120ce338ccca550463faa708b9b4d89df9ab14ba4edd809f60b32d15b5672ce84c5d84744f7f475de0b1305a637ea913974dff7410a14ed4dbbb40a5278eb14bbac34e59e12a284c7a294ab69a495d47e11e78ad50feb1070429e58f3fbd737c20fb16c7243eddbb619e52c31eb507d6765e3b14dcaa7e9d13492120aa6f2d9638cdc372f2d3c81cf4232ed0874302eb77e0fba53c7b0ac1b5e05196961ebceabaee4415690ead6a710c137c2757694abdfd08eb8c2634a5d753274da2b1c7bef744740058d16d7091d22fa8304764b0d2f6294ca8c6a207e9631f260ac16e68b32cf9dfed4653d1dfd7ec34e029768744ff31cf46be9716e7586f7db03f6fdbb5424f4ac8476c5cf96e9dd587787781164d9d02b9abc39b1840ec2cd478ca480554f4049c941e50195a77f0b24330ba24922044d92ee1b7b07c0420d90b435163e9b6ff6a187c600a683de6c80ee99f40ad6f04c57b1038b59b7c5fa28d202cbb5b68ac169c96e956c701fa2733dd351daaf2c77420ef2c9d81b4f6567415a90f9806bbd90215c38f75a31cdc983a0ee93c2590e66b10f2f88cf627e9e7230e093277675ba2ffa1f8a9d71e9763c19c4389b0f2a0f8903c564dc10ab53981fc487ccee24df6454c9466089470df32dfe841773b273a2e9212119f7c977a60035ea301d47eb5078523d993c6370d7cfc69c6bec52ff9cd8c59c346f4af5adb805c21e170d244cd7ff29f9d3f70acaf5041b2b2aa0e2a48c06b1e7fe726ad391187a60e8153790242e965db2f618c5cb9f90bac81e9171a4082f761d56fd77065727d7a01568bdf1a92fd6b69a47d66ae586c041362e158cf6918e643df04270fbf2d7b6c83b81a3cabfdeb4cec43465d5d0d18f24d8eccd0eecbba24f24f0ca3cfdf63d71f027c56dd478870b5bb67d334dbe2b1d1e1bcd505a731da11641c35e59e867a6c505cbd4078a924a22c3f47ec3268a07f420acf3c6ed043d684e69bd88468885b77dd7385241e94561c5a2132be9a941fe048e6fb604daf1ffb603538ba15f37ac2a09be55fe1b1e40ceceeeea21877916031087ca10ff470729aabadb15870ac7a684dda40c886f3270bea5db51aab42a40b4c64579bc15a5f4489b5c5c1c5b433251d46b7188000fd63a7691272641c1bf76e00f4a8558e58d69bb2b2c0130608f044657dd53f25cce7b98e4630c9d52c24136a428286ecb66a66313c2881f168df75a2cb8453c0bcac9e32cb1434d6809996eae2b590ebbdaac6c9009739126e9f8e92976ce7d71b9bcbe7e7de2fe5ade15b4d1e8dc3e147d0be96aaad8c939e1e122aeb55ba72ba38d37329e489f7c6edae50f15673c33606c8b801c6b4ad8d2f770c80cafc9a3dc5912a35995572ad088d582045bcd1b1d6b7839cb8a03bf6de7aea3d25c808f25e92bcac20dd3a8d76aeaaee615e7f58c68cec17e2df0d43f0ff1b11f23e5e684e3e8ad0679ebb58877fa208ca34f7014e80b0c60429634ff4d88cc0aadae737dd83f077ec2e164fa493b7e95e55e979d8a315b57c50699903b463027e33cf890ceb17eb29ab76882555cf0acdce817698929fdc6ae42c726c15cd09f9876c2e4c2824c265912a1ce677d096667d049cdcd82d88a78bf874effcdc566f641455cd98637d4b7880b733545ba61714b012fc1e09c3988cdb32023c4e6679d4772f3aefe2be7b775447b05182da8750b8eb0ab9c7e2057e9165432399749ac975f2f5f4ed9a4d0dfb53a9fca27c539f3cde6dd71523ca62fd8503a0b26edb8ca15af6be9aa9350df5e93595bde5fe38fa8f77daf14cec1eb557af4dbf77a5503fa04f201a89d536e268281174dda349431b7b582df05a892d5baf6104a9c2dc4f0127bee4bb2d694cc63c7e401c3a8f9de45d2ec27306206a40444f066be06109511c0049c38c1aa3b302645ab2786db16e50d6392729ab724f74631f65b029ee199ecb8aa9adba4985c3323b83133db3508595c9a146c051f9669c9a6d793d656024b6e68fac42f21b9ec4cb0f6e456680fdadd514588c7dc2fcb1192d10b7a4ed512f92eb4f510849f16fd9bec830f1a9cbe52a5a344143e44e0bc9608ce50b3a3c96ca8ff6f033fe415254e1bc2a50c2d4e56b784876cbf8004c45f63a45356c08199fbc1f433cecbadc18e21d5fee429e8b1292e19f125e57d4aed4f05a0b2194a6e17d423c01774476f04219848cbbb9699ab58bf26ddaa99130657470c6599aab71db4077a077b2784db5bbf7a108f85c27cf2e90dc801ef7f4ecc3ca15e28099c73bb4d8ca8604537b05f16d23c66bc7c2e152fbc478293af95ebacf9c8796a586d70ef75ad618fa2d3bbc5fbe81a22d8ee36105b70adb9f636c3cd69228e9b53a463e890f103ddc72f0603a6815e32ffed8a79e7467a91c3ea6da318d7d6a2c1bc4e440b3987bec38c4fe6724ef9ec7ff7367ad45351339ce7e2fc68db732404b75e057dbb658e05f68678fc99eac0479ed9aefd6521b65b88cf162dc1ad81e2fcc26d259805213d06d68e632d484e95cd8bb09b8954262b5063afe43274e2033352daa9313db01c405cc29528aa7a6467b926951bc8252d36a967916a8f7b385dbbcf51af18bb0f8bf5d2d99cb334f9ec55f15b7c5e4a6a49207fbdf3ff0f032feeff053f0caf51525b5d934f0281b317a2591f7863541829e6d3e4dfd10f495d34e76a37a4cb1994dc5956695f2af5b29a34c58cd54d6c1b6b4a25d80244d0bba77e336c80bcfd98c258c670d3b0c5cc7cf34acfb2ba60c885f80373bc9ba82e83e3c18432bdab9d5ddcae387b5e0c552cda7fffdf87128451ca3c5a874d268fe2305bbbcf5910a4b978866328649f891014df094ad555bd32f76972994bbcc2302e8fc6cc5123317a028cfa93a9d2aa4d2e006b7a8e9895a90e49ea4a65b67af51c19e8e993a954e96235e9f150917fae2fd1705f21782d256946066b4e4f531af3c5cef513d4ed399f7c252c3fb5097e828a0058ad4e1089dfc8df4f5d0293d9382284328ba9c7adbcdcd09383dca737191ddf9b2969e6b02af4212cf4361894ebc708f00b16e7c64ff10e4432b65fe2c7b05e4889c5808949737998e7f6335bf48a4354828d177ea3dffd66c4df7c03a187862eab302fca251e5a5be70cce19df939b5efbdb527f6004dc35c8fa4bb309e4d1047dc84157379579bc9c7bf6b1805b97d117a654c1d579765989e0f4b12be0958da5b3c7c9bf86a15679a4fecd60f11f83ad11b9091f80343d3397747d9680c70f8e3a5db6f49dc2c7f0e3e0a91abaaee24635d20d1d72a74afadea6040b9288356a89e19f86781940d1a552ac5b327a1fc06959c7a3433042a2e98ce411575f087822beb5e58303077bc84f16d07bb4c36f7bad34b2b575af27e0ba4228abdabb3380d6c2666514d82f82814eaad75e3a39b999d563dadc7074005acdd320836230eaa8ff4f153d5fb1e6e3bf23891d8409a3aab2b9ca7282fe15c11b6f9d7819574ae7e05b42b29f5ab944dc5c26af551487c7b4dd302a40741f28a14f50391abb1684ba1c8d79ed5211ea977d5f7038e733432f469761af877cdbe2ae70f6654976e9fd65c1b841b0a7a75f4f3a6dbbdca6e0e7712589a8d683b86e9dc4624813421f786966137286b13ff885b73c912ff105e233a888faa682c5f2190772d64fc9f820da019d49fc757bd442fc9ab962a212e1cf55f700765fd22517eaf05202fb28e61ad0c354abb781ce08359a497ae7efbb34dd52ad49db933da2f5138b95fe37a89753584c755070361a9d1721eb707164d8d85cdbfd53011fb8ba98971f45e6eb6cb849a955902875d84a7bc9fb98477a81b1d2f077d2846381889e1ae1171cd9a86d92b0b8666ed73de0a282013ef9a009dd51b3b6002b05210c4ce883d2e13d27f61c577cc6af7d69ef8ede74aaa73554d9ef9591b1c00fb1863ae9809614950608dee1b35b60724ad4fd4ae5c1b91f0d9fbe7482e4edd2cb6182bc0af102a823b9b45e62376af8dbb0aa24ffd3e8137e3f9390ead53f34dc2204a239e1e7307cb8ae198c70ff0f3e62488627ed20b48da9cdd6aa7e3698774c8002e82f6dedd77abc97301269443fbfe203103ff3807139e6472ed40a4d67ec624e141e50e1234ba20e93df4fa080cc6d5f4a62ebabd5b63d51f198c9b8305c196ff559ca00b1750a747ca1a187c2b3f1f0548f99b3006a70b70ab4458f3de77fa25050ae50e9c31a3345876e12a48d5184846e6a711cfeeff25f984e5929465a19f8811addf40d930b75c6ab0a58a27d3ff51e949c2875b6621f6f4a986b5110114321dc1c62390aa67bd88f77a41d5b5b49322a95351d36782034a6d45b6db85ebafa79cf5b3f3002f2a714e11576d03b026f2c256c12ed04f2163daf93d2c500c7d3053827c1eadc2450f4a02703ef5bc1ebb2021a84029a68b25a2936d76341ab9e710263f2bc3688cefb26405a6fef0fb5d4ff2fa28aafbfe3016880820bcfd32b4b1fe203124d4027a483492bba57e5121f2a9b3e31e7caec33cf72b586944dda700dc922f626593558cb60ebfcaf656883a3786aaff323b53cd38d02dd785670eb9ea232692ae40965f1ed43fd933d0fa4f95cbdbf9bb03809efcc82eefe4020e15d6bf2b9828012589fd5074d139aeb55b80438ea9f500f06b899be9b2cc0c575dcfcaa6da869c182e2fd70eb3a7a75b5361ff28150b215d50851b8c3a0050331587f0959a2310b71e957ef18fad48dc7e16efdd8cc14ac82c89012eac8bc0738413646a19b38a5ee89c54a934f28be319d9aef11a7003d34a8d3b16a3e9981e1e640e04809a94d566ae69ab90e58ca1647844939159901d632c0744355c4fe0759d6f3b66b7430db18cb85f659cb2c3357cc99af3ea7a92540b2e6d6202e5eb3f7f0d3b686de0f52ae301ab564c490c744190cee9daaecb8ab644fc24fc6699f3c099b006f6281229c303afe87c87d297f77b14ea36875bdea69b667ff9e9271596408077c120dd67d2b14c77f1f98c4577981fc649984b6546e07931ab91755759537debd2dc68560228fac2fcc2738522e975060279ca6a0c9d3df1af329d432ea7f0da40b3e3c9587c54e1fe1318c6f118611f372a7ca1b8e8b6b19847e189de01880cf9be6dd73106aa6c6f1e548b2a4bcdca64c33073302ba8a9142032406c65d52e4a18ad95044c4d4bf78494e4964b79a7d35deb929af3c3e9d05dfc713ab5018d75a564846ad762b58e1f64e4bbaf0508b3301a2bc7111a3c3df41f1657bcc66354ad3c152d5aab95c5ccfca48fe101f199bd13bcd76e1e64b6019edb0156d481632228320c98bce931dbbd413db446b783d853edc9013bd3db83f1327ad3d3d0c3ba5f91915bf9e8bd6c56bb34fcda55f88f0448cfb1c3148a1b44a7c0b131237db2c01847746e6abced888260001b2291ade4763a4a54f64f0bd722a4d218287be3ab6e579d4785459b289b3faceb7feebf7466101f1dbb9a11f5558cb2782a709029f69886a06e621215fda6addadf141548d7307141b6a9ab599c41b8c0435dfd4628551db893f19af55d67057f4d01e230c3dcdcb45858fc84978cbfc864005b8756a53f7412b6f8fb3fa55b821c2798d2ff4f8e2be30f3bdb5cb0113f230db06059ec2500a601b33608107b7cb3e2b8fcafcd29e768d28d97f4deee56a8f3688b1541cc5963efcd4a4cdd04368a41676bff2f02a10821025119b95fbcb4c998d5f58754171aac564c41f95f304f12401e629853ffdb63f5e8911acf87978144439b2c25616242a622a3ab0ea84343f63e7c59bfed97b2e7682dba574a411641410e38a60e5be49f6197e6e664d5310a0e93a92d97d48e3c596ef3577e50ecc6f11434e02222b613209878a9b07cd3552df6d1e9234dad172da1f9378ac6a98a71fc0099a48d9572225e193c84de989f5ea68110f3a77eea1</script> <div class="hbe hbe-content"> <div class="hbe hbe-input hbe-input-default"> <input class="hbe hbe-input-field hbe-input-field-default" type="password" id="hbePass"> <label class="hbe hbe-input-label hbe-input-label-default" for="hbePass"> <span class="hbe hbe-input-label-content hbe-input-label-content-default">Hey, password is required here.</span> </label> </div> </div></div><script data-pjax src="/lib/hbe.js"></script><link href="/css/hbe.style.css" rel="stylesheet" type="text/css">]]></content>
<categories>
<category> 个人日志 </category>
<category> 个人日志 </category>
</categories>
<tags>
<tag> C++ </tag>
</tags>
</entry>
<entry>
<title>【vscode】vim定制化插件</title>
<link href="/posts/vscode-vim/"/>
<url>/posts/vscode-vim/</url>
<content type="html"><![CDATA[<div class="admonition note"><p class="admonition-title">导读</p><ul><li><a href="https://github.com/VSCodeVim/Vim">VSCodeVim</a> 是仿照vim的vscode插件</li><li>本文在其基础上添加一些额外的个性化功能</li></ul></div><h3 id="VSCodeVim">VSCodeVim</h3><p>vscode vim主流插件有两款,分别是:</p><ul><li><a href="https://github.com/VSCodeVim/Vim">VSCodeVim</a>:仿vim的插件,功能不全</li><li><a href="https://github.com/vscode-neovim/vscode-neovim">VSCode Neovim</a>:基于Neovim,功能较全,但依赖nvim环境且配置复杂</li></ul><p>笔者一直使用前者,因此本文全部围绕 VSCodeVim展开。</p><h3 id="缺陷-😞">缺陷 😞</h3><p>VSCodeVim Github 拥有 1.5k 未处理的 Issues,作者的维护迭代速度非常慢,因此有很多缺陷和功能不足之处:</p><ul><li>不支持 vimscript function</li><li>不支持 vim bash</li><li><font color="#FF1E10"><strong>【bug】</strong></font>经常Esc失效,弹窗报错 <code>vim.Escape is undefined</code>… 需要重装插件</li></ul><h3 id="亮点-🎉">亮点 🎉</h3><p>打开 <code>"vim.statusBarColorControl": true</code>,可以使底部 <code>statusBar</code> 跟随 vim模式 而改变颜色。<br>本文希望进而改变 Cursor 和 当前行 的颜色、高亮显示,并支持 config 配置,效果图如下:</p><p><img src="../../images/vim-mode-demo.png" alt=""></p><h3 id="改进-💡">改进 💡</h3><ul><li><p><strong>支持配置 StatusBar, Highlight 等颜色配置</strong></p></li><li><p><strong>支持区分 Normal, Insert, Visual 三种模式的颜色</strong></p></li><li><p>下载链接:<br><a href="https://github.com/593413198/Vim/releases/tag/vim-mode-1.0">https://github.com/593413198/Vim/releases/tag/vim-mode-1.0</a></p></li><li><p>配置文件:</p></li></ul><pre><code> "vim.statusBarColorControl": true, "vim.alpha": "80", "vim.statusBarColors.visual": "#005f5f", "vim.statusBarColors.insert": "#5f0000",</code></pre>]]></content>
<categories>
<category> VSCode </category>
<category> VSCode </category>
</categories>
<tags>
<tag> vim </tag>
</tags>
</entry>
<entry>
<title>随手记</title>
<link href="/posts/idea/"/>
<url>/posts/idea/</url>
<content type="html"><![CDATA[<div class="hbe hbe-container" id="hexo-blog-encrypt" data-wpm="Oh, this is an invalid password. Check and try again, please." data-whm="OOPS, these decrypted content may changed, but you can still have a look."> <script id="hbeData" type="hbeData" data-hmacdigest="f98579ba7e41337bf12ff606dddb29230f744db5a6fc1d3b6bf7cdcffa45c282">b3dc07a81f6459d120ce338ccca550463faa708b9b4d89df9ab14ba4edd809f60b32d15b5672ce84c5d84744f7f475debb79baf718116b8398ebb7a36965f5712db04a990a04196bf7d89b9188d2f5969b089300c199c887ad4490953d1810357cd375ac06e732d356d4ec27b642aa2d46c8fdf198d48a4b9620936f8c18efa99cd53c0e6c5befbd6da9ebb0e8d473ba88daf9814bf097589053470cd244c49759b29258898ae147d3b92406541ecc1426781b7f965d69488f8227468d25ace43c6b69c8a19f97354453e8ff96435fa7c142eabaa7418866597b071d73575a4fa98f09916a3b6256959c82764411afd8f6b01c5b6bf4975a899e020f3dee128569e0a2113d6e5eea3dde855a24ae2aa431bfe1d66cd3cb55d7640d0b6991fc435e20626245ee0a53ac26e04647979c2561d16a51e30ef1d5cb013803f9b1ec1543c53db0e0612835d22a88763eb021d58fca3f98f8f9a466b23ec85f6ebc9730cbedc2ec3beac3e1e4aab13769ea50c4d0877e05a485ad6e80e8d95f90bd71e51ce624b4088c4a292994308c83bd9615ba9ceddd1b2b013e68b6f954bbbc2d9526df35e8df607ee068bf2417f8ed2c68c803d5fb036aebfda9e5852e0f2c3ee3f60fbd426bd7ca8d81e142b1688443566187802f206bffc24c533d88c4021b98271f489ff2a0cd9ea4c88b47d3858b8c28ff89929ef8c82a3e39b86f12be544c44b5b1ac7357322938f17d796c107223e3e61e2536bc9c9f685dcaef7fb967ed3725e32d02590f722c180df46dc1d6322835f1b42b4c0ff08650d36700c2cea5c141feef7d79b13c80bbb0a8fba54ebcc23f3b67fcff332fc61daff746eaf1288abc1a86a4b97fa64346f234e566d4f692449805cab93c64aaa500b9913cc61f341bb1c65079ed93ef988b248c860b2a24a2b209ce9d754f2b1df62dcbcacfad713f6d4f65a96b028ccfb0e64a60c723e8f75a4cfbd52c94d1aa50ec3f413505e15234a5343fdd00f9a2d2a242ab4b88c06310315d84f1696a55134767fcb9fb5b093e54bb71600ade189778bb8cf3319486269c47ba15f22b60da1ba2c887a3ce32b690877586c23125c5caae8c4b4f08f997e8c9d8c1dede3170a78325570a6d259c6b7d7bf646cee7820267bf00a424187c4d543ee633880109906ac0816150aa126eea1faf33cde18cf6254edcb7c2c071e56f9d2183c91a929435e0ecf10efcbaefd4c14bb5777dd02f1308a39af4ba1ce6efa86ebb83c68aa34bf2747349b1e89322955d7a4e242fff5f1450355ed214e1e4fd646f97ed41bd89803dddc681979222e476d7cb879c749e3c9913ee9b7dde54851bd09fc9a06a957c05f84c2c96460b8aed2018dc25c9406b2f4990c74ccd2a25d64296e0d49020ef13c45c63d02ba269b846ce3a4e82b9509c453a5cfcad94c8d1c45880f36269638ec83a6493c9abff864177c67b6afd206f214b626106ae6b8af1f00edb6d913fe7af03933a9bc36204dd0bd83d8dc516bc13d0f3dc06e86c4197b7997b4cedc11e5ba376fb69c6ffd4d5763cb0a283e280add5fcaf2cd74a2bf7312a482efe56e8584f6a1693dcf8eb0cf0608e91c3fcbe1f45847790e57eee74537715dbf6d530802e2f819775c0845d24efaed4e8f7a1d810d5b1e7a13082323c5e65e3d68094522cc3e7a09b4ddec448c7136767c556f99706c8c30b77b15786986b2b0c70c12f9e0d0fcf52d8d60ee8183eafc441fea5f4902530332abc8cfb1ccc2bf088b4500d3d6a7fe11f1659c507082c930b1152c218b97d777d5635a5cf90d07aec0b8d8bb04f75ebc051641881c3e872afe92c0ed0cf34109975f4f0267ba0ba76f4db3713cf95ca18c7b341e2fe8ca647e8186769199c1ebce7b0daebeaf25b1072da53ec5cbcd20904ac3bd9d6e09f05a6746a6b4e0bd35ada69c341fa3324e0fe20691e5dd826c282b73b44512d303c893fb2e22c852e93960bc777e8db71541108dc4acd1bdb6f37d75cf7f86cd379bd08e3e7ede3d43bfce30f4921f422c6ad1a7054fc4fb8ccd075f703e829db9d36f7af079988157ef75debc08c652e340a6869f985805bdf8d3a6d1427527e89b902c659ed49359b1d48f5d7e33d84a76128c70e88802c37333976de3f35d3ca317e4c5d13e5b203794057d577e77762524375cc1fd05c893d6831bf561fc3bf167fcfefdb1a8f079324b89759388db39e616a6bd912e8ad37dd485589207f5b08d8bc3e8dea37b2ff9915517b6b2336bb71021de0553ac5ffa57424e585ed24f6d5a7a74379cd57129b4fa665d59b87830009f757eb389c87de4de889b175114c31dbe93252a58ff6508816b150c823fcdb470b1e3afa3c72d89c2b286d1a8a55a24ff70b80d4f3186d417c167a6fadc795bdec784e17bfc58345aa3f4b72b748acca5e855c93611615e3efc6f82d278fad1b7c3b1bbacccf7335df24986d58000dc01036a3e8c87387b02d19706b935530ea08be797e0bb7c96752b892ecb5c6f745952c3f0b66b975525f5d98d3f6746485317883c2093436e3def6214f5d0172f17f77b6339efbfa3a2ac0357c482e655141a50419dc038eebbb3d89f8932330f3138decf4e0adc55b360ef13c9754f46cf27f63aed5db0a8b62b025636d4462f5f69df3463ff9401e5ec3b82a852dcfe5d8f10ac928a37709cdcbf42d24964e396011849da2131346eb6351893bb29fac891a33cc5fc6d7abf5b5f8247b10ccb5925973cca20cd475cb7062ae2813cd0328192019256c2d2498f954b6f4ab5afa42ea751b2670c85c4de46234fc8ac19a7a3f17bde9218bfde8fd2d5c2e128d6c1316f361fe5f4b5a3f7b1afe4c0b7145972e79b2967951048bd6b63f0acaed</script> <div class="hbe hbe-content"> <div class="hbe hbe-input hbe-input-default"> <input class="hbe hbe-input-field hbe-input-field-default" type="password" id="hbePass"> <label class="hbe hbe-input-label hbe-input-label-default" for="hbePass"> <span class="hbe hbe-input-label-content hbe-input-label-content-default">Hey, password is required here.</span> </label> </div> </div></div><script data-pjax src="/lib/hbe.js"></script><link href="/css/hbe.style.css" rel="stylesheet" type="text/css">]]></content>
<categories>
<category> 随笔 </category>
<category> 随笔 </category>
</categories>
</entry>
<entry>
<title>【表达技巧】跟罗永浩学演讲</title>
<link href="/posts/talk/"/>
<url>/posts/talk/</url>
<content type="html"><![CDATA[<p><code>罗永浩15堂演讲私教课</code>学习总结<span id="more"></span></p><div class="admonition note"><p class="admonition-title">导读</p><ul><li>B站视频:<a href="https://www.bilibili.com/video/BV1PP411T7fv">【演讲】罗永浩15堂演讲私教课</a></li><li>知乎:<a href="https://zhuanlan.zhihu.com/p/619973080">【学习笔记】罗永浩演讲私教课</a></li></ul></div><h2 id="前言">前言</h2><p>开这篇是因为近期,有一个在公司内部分享的计划。<br>因为不是技术类型的讲座,技巧因素占比就会很低(而技术分享更注重将东西解释清楚)。所以萌生学习这类演讲 + 表达技巧的念头。<br>认识罗永浩,最初是 6个亿 的负债梗,更深入的是在 脱口秀大会 作为嘉宾时期的发言,对其有几个认识:</p><ul><li><p><strong>听感</strong>:表达清晰连贯,但嗓音条件恶劣(据本人调侃是老太监音色)</p></li><li><p><strong>逻辑</strong>:知识涉猎广泛,且临场反应迅速(后者是幽默感 + 经验累积)</p></li><li><p><strong>内核</strong>:善于制造一句<strong>核心话题</strong>,并反复洗脑callback(如脱口秀的“大局观”,如演讲课的“因为大脑就是被这样设计的”…)</p></li></ul><h3 id="关于这个教程">关于这个教程</h3><p>基于罗老师 <strong>“演讲都是有套路和技巧”</strong> 的观点,将其演讲课的套路总结如下三点:</p><ul><li><p><strong>为什么要这样?(赢得认同)</strong></p></li><li><p><strong>如何达到这样?(方法论)</strong></p></li><li><p><strong>举例、类比论证(深入人心)</strong></p></li></ul><h2 id="十三个要素">十三个要素</h2><p>这些技巧比较多,难以短时间记忆和掌握,因此先记录一下核心观念,剩下的在实践中掌握和理解。</p><div class="markmap-container" style="height:350px"> <svg data="{"t":"root","d":0,"v":"","c":[{"t":"heading","d":1,"p":{"lines":[0,1]},"v":"让观众听下去","c":[{"t":"heading","d":2,"p":{"lines":[1,2]},"v":"<strong>讲笑话</strong>"},{"t":"heading","d":2,"p":{"lines":[2,3]},"v":"吹牛逼"},{"t":"heading","d":2,"p":{"lines":[3,4]},"v":"装逼"}]},{"t":"heading","d":1,"p":{"lines":[4,5]},"v":"让观众容易理解","c":[{"t":"heading","d":2,"p":{"lines":[5,6]},"v":"讲故事"},{"t":"heading","d":2,"p":{"lines":[6,7]},"v":"<strong>作类比</strong>"},{"t":"heading","d":2,"p":{"lines":[7,8]},"v":"<strong>三段式</strong>"},{"t":"heading","d":2,"p":{"lines":[8,9]},"v":"提问题"}]},{"t":"heading","d":1,"p":{"lines":[9,10]},"v":"具有说服力","c":[{"t":"heading","d":2,"p":{"lines":[10,11]},"v":"有逻辑"},{"t":"heading","d":2,"p":{"lines":[11,12]},"v":"有数据"}]},{"t":"heading","d":1,"p":{"lines":[12,13]},"v":"让观众产生共鸣","c":[{"t":"heading","d":2,"p":{"lines":[13,14]},"v":"有激情"},{"t":"heading","d":2,"p":{"lines":[14,15]},"v":"有情感"},{"t":"heading","d":2,"p":{"lines":[15,16]},"v":"<strong>升华境界</strong>"}]},{"t":"heading","d":1,"p":{"lines":[16,17]},"v":"核心","c":[{"t":"heading","d":2,"p":{"lines":[17,18]},"v":"<strong>有画面</strong>"}]}],"p":{}}"></svg></div><h3 id="精华摘要">精华摘要</h3><ul><li><p>开始一场演讲的最好方式,就是<font color="#ef6d3b"><strong>讲笑话或讲故事</strong></font>。(千万不能讲道理)</p></li><li><p>对于晦涩或陌生的事物,要<font color="#ef6d3b"><strong>巧用类比</strong></font>,如 谈判就是找交集。</p></li><li><p>开始主体内容前,用<font color="#ef6d3b"><strong>三段式</strong></font>介绍提纲。(思维导图)</p></li><li><p>讲故事的三要素:冲突(吸引注意力) + 行动(故事的发展) + 结局(表达的内涵)</p></li></ul><h2 id="其他技巧">其他技巧</h2><h3 id="1-用坐标系描述事物">1. 用坐标系描述事物</h3><p>举例,如何看待 <strong>“量化交易”</strong>?</p><p>首先,一个交易策略的评判标准有两个维度,分别是:</p><ul><li>Interpret:(金融底层的)解释能力</li><li>Predict:(金融市场的)预测能力</li></ul><p>因此,引入一个二维坐标系,甚至可以类比不同事物在其分布,例如:</p><ul><li>进化论:能解释为什么猿猴进化到人类,但无法预测人类未来进化的趋势</li><li>地心说:能预测太阳东升西落,但底层科学原理是错的</li></ul><p><img src="../../images/interpret-predict.png" alt=""></p><p>最后,根据 量化交易 预测能力强,但无法自圆其说的特点,可以将它放在类似进化论的位置。</p><p><strong>该方法论可以让听众直观清晰地了解事物的多维度特征。</strong></p><hr><h3 id="2-结尾升华主题">2. 结尾升华主题</h3><ul><li><p>几天过后,观众未必会记得你讲了什么,但他们或许能从情感上认同你。</p></li><li><p>这很大程度上,来源于结尾的几句升华。</p></li></ul>]]></content>
<categories>
<category> 随笔 </category>
<category> 随笔 </category>
</categories>
</entry>
<entry>
<title>【cpp】Memory</title>
<link href="/posts/memory/"/>
<url>/posts/memory/</url>
<content type="html"><![CDATA[<p>C++的内存分配与管理<span id="more"></span></p><div class="admonition note"><p class="admonition-title">导读</p><ul><li>理论偏:<a href="/posts/virtual-memory/">【CSAPP】Virtual Memory</a></li><li>本篇结合 C/C++ 了解内存分配相关领域知识</li><li>ptmalloc,tcmalloc,jemalloc ...</li></ul></div><div class="markmap-container" style="height:300px"> <svg data="{"t":"root","d":0,"v":"","c":[{"t":"heading","d":1,"p":{"lines":[0,1]},"v":"malloc / free"},{"t":"heading","d":1,"p":{"lines":[1,2]},"v":"new / delete"},{"t":"heading","d":1,"p":{"lines":[2,3]},"v":"system-call","c":[{"t":"heading","d":2,"p":{"lines":[3,4]},"v":"<a href=\"#brk-sbrk\">brk / sbrk</a>"},{"t":"heading","d":2,"p":{"lines":[4,5]},"v":"<a href=\"#mmap\">mmap</a>"}]},{"t":"heading","d":1,"p":{"lines":[5,6]},"v":"c-malloc","c":[{"t":"heading","d":2,"p":{"lines":[6,7]},"v":"<a href=\"#ptmalloc\">ptmalloc(glibc)</a>"},{"t":"heading","d":2,"p":{"lines":[7,8]},"v":"tcmalloc(google)"},{"t":"heading","d":2,"p":{"lines":[8,9]},"v":"jemalloc(facebok)"},{"t":"heading","d":2,"p":{"lines":[9,10]},"v":"mimalloc(microsoft)"}]},{"t":"heading","d":1,"p":{"lines":[10,11]},"v":"c++","c":[{"t":"heading","d":2,"p":{"lines":[11,12]},"v":"std::allocator"}]}],"p":{}}"></svg></div><h2 id="malloc-free">malloc/free</h2><ul><li>阅读文档:<a href="https://en.cppreference.com/w/c/memory">cppreference: Dynamic memory management</a></li><li>使用的时候多查阅文档,注意 <code>malloc</code> 使用时要判断 <code>NULL</code> 避免内存分配失败</li></ul><pre><code class="language-c++">#include <unistd.h>void *malloc(size_t size);void *calloc( size_t num, size_t size );void *realloc( void *ptr, size_t new_size );</code></pre><ul><li>分配过程:↓</li><li>需要考虑字节对齐,注意被释放后的内存也可能重复利用,这也解释了为什么野指针的 <code>undefined behavior</code></li></ul><p><img src="../../images/malloc-demo.png" alt=""></p><h2 id="new-delete">new/delete</h2><p><a href="/posts/virtual-memory/#u-class-black-mallco-free-u">malloc / free</a> 前面有介绍过。</p><p><strong>以 <code>A* a = new A</code>为例,通过 <a href="https://gcc.godbolt.org/">godbolt</a> 查看汇编代码,发现其有两段逻辑组成:</strong></p><ul><li>调用 <code>new operator</code></li><li>调用 <code>class's constructor</code></li></ul><pre><code class="language-asm">call operator new(unsigned long)mov rbx, raxmov rdi, rbxcall A::A() [complete object constructor]</code></pre><p><strong>相应的 <code>delete</code> 方法,也对应如下的两段逻辑:</strong></p><ul><li>调用 <code>class's destructor</code></li><li>调用 <code>delete operator</code></li></ul><p>下面重点展开对 <code>new / delete</code> 两个操作符的学习(推荐阅读 <a href="https://www.programiz.com/cpp-programming/operators">C++ Operators</a> 和 <a href="https://en.cppreference.com/w/cpp/language/operators">cppreference operator overloading</a>)</p><p><strong>先看 libc 的 <a href="https://codebrowser.dev/llvm/libcxx/src/new.cpp.html">源码实现</a>,可以看到是对 <code>malloc</code> 的一层封装。</strong><br>如果类自定义了 <code>new /delete</code>,则优先调用它们。</p><pre><code class="language-C++">void *operator new(std::size_t size) _THROW_BAD_ALLOC{ if (size == 0) size = 1; void* p; while ((p = ::malloc(size)) == nullptr) { // If malloc fails and there is a new_handler, // call it to try free up memory. std::new_handler nh = std::get_new_handler(); if (nh) nh(); else#ifndef _LIBCPP_HAS_NO_EXCEPTIONS throw std::bad_alloc();#else break;#endif } return p;}voidoperator delete(void* ptr) noexcept{ ::free(ptr);}</code></pre><h2 id="System-Call">System Call</h2><p>程序中的内存分配有三个层次,如下图。<br>最终调用的还是Linux/Windows中的操作系统API:如sbrk, mmap… 因此需要重点掌握这些系统调用。</p><p><img src="../../images/memory-call.png" alt=""></p><h3 id="brk-sbrk">brk, sbrk</h3><blockquote><p><em>change data segment size</em></p></blockquote><p>参考阅读 <a href="https://www.cnblogs.com/sylar5/p/11508821.html">cnblog: brk 和 sbrk 区别</a></p><p>linux man 手册中描述两者的作用是改变 <code>data segment</code> 的结束地址。<br>通俗地理解就是,<code>brk</code>函数会重新设置 <code>heap</code> 的高位地址,而 <code>sbrk</code>函数会根据大小来调整 <code>heap</code> 的容量。</p><p>两个函数的定义如下:</p><pre><code class="language-C++"> #include <unistd.h>int brk(void *addr);void *sbrk(intptr_t increment);</code></pre><h3 id="mmap">mmap</h3><blockquote><p><em>map (or unmap) files or devices into memory</em></p></blockquote><pre><code class="language-C++">#include <sys/mman.h>void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);int munmap(void *addr, size_t length);</code></pre><h2 id="ptmalloc">ptmalloc</h2>]]></content>
<categories>
<category> C++ </category>
<category> C++ </category>
</categories>
<tags>
<tag> C++ </tag>
<tag> linux </tag>
</tags>
</entry>
<entry>
<title>日志:2023年7月</title>
<link href="/posts/2023-7/"/>
<url>/posts/2023-7/</url>
<content type="html"><![CDATA[<div class="hbe hbe-container" id="hexo-blog-encrypt" data-wpm="Oh, this is an invalid password. Check and try again, please." data-whm="OOPS, these decrypted content may changed, but you can still have a look."> <script id="hbeData" type="hbeData" data-hmacdigest="97e14be9e854e1b3237cbaf95d9dc3cf8f8b75fab2a1a1bc54fde494d8cb23cd">b3dc07a81f6459d120ce338ccca550463faa708b9b4d89df9ab14ba4edd809f60b32d15b5672ce84c5d84744f7f475de0b1305a637ea913974dff7410a14ed4dbbb40a5278eb14bbac34e59e12a284c7a294ab69a495d47e11e78ad50feb107058e3275f75d936a554eb95bedbf96a7783a11b9f4096cfe1861725619e5a5317633279c3d957779453d57b5fa0ba64fc50c8a8867249b1e4e1b679e8a138ffe4e44ee9fd3637dc99e76ff334587dd2acf41dd9805cce4bf275cc1a585ab11cc84c7949a8c4e30c48778c74ce922226777c41d0403e225f7f266e27ebdc913e4f3b0bc811d619052f5d10bd439c7abdb7fa6a5c74b5a8b4c939d638e648ea06be90c69907d299438ee2c58f9f5cd7175c2f88ff07ca6f3b74e3c1e7e424dc71a31cd92fca1339935722708f2b8548366dfbf220add8202ae63bd0f61d1e2b8c30858b8e5cb1961461f31aa3f597e5e1adb652a72f747502c39390ff9e737034a058c8b11d06863b6a5b5f18a769141cd8150b3ea85bb1fcc5cea71202f61cca8762fc37fd67889befbc47916f913e2eaf895c32be1a951878ab4084c3d1edb2064b915ad9e3bf068fd9971f373c39af676c5edce063465935625bca670a695cfa507b62838d46af4a3a09099647d87c4095d6987040ca113d938096f7e41421c4c42540e606f60e3c50871af5464d332b97615d22822f5b4de3b53ce8d075e087c3877da3a120a16f7843f00a382283056c5d46ae2fc0e6a171f53b4d0a505614245e4bd03f8ce78719740161abd0ec71e84075ae40c4eef290dac819a9f645843f06cc21796aab1595932bc05ac984401060207100fdac93fff9badd829fc74e4aa0ff13ad6de5f506abb0b92ca5094bc5701d737ab28659315e27d070c76c7706c6f59bade15915c6e8af214bf5d33e5079f8731299b0517e571656bba9a7f6dac72cbcb797357071cb2b1f3a2e8bc99f014ac31b7061759a41b78bedc543d61b02123bdb85dafed55536872f1a0dc38ee17408b28d3068b3380b45b8c9b7731b685ce690733fa7bb0b046762bc834cb6d3efce14c8bfb6afa611310c3113d3b013e4093659a2a48aa3089a1318ac8fcd09fff675f13a5233ab6384cf7925e71f21e353542b173c100abbcec5bf92fc11bb09f325d4ec782f0696e264e9cb221f2642675a442a6816d0aeb80f8e8d1aadd9fb58a0e922db618dcdaf16363fdf09978b0534ae4e82f629afd76021debd8d45523748fe3186c4d48eff1b6595de700c0a8c40dc8a16f1bb53369ee838147f0d5a4befc78d9967033a18982453c17291a958380f29e1e3b465b61fbe779710a948056c77454035a6a5668ed000848adbf7b32ed3ef3b468684f935e395e234f0ae038dd5e307672c0307feca67877cf94fa61a02dbed70dc0af8062bc7c2f2f28badb1efbfbab9d894e0faada08742527d8eec99a24809289b8d172f2671044a3f8e7af52d54d1a047608eed3812c87afe68392f8fed5d632ed3067b5f2044004674ff317dff9712ba114861b3f4dfa85bfe5c5945dca7edbfafffa8b31a713bfd00ceb01bcfebc88b71a24cde8aa420dd9e24782fcd074323667037681f430b19c6d33960a6e64132617487789c83e6d8e4e6368ef7ccc33cfd4d2e793c1cdd85220f79849282aa3b0647b633c35cc41ce2a95ad6e88b2514bc406caefc067c2c6b813640760aebbd31967d290c7465460a5653606ce7f031560448d18fa8a801cbd322a041491269b000a4c300ef2d5b2ea6737a19048f88fe9c3fe396b494606614240e4d40299d791fd209208b152306cdf5dd3a1b2d740b6c82a95663ab48899b29398a0471b01a6f21a4178f5ea3282f78a4c53cb4e2c55ea6305b8c31ba87014f627ab418a8268d05ab608d25a614c4e16302be97ca3ea984d8ce30fdaa5c0c1be976ed17e200e18572fd2aa32af070b9c0e92bf578edbd0999f078f2d5890d6dff137366e0b770cbdb3bf337f9dffd1d9df9e927552ac556c096fede1f78c0f3437be08b9b4c415b37372f8eeec4522a45b777fc42fb3de3022f276ae2a14c0be67a7b50aa7b88dcf1b9a2969ab6facd3171091137f5595ab72ea3f6794b6cd451b9fbc8ff63f5ae08050318651b4b7396e24357069e5b5b98d5e2420a1608d2bec7fc8a6291541b2e582ffccd9fe8f46829eec756351542037533ee005b49cf6ad720508c8ba52241df29490f736d43e4499dc19456630ab1b3f0523e5d3086e33f6894a67849ddf5df832343e2f25a51258ecd0976ab807c5c725c497551eaf7e18ea9b8ae42d58fb6036bb473f25cef48bac20cafb2680ce68f539de2aa25b514182a920363c7fe185fb2ae5bdd073fed91413cebcd6fab0042006aa984f132442a3eabb8b916221dc04e28b6fbc769f47af0ce4b48d8c26a0922ebc74decb4704d5081f3de44469ea1cee6479ec013bc7d3d01acfd074a1dd14714cc76ed0e0d40c0ae2e9272bc569d16d873945ea9b970472d1f14c06176b346393d85f2e250821022e6e0afc37b7de9ecc600defee81e84e6488a01327726484df413200d2452ceb4bf5cb646e4aa7414421005864f56e8f2ba147388b8b1baae2181b46c35d274835952f653c063266fff48f5b2544b4e796dcca215c4751d7cd32aea23ab9dfa3a734b207f7d242c9b233dc789acbdbcddd87d81a3c6e19ab6e80903b6ee969c979e474d4d9d6eefbdff0aca5705d5b6cc46ae392b43baf10b1c58db7c5f6a41c4f6f160e6c418f015f5ce902a777b9fb3a68c933ad43b785a713d592f90533d9e35cd46a52457821e478e81299fb886670c073434ab388a983c821f435c6a5e4fbe5467a51b4b64ffda543c596efd8ac17da0e35858ab328ca385342e6f48fea6e59cec55b96ee5a434e15db34246d5dd739ee47cf7327444b4a8ed9a34ad63ac5b67ac91fe50034d2c8040e13d0bc09a982d1dea672fb15741e8b4c32850ee74fed103e72ff6a2c209e144d6f40fc5000750037d9c8a26027943f81d6d18d51a62dbe6d9f5fa509026d84e1a04759e7bc5fa6a56810ebc0efac3e0b664050b40cb81d45e314c7f6359ae273dea19615be0231e71eb9b371b03e1927294f235df8258efde83e268cdf6287013c856c50dd270644dc0fe66bca434d88599ba2979ac98a49a1cbb1597fe888ce001365bbd36205714f8fe02c520a28d79e438443f801d2fbd75181939935fa514ab474be8be724be174ea15808bd311f77770c06c9e47aa5c333f1221c534263a48e9edec4f96655ccbfd2bfa0144eb9acc66c0c7ed770c6e994e39ea71ef0eeefc85d5446b537c4d444cc97ce842fa576526a5e72fcaae3030184fef7d61c964f58813633eb0d767591785324ae5555d29b2f41f94b7eec44ccb3395ac00e102be44dd5087956e4c75fc8520dc0b400f17c2254c126098aa7e4de23a4d1de8bee988069f3a55a21a51f1095105bd73fa67a7e61c9d973f8913b18f518b3a7a58b868f2085105d10c2c0bb7bc953fbe73248a20a3a13c1e60bab87f8a77e881d91e5c7610fddfdba3a12225e5abd6e314320936c28990e5a5e2b41de8a5fd0a62d7ceb97c4720753980c90d2239edf31e5a5b2d1ffa3670ef5ee81f55183e3507deb6d7fd3e725c1cb537f1f9af7efe8dc88a5f88699887a64c1c3b48c64f4af05f8b9a6438b1e138b6c5f16393a8ef1fe294a48cb441fa97363a4b7c9813a77b81b37ee8a8b15011b00846bffcab0d63ea748d98560edcd6f0cfafdae5954685387b758a8be894be25f8ff7ee14b97325172efd3540b6330c1614cc9762544042525b2bcf5746efe9d77e7d1b44779a5e70b50c56451d49b32f8ebe8d8988d6e717b5b66a73c99477098e1ebd46733d8bd4e601533e9ae10c12d2816bc6570a0b1b4cc5c8fadaa4bdccbc2a3c7ee08da116a07d5da8386e6b308126725e3bb4932e8e1c580f683f2b287788e6019f6a3bc988cffbcdcac0710c073069fa833dccfcc8a0f99d28d7fa69e19fc2c66db3611ea6f6e26091e414017a87aca34b52fc55c0e82201c58f3c4ddd6eb20d4b937df0011c764c38180c77f7906345ffb1bc2481a9a71e95478efb068c0f729103295395fe4e520409d1e34bcc11047f4232109c4bf58e6cc8468729f933d61c4838c84f2e51d1874d888d25104fb8907dc662506badf3468df6815e29b8cf87471827046b12ff4b32d1e2f9554398d6fec64a80bf07c646ea392a01a9100ed91fd36ac1aff05f9411b0ffd7fbc405edf970f897e9ca2d50ec34c635997e90d19fb5d157720f64b53bc942d84c9ff7ecbf4cd362b36711bb239812135bb53fd734f4f49752579105e281f3c9bb3e179e5dc07878bbbdc0947cba7c2d7b3082ea3fc49a9d83006827b2850485ca538215902ce96c835c7331f6e7241df62e5cadd5b213d7214951a8583833cc814db2ad9d53a89852465de9e185310955c8b0ca70440022dbaaf57f94b311fd4ea6f804c0f3e09131aecca1ec0632e0d7f51afa28878254cd26fa53119347fcff91f21abee456623734fb7b5374aafd46a76cba87041ca4cd9a70117ad157a6f0d80b06f8abb937c1f4c407994c487be113a0b76516989dfea1c8960227c5ed2037001c4ae4a1255a37916b07c73f84ecb0774f9b1664c3097043d936b8f86e4e1169a69ed9642f3f42f0bd978f870b7a8b8a77363c935ddba62db8122aa89ca7a31c2e64116ac055835f260c9909ccea7a8e4b2b9b21b3bfb393ad84a1008b10eae75daa7082b6b1ca529317d83dec85e6d26e01dbac488ca60af61be855014be1a31602a3be743a619f27dec9be89d5d012273f40d8f5f24aa14eb0a052e06674e20a33cebdbc9f6d3fabef0a1457cfcc1934ea78acdc93b31b0ceeec14a8b82a778a2ff01373d5d798d8686634cad90995475f6b9516b652fd3d7ebf89461a18f239747010baa7ea9162fad6feb1a5a8ca8ab00b33d8f511948ec004b4f1d8a88119069968118906c9cb16947e6466cf02cbb67b3e5c1078a7a3013a2d4dbcce04f4f83184a56c53df2a9f6b13b18392cb35ab054251518756c7707ba7d122ab0e872c9658db3167434a413a3d1a0154e53b660a270ffd9c3680d854ecf41ed637366e188630a4a33d1f7f0ce528cde65b51e42375024e2892f5613c5c1648a3425e713a7fec772355deef1515d4b839152a69d2a04477456c8e32de38a7b365cd7cac98240f47b29605838a62911f3b0afe0219dfd85a1d4eb0919ee0e944e48edd913ede081c769ade7e003785916b6aa8392019300e9de3019f8360574c0b59373e8cc97ce8f8a9d4bcbfecfae97890e955a1d395f8866b69aa1fc4d762075aab9cc7f02de0cb39e26a3ce8de63bd86ac238a94b96ea625cb2abdad9adf1a1998b410690ac48f92462ee997d1dfd30717f07ef368d0ff7d0e76297a410477a502c56bb8d5dbc464cab8165a8b1f28c8403b89205db9611ab4632b8d36bf726522de64c074831ce24a46523cb9f425149f6adf19a9a8b90e1ae59726e19a9fccfa885c957f2d768d663e6bd6cb2a81a829fcb59c6b50dc1b3fb3854f4d4f5382e5ab7768f21e3cf9bf4d67aeece25e00a07c2f095508f75defdf8f02855916f1af2d10cb5c624e27349696a9dbee336380ec1def04174218493332fefa47bbdbefde0f84768c260620c209e6632d305e07fd8beacf69e737f100a242e92cb1dc362a41f60b814a006b5910da961f816b60c658d82c45b235bb6deb24335971fa7b05d5821cb3246568034f54241765f9207a7ae30ebdb9171b8b0aa750095552a0fdb34288a2a45e19b6722b8bd1b0d51b73a50e6a333ff67e66a36880eb2148296c032454eb1b04c023caacbaf215dc049c8ac96334e163d8487e8ad576dd2c19d9d067fd78181407e907e34e625b4a6911f437a3a2f714f4b12807c1e2ae876c45cdfb25bc3df7a89a0ad179906644c253f6ce3afcb0bdf8d60e9dfc40fdfe0604d71eec4c21aa9cfe769a57b2fa453a4c1dc0025d2006d4fa5c51694e5c68e601d0fe1bec94b566e70d33d24d9f2079ebaead15915d931c9f925b14a4b7cf96d2c15bb373f1469b8c82fc0d8244cf841ad262a8c2a83ede69738f136edc452bda82e4f20b64ff8b9015ed73c32193ff9041860e8543d5af18833b4672860d23eed8b25cad8ecceaba4237b7e5e01be2084caa9de9092c001a15463bb6354cc7c3d21f2849b24e4215b4c3f23f2c94d36568840605d6b0dd1dbf6c3aca47abd3d640290628f189192be94d5c1dd0bfaf445781c85da0509eb4b1937e4a1d0dc2a18f7c417bc2344d3e1a1d38d1fec462bb3f8eecd1cc6068b1be349f39d42f0c5938f823cd37651df324ac31ff604e021abddfa7a57ad445d01ad434f97b776d8d4ef460714bd2aff528a73795d3ce9fecda6d94c74b857544b84cc03a26eed59b1f2f0188a6d9b6e04c6a1b8683ab75acbade2facea552f14e8845d7395bf81c58bc324795527280fdb99796aa41d6bd92a82eee812f4a1aa8aecbc90c3a298e2749d1c7ed7ea68cbc54ede310093acaf30fa37a0add2983a94fe0e70539588240fba297ad1f2f2ef30fa1b3595aff6e586fe5d527d8d4036c8489b4741e90b94f0a41ea5c4209d120df43a98488a67ab9d3eed4206fca4878b206b43ec193f085ffdb3a0fb5d0fd04d44bc524260d99afd0be26569946aa76a1287d8e911a78d1f1c18cb2b81b7f264253b09a355f94e7e851a68e7c1cb1ada23137b309b295b6c51d99b638a5b5a6c988cbae0844b93594e1b5761adbef0234bbf4b094f6f802bbe217029ee65a6e0ae4119a955fe4174c4f34ffe723c6f31942fbbb0320d7bdf204ed34869c382219705d35b9019b0ce4c8b3bae72c56303a314cf497334f6eb6eebc449e7e95a50eab56439a626c81f106b2032a5a4e76fd1c455bda921712b4819a8827c4517a29106a83d1ec535b94097fbc80500d13aa873bb84bdde0d3e78ca01e88b7c43da42953675a3152434caf7b4fa7c10e20061ec3353809c45325076ca42fdd7885bcbf8a93aaf1f4a4042f7ae5d4304e4b690c237c89f86b38b1123661ff89df59c9cf143dc314a6137e2448293c8a5f388e0386476ed28a76048b8911101d17ff2d7038eb12b8214db73f2849516189039c26d2a928c0769b3ea0e6564e8acbd1318aa3d33534e6f6e2e22cf14bb4e00ef121f3ec95150ad0979fd120cf9cafd1435c8f5698f4e44eb5b0ccf823dd890e70eda7393bf45daa52f12ab7bc927f031f79a570889b6b3bc3c5985a3b132e70824f4df0eedf749176ea169f0541441315f7a749f88dd2c6ec87ebe728550d2905f909bd440cf032da45f6deda7a22c839b015a732cb21eba03b074316d278cff03bfae6e083d15a77632066fd65261600e443ea8815565f07161ff2380ab82c01fc9aa5a84b8015aeacf4e3e1d69314de932b7088ace8f579ef4ba389975d05677b5417f6d0394dbc5e4ea351dcff2e79e798faae0062b2400ac36a42f9f38153053a4e14154a741a05a0010fa46f9416ddea29133f014ac415c947c8a8bb602aec9b4d2a18e25a5a77271d34f5f9bce14316374101a57c25cf85a01e7ce4dfe1e4fe3193700595473c8e5a3da9c67faba5e9c9529c20560797f7142c529af7d329ed3f9ecae21816aa8a3073d371d9c315e789d26795f5c14805b28c5ececa85e044ed79955feb0a48f1f25a645e1636</script> <div class="hbe hbe-content"> <div class="hbe hbe-input hbe-input-default"> <input class="hbe hbe-input-field hbe-input-field-default" type="password" id="hbePass"> <label class="hbe hbe-input-label hbe-input-label-default" for="hbePass"> <span class="hbe hbe-input-label-content hbe-input-label-content-default">Hey, password is required here.</span> </label> </div> </div></div><script data-pjax src="/lib/hbe.js"></script><link href="/css/hbe.style.css" rel="stylesheet" type="text/css">]]></content>
<categories>
<category> 个人日志 </category>
<category> 个人日志 </category>
</categories>
<tags>
<tag> vim </tag>
</tags>
</entry>
<entry>
<title>Google评分卡💯及自评</title>
<link href="/posts/grade/"/>
<url>/posts/grade/</url>
<content type="html"><![CDATA[<div class="admonition note"><p class="admonition-title">导读</p><ul><li>Google将技术能力划分为 0~11 的等级</li><li>自我评估技术,判断下一步需要作出的努力</li></ul></div><h2 id="↓-Self-Evaluation">↓ Self-Evaluation</h2><table><thead><tr><th style="text-align:left">↓ 基础要求</th><th style="text-align:center">grade</th></tr></thead><tbody><tr><td style="text-align:left">熟悉数据结构与算法</td><td style="text-align:center">2</td></tr><tr><td style="text-align:left">熟练使用 C++11</td><td style="text-align:center">3</td></tr><tr><td style="text-align:left">熟悉并掌握 C++高级特性 (14/17/20)</td><td style="text-align:center">1</td></tr><tr><td style="text-align:left">熟练使用 Python 等脚本语言</td><td style="text-align:center">4</td></tr><tr><td style="text-align:left">熟悉batch、Shell、Linux常见指令</td><td style="text-align:center">1</td></tr><tr><td style="text-align:left">熟悉MySQL等数据库的设计、优化</td><td style="text-align:center">0</td></tr><tr><td style="text-align:left">熟悉编译原理、编译优化</td><td style="text-align:center">1</td></tr><tr><td style="text-align:left">熟悉 vscode、sublime、vim 等IDE、Editor</td><td style="text-align:center">5</td></tr><tr><td style="text-align:left">熟悉 Jenkins、TeamCity 等 CI&CD 平台</td><td style="text-align:center">2</td></tr><tr><td style="text-align:left">熟悉 ChatGPT、CodeMaker 等 AI工具</td><td style="text-align:center">1</td></tr><tr><td style="text-align:left">熟悉Linux内核,如进程管理、内存管理、文件系统等</td><td style="text-align:center">1</td></tr><tr><td style="text-align:left">熟悉网络协议和网络编程,熟悉websocket、HTTP、socket、TCP/IP等</td><td style="text-align:center">1</td></tr><tr><td style="text-align:left">↓ <strong>C/C++领域</strong></td><td style="text-align:center"></td></tr><tr><td style="text-align:left">内存管理、内存分配、ASan原理、内存错误及排查</td><td style="text-align:center">1</td></tr><tr><td style="text-align:left">模板、SFINAE、type traits、metaprogramming</td><td style="text-align:center">1</td></tr><tr><td style="text-align:left">并发、memory order、同步、互斥、boost::asio</td><td style="text-align:center">0</td></tr><tr><td style="text-align:left">编译优化、SIMD、ISPC、CPU性能分析</td><td style="text-align:center">0</td></tr><tr><td style="text-align:left">C++编码规范</td><td style="text-align:center">2</td></tr><tr><td style="text-align:left">↓ <strong>Python2/3领域</strong></td><td style="text-align:center"></td></tr><tr><td style="text-align:left">Todo</td><td style="text-align:center"></td></tr><tr><td style="text-align:left">↓ <strong>工具/pipline领域</strong></td><td style="text-align:center"></td></tr><tr><td style="text-align:left">Jenkins 日常工作使用,无阻碍</td><td style="text-align:center">3</td></tr><tr><td style="text-align:left">VSCode 日常开发使用、多个插件开发经验</td><td style="text-align:center">5</td></tr><tr><td style="text-align:left">Vim 熟练使用、vimrc配置</td><td style="text-align:center">4</td></tr><tr><td style="text-align:left">Git 基本的GUI、CMD操作</td><td style="text-align:center">2 ~ 3</td></tr><tr><td style="text-align:left">↓ <strong>加分项</strong></td><td style="text-align:center"></td></tr><tr><td style="text-align:left">具备内存优化经验、熟悉linux内存分配</td><td style="text-align:center">1</td></tr><tr><td style="text-align:left">熟悉GPU使用,或有底层基础库(CUDA,mkl、openblas等)优化经验</td><td style="text-align:center">0 ~ 1</td></tr><tr><td style="text-align:left">良好的系统设计能力,如 performance、reliability、availability 多维度考量程序</td><td style="text-align:center">2</td></tr><tr><td style="text-align:left">熟悉机器学习平台相关工具,比如k8s,kubeflow,mlflow,automl等</td><td style="text-align:center">0</td></tr><tr><td style="text-align:left">有视频解码和渲染开发经验者优先</td><td style="text-align:center">2</td></tr><tr><td style="text-align:left">有存储系统、分布式系统等底层开发经验</td><td style="text-align:center">0</td></tr></tbody></table><br>## ↓ Google-Standards<table><thead><tr><th style="text-align:center">等级</th><th style="text-align:left">标准</th></tr></thead><tbody><tr><td style="text-align:center">0</td><td style="text-align:left">You are unfamiliar with the subject area</td></tr><tr><td style="text-align:center"></td><td style="text-align:left">一窍不通</td></tr><tr><td style="text-align:center">1</td><td style="text-align:left">You can read/understand the most fundamental aspects of the subject area</td></tr><tr><td style="text-align:center"></td><td style="text-align:left">理解基本概念</td></tr><tr><td style="text-align:center">2</td><td style="text-align:left">Ability to implement small changes,understand basic principles and able to figure out additional details with minimal help</td></tr><tr><td style="text-align:center"></td><td style="text-align:left">能够实现一些小改动,在别人帮助下钻研更多细节</td></tr><tr><td style="text-align:center">3</td><td style="text-align:left">Basic proficiency in a subject area without relying on help</td></tr><tr><td style="text-align:center"></td><td style="text-align:left">基本掌握和熟练使用</td></tr><tr><td style="text-align:center">4</td><td style="text-align:left">You are comfortable with the subject area and all routine work on it</td></tr><tr><td style="text-align:center"></td><td style="text-align:left">足够精通,足够应对所有日常工作</td></tr><tr><td style="text-align:center">5</td><td style="text-align:left">An even lower degree of reliance on reference materials. Deeper skills in a field or specific technology in the subject area.</td></tr><tr><td style="text-align:center"></td><td style="text-align:left">深耕某个细分领域</td></tr><tr><td style="text-align:center">6</td><td style="text-align:left">Ability to develop large programs and systems from scratch</td></tr><tr><td style="text-align:center"></td><td style="text-align:left">独立开发大型系统</td></tr><tr><td style="text-align:center">7~10</td><td style="text-align:left">脚踏实地慢慢来吧…</td></tr></tbody></table>]]></content>
<categories>
<category> 个人日志 </category>
<category> 个人日志 </category>
</categories>
<tags>
<tag> Python </tag>
<tag> C++ </tag>
<tag> linux </tag>
<tag> vim </tag>
</tags>
</entry>
<entry>
<title>【网络】HTTP协议进阶</title>
<link href="/posts/http-2/"/>
<url>/posts/http-2/</url>
<content type="html"><![CDATA[<div class="admonition note"><p class="admonition-title">导读</p><ul><li><a href="https://learn.lianglianglee.com/%E4%B8%93%E6%A0%8F/%E9%80%8F%E8%A7%86HTTP%E5%8D%8F%E8%AE%AE">专栏:透视HTTP协议</a> 墙裂推荐 ⭐</li><li><a href="/posts/http-1/">HTTP协议入门</a></li></ul></div><div class="markmap-container" style="height:500px"> <svg data="{"t":"root","d":0,"v":"","c":[{"t":"heading","d":1,"p":{"lines":[0,1]},"v":"HTTP数据编码","c":[{"t":"heading","d":2,"p":{"lines":[1,2]},"v":"<a href=\"#MIME-type\">MIME-type</a>"},{"t":"heading","d":2,"p":{"lines":[2,3]},"v":"&quot;Accept&quot;"},{"t":"heading","d":2,"p":{"lines":[3,4]},"v":"&quot;Content-Type&quot;"}]},{"t":"heading","d":1,"p":{"lines":[4,5]},"v":"HTTP大文件","c":[{"t":"heading","d":2,"p":{"lines":[5,6]},"v":"数据压缩"},{"t":"heading","d":2,"p":{"lines":[6,7]},"v":"chunked传输"}]},{"t":"heading","d":1,"p":{"lines":[7,8]},"v":"HTTP连接","c":[{"t":"heading","d":2,"p":{"lines":[8,9]},"v":"短连接"},{"t":"heading","d":2,"p":{"lines":[9,10]},"v":"长连接"},{"t":"heading","d":2,"p":{"lines":[10,11]},"v":"<a href=\"#队首阻塞\">队首阻塞</a>"}]},{"t":"heading","d":1,"p":{"lines":[11,12]},"v":"<a href=\"#Cookie\">Cookie</a>","c":[{"t":"heading","d":2,"p":{"lines":[12,13]},"v":"<a href=\"#Cookie原理\">Cookie原理</a>","c":[{"t":"heading","d":3,"p":{"lines":[13,14]},"v":"<a href=\"#Cookie生命周期\">Cookie生命周期</a>"},{"t":"heading","d":3,"p":{"lines":[14,15]},"v":"<a href=\"#Cookie作用域\">Cookie作用域</a>"}]},{"t":"heading","d":2,"p":{"lines":[15,16]},"v":"Cookie应用","c":[{"t":"heading","d":3,"p":{"lines":[16,17]},"v":"<a href=\"#Cookie应用:身份识别⭐\">身份识别</a>"},{"t":"heading","d":3,"p":{"lines":[17,18]},"v":"<a href=\"#Cookie应用:广告追踪\">广告追踪</a>"}]}]},{"t":"heading","d":1,"p":{"lines":[18,19]},"v":"<a href=\"#HTTP代理\">HTTP代理</a>","c":[{"t":"heading","d":2,"p":{"lines":[19,20]},"v":"<a href=\"#代理字段\">代理字段</a>"},{"t":"heading","d":2,"p":{"lines":[20,21]},"v":"<a href=\"#代理协议\">代理协议</a>"},{"t":"heading","d":2,"p":{"lines":[21,22]},"v":"<a href=\"#负载均衡\">负载均衡</a>"}]},{"t":"heading","d":1,"p":{"lines":[22,23]},"v":"Cache","c":[{"t":"heading","d":2,"p":{"lines":[23,24]},"v":"<a href=\"#Cache:浏览器\">浏览器cache</a>"},{"t":"heading","d":2,"p":{"lines":[24,25]},"v":"<a href=\"#Cache:服务器\">服务器cache</a>"}]},{"t":"heading","d":1,"p":{"lines":[25,26]},"v":"<a href=\"#Chrome调试\">Chrome调试</a>"}],"p":{}}"></svg></div><h2 id="HTTP数据编码">HTTP数据编码</h2><p><font color="#FF1E10"><strong>todo</strong></font></p><h3 id="MIME-type">MIME-type</h3><p>使用svn更新的时候有一栏会标注 <code>Mime type</code>,可以观察到除了常见代码文件外,都是以 <code>application/octet-stream</code> 格式传输,它代表未知的二进制数据。</p><p><img src="../../images/svn-mime-type.png" alt=""></p><h2 id="HTTP大文件">HTTP大文件</h2><p><font color="#FF1E10"><strong>todo</strong></font></p><h2 id="HTTP连接">HTTP连接</h2><p><a href="/posts/http-1/#TCP">前面说过</a>,HTTP协议 是运行在 TCP/IP协议 之上,因此每一次新的HTTP连接,都需要经过TCP协议的 <strong>“3次握手 & 4次挥手”</strong>,这无形中降低了HTTP协议连接的代价。</p><p>因为 TCP位于传输层,HTTP位于应用层,所以可以用如下的类比来理解连接的代价:</p><ul><li>开关机:TCP连接</li><li>使用电脑办公:HTTP连接</li></ul><p>每次使用电脑办公,都需要打开电脑,在使用完毕后又需要关闭电脑。这就好比 <strong>HTTP 短连接</strong>。而更常规地做法是,保持电脑的始终开启,这样利于随时使用,这就好比 <strong>HTTP 长连接</strong>。</p><p><img src="https://learn.lianglianglee.com/%E4%B8%93%E6%A0%8F/%E9%80%8F%E8%A7%86HTTP%E5%8D%8F%E8%AE%AE/assets/57b3d80234a1f1b8c538a376aa01d3b4.png" alt=""></p><h3 id="Connection字段">Connection字段</h3><p>当HTTP请求采取长连接时,在响应报文的 “Connection” 字段会标记为 <strong>keep-alive</strong>,此时服务器不会在短时间内断开连接,但是为了降低服务器的无效占用,Web-Server 往往会在一段时间内若没有任何数据收发,便会主动断开连接,断开后会收到 “Connection: close” 的字段。</p><h3 id="队首阻塞">队首阻塞</h3><p>因为 HTTP协议 采取 “一问一答” 的模式,即典型的 <strong>FIFO</strong> 结构,当队首的请求因为处理太慢而耽误时间,那么队列后面的所有请求也会相应地被阻塞,这就是 <a href="https://en.wikipedia.org/wiki/Head-of-line_blocking"><strong>Head-of-line blocking</strong></a>。</p><p>类比理解为:食堂排队打饭,每次刷卡是一次 Request,每次领到饭是一次 Response,每处理完一次成对的 Request-Response,队伍才能往前推进一步。只要前面打饭的慢了,后面所有人都会受影响。</p><p>解决方案是:并发连接,即增加打饭的窗口。这里不详细介绍。</p><h2 id="Cookie">Cookie</h2><p>前面说过 <strong>HTTP连接 是无状态的,即没有任何记忆</strong>。即使某个请求会让服务器出现500的错误,下次请求时服务器依然会 “热情招待”。这迫切得需要一种缓存的机制,Cookie应运而生。</p><blockquote><p>Cookie 是服务器委托浏览器存储的一些数据,让服务器有了“记忆能力”</p></blockquote><h3 id="Cookie原理">Cookie原理</h3><p>Response报文中,<strong>利用 <code>Set-Cookie</code>字段发送多个 “key=value” 形式的 cookie值</strong>,这些会由浏览器负责记录下来。当浏览器下次访问同样的地址时,Request报文会自动利用 <code>Cookie</code>字段将本地缓存的 cookie 发送给服务器,这样服务器就知道自己的身份了。</p><p>因为 Cookie 是与浏览器绑定的,如果你换个浏览器或者换台电脑,就会丢失之前的 Cookie记录,此时服务器也会重走一遍新的 <code>Set-Cookie</code> 流程。</p><h3 id="Cookie生命周期">Cookie生命周期</h3><p>Cookie拥有自己的生命周期,它通过 <code>Expires</code> 或 <code>Max-Age</code> 两个字段实现。当超过标记的有效期后,浏览器会自动在本地删除记录,不会再通过HTTP请求发送给服务器。</p><ul><li><code>Expires</code>:记录“过期时间”,如 <code>Fri, 07-Jun-23 20:00:00 GMT</code></li><li><code>Max-Age</code>:记录“保质期”,单秒是秒。将浏览器收到相应的时间加上 <code>Max-Age</code>,即得到 <code>Expires</code></li></ul><h3 id="Cookie作用域">Cookie作用域</h3><p>浏览器会存储大量的Cookies,因此需要标记其作用域,即发送给哪个服务器或者URL,常用字段是:(不清楚的推荐阅读 <a href="/posts/http-1/#URL">HTTP协议之URL</a>)</p><ul><li><code>Domain</code>:域名</li><li><code>Path</code>:路径</li></ul><h3 id="Cookie应用:身份识别⭐">Cookie应用:身份识别⭐</h3><p>登录taobao等电商网站时,浏览器会自动保存你的登录账户(或密码),就便是利用cookies实现的。它同时还会记录你的浏览记录和购物车。</p><p>大概格式为:<code>name=xxxxx....</code></p><h3 id="Cookie应用:广告追踪">Cookie应用:广告追踪</h3><p>当你浏览各种网站时,它们会根据你的访问喜好作行为分析,然后定向推荐一些图片广告给你,这就是利用cookies的原理。<br>这部分成为 “第三方Cookie”,属于搜集用户隐私的行为,浏览器经常会弹出确认框以请求权限。</p><h2 id="HTTP代理">HTTP代理</h2><p>传统的HTTP请求是 <code>Client-Server</code>,现在常常有“第三者插足”,即在中间会引入一个 <strong>代理服务器(Proxy Server)</strong>,它的角色是双面的:</p><ul><li>面对上游:充当客户端,发送请求</li><li>面对下游:充当服务端,响应请求</li></ul><div class="admonition note"><p class="admonition-title">类比:消费者(浏览器)—— 便利店(代理)—— 源服务器(批发市场)</p></div><div class="admonition note"><p class="admonition-title">定理:计算机领域的任何问题,都可以通过引入一个中间层来解决</p></div><p><img src="../../images/proxy-server.png" alt=""></p><h3 id="代理字段">代理字段</h3><p><strong>代理服务器 通过字段 <code>Via</code> 标明代理的身份</strong>,在HTTP请求的链路中,每当报文经过一个代理节点,代理服务器就会将自身的信息追加到 <code>Via</code>字段的末尾。<br>另通过如下字段标明其他信息:</p><ul><li><code>X-Forwarded-For</code>:追加代理的域名</li><li><code>X-Real-IP</code>:客户端真实IP</li></ul><p><img src="../../images/proxy-process.png" alt=""></p><h3 id="代理协议">代理协议</h3><p>针对代理的HTTP请求,<a href="https://www.haproxy.org/">HAProxy</a> 公司推出了专门的代理请求协议,The Proxy Protocol。<br>其基本格式为:开头必须是“PROXY”五个大写字母,然后是“TCP4”或者“TCP6”,表示客户端的 IP 地址类型,再后面是请求方地址、应答方地址、请求方端口号、应答方端口号,最后用一个回车换行(\r\n)结束。</p><pre><code>PROXY TCP4 1.15.115.4 110.42.228.178 32200 80\r\n</code></pre><h3 id="负载均衡">负载均衡</h3><p>当一个区域所有的消费者,都蜂拥而至一个批发市场购物,就会造成堵塞排场对的现象。<br>因此 “负载均衡” 的解决方案是,在每个居民集中地地区设置一个小商超,或者经销商,而自己只负责货物的批发和调配。消费者 择近择闲 选择小商超去购物即可。</p><p><strong>通过中间的代理服务器,将请求均匀合理地分散到多台源服务器</strong>,能够有效提高系统的响应速度和利用率,这就是 负载均衡 的基本原理。</p><p>而如何挑选转发的服务器,有如下的思路:</p><ul><li>哈希:如尾数单号的去A服,尾数双号的去B服</li><li>轮询:对于新请求,分配一个最空闲的Server去处理</li></ul><h2 id="Cache">Cache</h2><div class="admonition note"><p class="admonition-title">两句话讲清楚Cache</p><ul><li>浏览器Cache:消费者家里囤(上次买的)货</li><li>服务器Cache:小商超囤(上次卖的)货</li></ul></div><h3 id="Cache:浏览器">Cache:浏览器</h3><p>当浏览器频繁每秒请求同样的数据时,如果服务器不厌其烦的依次发送,会造成很大的性能和流量浪费。因此需要客户端(即浏览器)的缓存。</p><p><strong>一个带Cache的HTTP请求流程是:</strong></p><ul><li><p>浏览器检查cache,若有则直接读取,若无则发送新的HTTP请求;</p></li><li><p>服务器响应请求,并返回资源,同时标记资源的有限期;</p></li><li><p>浏览器接受请求,并缓存资源;</p></li></ul><p>而<strong>标记资源的有效期字段是 <code>max-age</code></strong>,即cache的生存时间(秒),过期则被浏览器自动销毁。<br>其他常见字段有:</p><ul><li><code>no_store</code>:不允许缓存,如一些高频的秒杀字段</li><li><code>no_cache</code>:使用缓存前,检查是否有最新版本</li><li><code>muster-revalidate</code>:不过期则直接使用缓存</li></ul><h3 id="Cache:服务器">Cache:服务器</h3><p><font color="#FF1E10"><strong>Todo</strong></font>,与HTTP协议关系不大,可以了解 Redis、Varnish 等缓存技术。</p><h2 id="Chrome调试">Chrome调试</h2><p>Chrome浏览器提供了丰富而强大的调试功能,按下 <code>F12</code> 或者右键点击“检查” 以进入调试页面。</p><img src="/images/chrome-http-debug.png" height="400">]]></content>
<categories>
<category> Network </category>
<category> Network </category>
</categories>
</entry>