-
Notifications
You must be signed in to change notification settings - Fork 1
/
index.html
1452 lines (1034 loc) · 126 KB
/
index.html
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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2">
<meta name="theme-color" content="#222">
<meta name="generator" content="Hexo 7.3.0">
<link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png">
<link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png">
<link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png">
<link rel="mask-icon" href="/images/logo.svg" color="#222">
<link rel="stylesheet" href="/css/main.css">
<link rel="stylesheet" href="//fonts.googleapis.com/css?family=Lato:300,300italic,400,400italic,700,700italic&display=swap&subset=latin,latin-ext">
<link rel="stylesheet" href="/lib/font-awesome/css/all.min.css">
<script id="hexo-configurations">
var NexT = window.NexT || {};
var CONFIG = {"hostname":"shaffer.cn","root":"/","scheme":"Muse","version":"7.8.0","exturl":false,"sidebar":{"position":"right","display":"post","padding":18,"offset":12,"onmobile":false},"copycode":{"enable":true,"show_result":true,"style":"flat"},"back2top":{"enable":true,"sidebar":false,"scrollpercent":true},"bookmark":{"enable":false,"color":"#222","save":"auto"},"fancybox":false,"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"algolia":{"hits":{"per_page":10},"labels":{"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}},"localsearch":{"enable":true,"trigger":"auto","top_n_per_article":5,"unescape":"enable","preload":false},"motion":{"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}}};
</script>
<meta name="description" content="一只生之无趣死之乏味的丧家之犬">
<meta property="og:type" content="website">
<meta property="og:title" content="朱晓峰">
<meta property="og:url" content="https://shaffer.cn/">
<meta property="og:site_name" content="朱晓峰">
<meta property="og:description" content="一只生之无趣死之乏味的丧家之犬">
<meta property="og:locale" content="en_US">
<meta property="article:author" content="Shaffer John">
<meta property="article:tag" content="朱晓峰,Shaffer John,博客">
<meta name="twitter:card" content="summary">
<link rel="canonical" href="https://shaffer.cn/">
<script id="page-configurations">
// https://hexo.io/docs/variables.html
CONFIG.page = {
sidebar: "",
isHome : true,
isPost : false,
lang : 'en'
};
</script>
<title>朱晓峰</title>
<noscript>
<style>
.use-motion .brand,
.use-motion .menu-item,
.sidebar-inner,
.use-motion .post-block,
.use-motion .pagination,
.use-motion .comments,
.use-motion .post-header,
.use-motion .post-body,
.use-motion .collection-header { opacity: initial; }
.use-motion .site-title,
.use-motion .site-subtitle {
opacity: initial;
top: initial;
}
.use-motion .logo-line-before i { left: initial; }
.use-motion .logo-line-after i { right: initial; }
</style>
</noscript>
</head>
<body itemscope itemtype="http://schema.org/WebPage">
<div class="container use-motion">
<div class="headband"></div>
<header class="header" itemscope itemtype="http://schema.org/WPHeader">
<div class="header-inner"><div class="site-brand-container">
<div class="site-nav-toggle">
<div class="toggle" aria-label="Toggle navigation bar">
<span class="toggle-line toggle-line-first"></span>
<span class="toggle-line toggle-line-middle"></span>
<span class="toggle-line toggle-line-last"></span>
</div>
</div>
<div class="site-meta">
<a href="/" class="brand" rel="start">
<span class="logo-line-before"><i></i></span>
<h1 class="site-title">朱晓峰</h1>
<span class="logo-line-after"><i></i></span>
</a>
<p class="site-subtitle" itemprop="description">一只生之无趣死之乏味的丧家之犬</p>
</div>
<div class="site-nav-right">
<div class="toggle popup-trigger">
<i class="fa fa-search fa-fw fa-lg"></i>
</div>
</div>
</div>
<nav class="site-nav">
<ul id="menu" class="main-menu menu">
<li class="menu-item menu-item-home">
<a href="/" rel="section"><i class="fa fa-home fa-fw"></i>Home</a>
</li>
<li class="menu-item menu-item-about">
<a href="/about/" rel="section"><i class="fa fa-user fa-fw"></i>About</a>
</li>
<li class="menu-item menu-item-photography">
<a href="/photography/" rel="section"><i class="fa fa-camera fa-fw"></i>Photography</a>
</li>
<li class="menu-item menu-item-search">
<a role="button" class="popup-trigger"><i class="fa fa-search fa-fw"></i>Search
</a>
</li>
</ul>
</nav>
<div class="search-pop-overlay">
<div class="popup search-popup">
<div class="search-header">
<span class="search-icon">
<i class="fa fa-search"></i>
</span>
<div class="search-input-container">
<input autocomplete="off" autocapitalize="off"
placeholder="Searching..." spellcheck="false"
type="search" class="search-input">
</div>
<span class="popup-btn-close">
<i class="fa fa-times-circle"></i>
</span>
</div>
<div id="search-result">
<div id="no-result">
<i class="fa fa-spinner fa-pulse fa-5x fa-fw"></i>
</div>
</div>
</div>
</div>
</div>
</header>
<div class="back-to-top">
<i class="fa fa-arrow-up"></i>
<span>0%</span>
</div>
<div class="reading-progress-bar"></div>
<main class="main">
<div class="main-inner">
<div class="content-wrap">
<div class="content index posts-expand">
<article itemscope itemtype="http://schema.org/Article" class="post-block" lang="en">
<link itemprop="mainEntityOfPage" href="https://shaffer.cn/golang/false-sharing/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="image" content="/images/avatar.jpg">
<meta itemprop="name" content="Shaffer John">
<meta itemprop="description" content="一只生之无趣死之乏味的丧家之犬">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="朱晓峰">
</span>
<header class="post-header">
<h2 class="post-title" itemprop="name headline">
<a href="/golang/false-sharing/" class="post-title-link" itemprop="url">False sharing 伪共享问题</a>
</h2>
<div class="post-meta">
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar"></i>
</span>
<span class="post-meta-item-text">Posted on</span>
<time title="Created: 2024-07-06 22:41:23" itemprop="dateCreated datePublished" datetime="2024-07-06T22:41:23+08:00">2024-07-06</time>
</span>
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar-check"></i>
</span>
<span class="post-meta-item-text">Edited on</span>
<time title="Modified: 2024-07-07 03:16:28" itemprop="dateModified" datetime="2024-07-07T03:16:28+08:00">2024-07-07</time>
</span>
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-folder"></i>
</span>
<span class="post-meta-item-text">In</span>
<span itemprop="about" itemscope itemtype="http://schema.org/Thing">
<a href="/categories/golang/" itemprop="url" rel="index"><span itemprop="name">golang</span></a>
</span>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<h1 id="Cache-Line"><a href="#Cache-Line" class="headerlink" title="Cache Line"></a>Cache Line</h1><p>数据经过主存、L3、L2、L1 Cache到CPU寄存器进行运算,访问的速度逐级提高,L3一般为多核共享,L2、L1一般为单核本地使用。</p>
<p>按照局部性原理,正在使用的数据的附近数据也大概率会被使用,所以CPU会按照<code>Cache Line</code>为一个单位加载一批连续的数据到缓存里,主流CPU一般为64B</p>
<p>例如你有一个数组,每个元素的类型为int64,大小为8B,当你用到第一个元素时,CPU会将另外7个元素也一起作为一个<code>Cache Line</code>加载到各级缓存中</p>
<h1 id="什么是False-sharing伪共享"><a href="#什么是False-sharing伪共享" class="headerlink" title="什么是False sharing伪共享"></a>什么是False sharing伪共享</h1><p><img src="/../images/false_sharing_01.png" alt="false sharing"></p>
<p>两个线程修改同一条<code>Cache Line</code>上的不同位置,发生了缓存不一致的情况,会导致L1、L2缓存上这条<code>Cache Line</code>被认为是无效数据</p>
<p>此时就需要<code>MESI缓存一致性协议</code>和<code>RFO请求</code>核间通信方式,来标记自己本地缓存上<code>Cache Line</code>的状态并通知另一个核是否需要更新缓存,最后通过相对比较慢的<code>L3 Cache</code>同步数据,甚至穿透到主存,总之这些都会带来开销</p>
<p>所以在这种情况下没有真正达到共享的目的,此为伪共享(其实应该翻译为错误共享)</p>
<p>那延伸一下,<code>Cache Line</code>如果大小为1B一个单位,只要不是修改到同一个字节的位置,那是不是就没有这个问题了?</p>
<p>但随之而来的是局部性的损失以及控制电路复杂度的提高,设计就是不断权衡利弊,这是另一个话题了</p>
<h1 id="如何验证伪共享问题呢,我要眼见为实"><a href="#如何验证伪共享问题呢,我要眼见为实" class="headerlink" title="如何验证伪共享问题呢,我要眼见为实"></a>如何验证伪共享问题呢,我要眼见为实</h1><h2 id="复现"><a href="#复现" class="headerlink" title="复现"></a>复现</h2><figure class="highlight golang"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"fmt"</span></span><br><span class="line"> <span class="string">"time"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment">// a demo for false share</span></span><br><span class="line"><span class="keyword">type</span> MyInt64 <span class="keyword">struct</span> {</span><br><span class="line"> val <span class="type">int64</span> <span class="comment">// 8Byte</span></span><br><span class="line">}</span><br><span class="line"><span class="keyword">var</span> (</span><br><span class="line"> N = <span class="number">100000000</span></span><br><span class="line"> M = <span class="number">8</span></span><br><span class="line"> List = <span class="built_in">new</span>([<span class="number">8</span>]MyInt64)</span><br><span class="line">)</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">run</span><span class="params">(idx <span class="type">int</span>)</span></span> {</span><br><span class="line"> <span class="keyword">for</span> i:=N; i><span class="number">0</span>; i-- {</span><br><span class="line"> List[idx].val = <span class="type">int64</span>(i)</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> ch := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="type">int</span>, M) <span class="comment">// exit signal</span></span><br><span class="line"> start := time.Now()</span><br><span class="line"> <span class="keyword">for</span> i:=<span class="number">0</span>; i<M; i++ {</span><br><span class="line"> <span class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">(i <span class="type">int</span>)</span></span> {</span><br><span class="line"> run(i)</span><br><span class="line"> ch <- <span class="number">1</span></span><br><span class="line"> }(i)</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">for</span> i:=<span class="number">0</span>; i<M; i++ {</span><br><span class="line"> <-ch</span><br><span class="line"> }</span><br><span class="line"> fmt.Printf(<span class="string">"%v\n"</span>, time.Since(start))</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>我们构造一个长度为8的数组,每个元素都是一个大小为64bit的类型,共占64Byte</p>
<p>然后用8个协程分别更新各自索引位置的元素</p>
<p>执行 <code>go run main.go</code></p>
<p>此时耗时约在秒级:2.5329389s</p>
<h2 id="改进"><a href="#改进" class="headerlink" title="改进"></a>改进</h2><p>如果我们加上一些padding,此时数组中每个元素都是64Byte的类型,每个元素都占满一个<code>Cache</code>,使得每个核操作不同的<code>Cache Line</code></p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">type padding int64</span><br><span class="line">type MyInt64 struct {</span><br><span class="line"> val int64 // 8Byte</span><br><span class="line"> pad1, pad2, pad3, pad4, pad5, pad6, pad7 padding // 56Byte</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>耗时约在百毫秒级别:113.9928ms</p>
<h1 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h1><p>所以可见,如果我们编写的程序恰巧遇到了<code>False Sharing</code>,会导致严重的性能问题。</p>
<p>目前<code>perf-c2c</code>等工具可以检测伪共享问题。开源的<code>Disruptor</code>框架也详细阐述了该问题</p>
<p>Golang在<code>sync.Pool</code>中也有对应的设计</p>
<figure class="highlight golang"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">type</span> poolLocal <span class="keyword">struct</span> {</span><br><span class="line"> poolLocalInternal</span><br><span class="line"></span><br><span class="line"> <span class="comment">// Prevents false sharing on widespread platforms with</span></span><br><span class="line"> <span class="comment">// 128 mod (cache line size) = 0 .</span></span><br><span class="line"> pad [<span class="number">128</span> - unsafe.Sizeof(poolLocalInternal{})%<span class="number">128</span>]<span class="type">byte</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h1 id="但是真的结束了吗?"><a href="#但是真的结束了吗?" class="headerlink" title="但是真的结束了吗?"></a>但是真的结束了吗?</h1><p>如果在上面的代码中不用数组,而是用slice会怎样?</p>
<p>slice底层是什么结构,会有什么影响?</p>
<figure class="highlight golang"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">type</span> slice <span class="keyword">struct</span> {</span><br><span class="line"> array unsafe.Pointer</span><br><span class="line"> <span class="built_in">len</span> <span class="type">int</span></span><br><span class="line"> <span class="built_in">cap</span> <span class="type">int</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<hr>
<p>如果改成8个pad会怎样?哪种对齐的结构对编译器更加友好?</p>
<p>(实际8个pad速度更快,可以试试)</p>
<p>7个pad</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">MOVQ AX, SI</span><br><span class="line">SHLQ $6, AX</span><br><span class="line">MOVQ DX, (BX)(AX*1)</span><br><span class="line">DECQ DX</span><br><span class="line">MOVQ SI, AX</span><br><span class="line">TESTQ DX, DX</span><br><span class="line">JLE 71</span><br><span class="line">MOVQ main.List+8(SB), CX</span><br><span class="line">MOVQ main.List(SB), BX</span><br><span class="line">NOP</span><br><span class="line">CMPQ AX, CX</span><br></pre></td></tr></table></figure>
<p>8个pad</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">LEAQ (AX)(AX*8), SI</span><br><span class="line">MOVQ DX, (BX)(SI*8)</span><br><span class="line">DECQ DX</span><br><span class="line">TESTQ DX, DX</span><br><span class="line">JLE 56</span><br><span class="line">MOVQ main.List(SB), BX</span><br><span class="line">TESTB AL, (BX)</span><br><span class="line">CMPQ AX, $8</span><br></pre></td></tr></table></figure>
<hr>
<p>如果关闭编译优化会怎样?或者说编译优化究竟在优化什么?</p>
<p>关闭编译优化:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line">go build -gcflags="-N -l -S" main.go</span><br><span class="line">0x000e 00014 (main.go:18) MOVQ AX, main.idx+40(SP)</span><br><span class="line">0x0013 00019 (main.go:19) MOVQ main.N(SB), DX</span><br><span class="line">0x001a 00026 (main.go:19) MOVQ DX, main.i+16(SP)</span><br><span class="line">0x001f 00031 (main.go:19) NOP</span><br><span class="line">0x0020 00032 (main.go:19) JMP 34</span><br><span class="line">0x0022 00034 (main.go:19) CMPQ main.i+16(SP), $0</span><br><span class="line">0x0028 00040 (main.go:19) JGT 44</span><br><span class="line">0x002a 00042 (main.go:19) JMP 91</span><br><span class="line">0x002c 00044 (main.go:20) MOVQ main.idx+40(SP), AX</span><br><span class="line">0x0031 00049 (main.go:20) MOVQ main.List+8(SB), CX</span><br><span class="line">0x0038 00056 (main.go:20) MOVQ main.List(SB), DX</span><br><span class="line">0x003f 00063 (main.go:20) MOVQ main.i+16(SP), BX</span><br><span class="line">0x0044 00068 (main.go:20) CMPQ CX, AX</span><br><span class="line">0x0047 00071 (main.go:20) JHI 75</span><br><span class="line">0x0049 00073 (main.go:20) JMP 101</span><br><span class="line">0x004b 00075 (main.go:20) LEAQ (DX)(AX*8), DX</span><br><span class="line">0x004f 00079 (main.go:20) MOVQ BX, (DX)</span><br><span class="line">0x0052 00082 (main.go:20) JMP 84</span><br><span class="line">0x0054 00084 (main.go:19) DECQ main.i+16(SP)</span><br><span class="line">0x0059 00089 (main.go:19) JMP 34</span><br><span class="line">0x005b 00091 (main.go:22) MOVQ 24(SP), BP</span><br><span class="line">0x0060 00096 (main.go:22) ADDQ $32, SP</span><br><span class="line">0x0064 00100 (main.go:22) RET</span><br><span class="line">0x0065 00101 (main.go:20) PCDATA $1, $0</span><br><span class="line">0x0065 00101 (main.go:20) CALL runtime.panicIndex(SB)</span><br><span class="line">0x006a 00106 (main.go:20) XCHGL AX, AX</span><br></pre></td></tr></table></figure>
<p>开启编译优化:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">go build -gcflags=-S main.go</span><br><span class="line">0x000e 00014 (main.go:19) MOVQ main.N(SB), DX</span><br><span class="line">0x0015 00021 (main.go:19) JMP 32</span><br><span class="line">0x0017 00023 (main.go:20) MOVQ DX, (BX)(AX*8)</span><br><span class="line">0x001b 00027 (main.go:19) DECQ DX</span><br><span class="line">0x001e 00030 (main.go:19) NOP</span><br><span class="line">0x0020 00032 (main.go:19) TESTQ DX, DX</span><br><span class="line">0x0023 00035 (main.go:19) JLE 58</span><br><span class="line">0x0025 00037 (main.go:20) MOVQ main.List+8(SB), CX</span><br><span class="line">0x002c 00044 (main.go:20) MOVQ main.List(SB), BX</span><br><span class="line">0x0033 00051 (main.go:20) CMPQ AX, CX</span><br><span class="line">0x0036 00054 (main.go:20) JCS 23</span><br><span class="line">0x0038 00056 (main.go:20) JMP 68</span><br><span class="line">0x003a 00058 (main.go:22) MOVQ 16(SP), BP</span><br><span class="line">0x003f 00063 (main.go:22) ADDQ $24, SP</span><br><span class="line">0x0043 00067 (main.go:22) RET</span><br><span class="line">0x0044 00068 (main.go:20) PCDATA $1, $0</span><br><span class="line">0x0044 00068 (main.go:20) CALL runtime.panicIndex(SB)</span><br><span class="line">0x0049 00073 (main.go:20) XCHGL AX, AX</span><br></pre></td></tr></table></figure>
<p>可以看到把很多操作压在<code>DX</code>寄存器里操作了,不用内存取值了</p>
</div>
<footer class="post-footer">
<div class="post-eof"></div>
</footer>
</article>
<article itemscope itemtype="http://schema.org/Article" class="post-block" lang="en">
<link itemprop="mainEntityOfPage" href="https://shaffer.cn/astronomy/spectrum-107-Piscium/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="image" content="/images/avatar.jpg">
<meta itemprop="name" content="Shaffer John">
<meta itemprop="description" content="一只生之无趣死之乏味的丧家之犬">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="朱晓峰">
</span>
<header class="post-header">
<h2 class="post-title" itemprop="name headline">
<a href="/astronomy/spectrum-107-Piscium/" class="post-title-link" itemprop="url">107 Piscium 恒星光谱计划</a>
</h2>
<div class="post-meta">
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar"></i>
</span>
<span class="post-meta-item-text">Posted on</span>
<time title="Created: 2022-08-13 03:42:02" itemprop="dateCreated datePublished" datetime="2022-08-13T03:42:02+08:00">2022-08-13</time>
</span>
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar-check"></i>
</span>
<span class="post-meta-item-text">Edited on</span>
<time title="Modified: 2024-07-07 01:19:51" itemprop="dateModified" datetime="2024-07-07T01:19:51+08:00">2024-07-07</time>
</span>
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-folder"></i>
</span>
<span class="post-meta-item-text">In</span>
<span itemprop="about" itemscope itemtype="http://schema.org/Thing">
<a href="/categories/astronomy/" itemprop="url" rel="index"><span itemprop="name">astronomy</span></a>
</span>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>记录一下实现一个想法的过程</p>
<h1 id="真实影像"><a href="#真实影像" class="headerlink" title="真实影像"></a>真实影像</h1><h2 id="远程天文台拍摄过程"><a href="#远程天文台拍摄过程" class="headerlink" title="远程天文台拍摄过程"></a>远程天文台拍摄过程</h2><p><img src="/images/107_psc_deepsky_shooting.jpg" alt="107_psc_deepsky_shooting"></p>
<h2 id="原图"><a href="#原图" class="headerlink" title="原图"></a>原图</h2><p>略</p>
<h2 id="处理过程"><a href="#处理过程" class="headerlink" title="处理过程"></a>处理过程</h2><p><img src="/images/107_psc_pixinsight.png" alt="107_psc_pixinsight"></p>
<h1 id="光谱图像制作"><a href="#光谱图像制作" class="headerlink" title="光谱图像制作"></a>光谱图像制作</h1><h2 id="获取原始数据"><a href="#获取原始数据" class="headerlink" title="获取原始数据"></a>获取原始数据</h2><ol>
<li>数据源:<a href="https://www.eso.org/sci/observing/phase3/data_streams.html">https://www.eso.org/sci/observing/phase3/data_streams.html</a></li>
<li>搜索HD编号:<code>HD 10476</code>,找到两份光谱数据,选择合适的下载,最好包含可见光波段</li>
</ol>
<p><img src="/images/107_psc_search_spectrum.png" alt="107_psc_search_spectrum"></p>
<h2 id="处理光谱数据"><a href="#处理光谱数据" class="headerlink" title="处理光谱数据"></a>处理光谱数据</h2><ol>
<li>读取数据,并转换合适的单位</li>
<li>降采样(可选)</li>
<li>Savitzky-Golay滤波</li>
<li>输出</li>
</ol>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> os</span><br><span class="line"><span class="keyword">from</span> astropy.io <span class="keyword">import</span> fits</span><br><span class="line"><span class="keyword">from</span> scipy.signal <span class="keyword">import</span> savgol_filter</span><br><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line"></span><br><span class="line"><span class="comment"># 导入文件</span></span><br><span class="line">filepath = os.path.join(<span class="string">r'./spectrum'</span>, <span class="string">'ADP.2016-09-27T07_02_47.049.fits'</span>)</span><br><span class="line">f = fits.<span class="built_in">open</span>(filepath)</span><br><span class="line">spec = f[<span class="number">1</span>]</span><br><span class="line">data = spec.data</span><br><span class="line"></span><br><span class="line"><span class="comment"># 获取数据,并转换单位</span></span><br><span class="line">wave_nm = [wvl/<span class="number">10</span> <span class="keyword">for</span> wvl <span class="keyword">in</span> <span class="built_in">list</span>(data.field(<span class="number">0</span>)[<span class="number">0</span>])]</span><br><span class="line">flux = <span class="built_in">list</span>(data.field(<span class="number">1</span>)[<span class="number">0</span>])</span><br><span class="line"></span><br><span class="line"><span class="comment"># 降采样</span></span><br><span class="line">wave_nm = wave_nm[<span class="number">0</span>::<span class="number">3</span>]</span><br><span class="line">flux = flux[<span class="number">0</span>::<span class="number">3</span>]</span><br><span class="line"></span><br><span class="line"><span class="comment"># Savitzky-Golay滤波</span></span><br><span class="line">flux = savgol_filter(flux, <span class="number">53</span>, <span class="number">1</span>, mode= <span class="string">'nearest'</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 设置输出图片尺寸、标题、纵轴、横轴</span></span><br><span class="line">plt.figure().set_size_inches(<span class="number">24</span>, <span class="number">4</span>)</span><br><span class="line">plt.title(<span class="string">'HD 10476 Spectrum Comparison'</span>)</span><br><span class="line">plt.xlabel(<span class="string">'Wavelength (nm)'</span>)</span><br><span class="line">plt.ylabel(<span class="string">'Flux (ADU s-1 mW-1)'</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 绘制</span></span><br><span class="line">plt.plot(wave_nm, flux)</span><br><span class="line">plt.show()</span><br></pre></td></tr></table></figure>
<p>滤波前的结果</p>
<p><img src="/images/107_psc_specturm_no_filter.png" alt="107_psc_specturm_no_filter"></p>
<p>滤波后的结果</p>
<p><img src="/images/107_psc_specturm.png" alt="107_psc_specturm"></p>
<h2 id="创建渐变光谱背景"><a href="#创建渐变光谱背景" class="headerlink" title="创建渐变光谱背景"></a>创建渐变光谱背景</h2><p>由于我的Adobe Photoshop 2022没有自带的光谱渐变,所以我只能手工创建</p>
<p>色阶并不是均分的,因为在人眼可见光中,红光占的谱线宽度比别的颜色要更宽,所以在编辑颜色的渐变时,给红色到黑色更长的渐变过程</p>
<p><img src="/images/107_psc_PS_gradient_editor.png" alt="107_psc_PS_gradient_editor"></p>
<p>合并图层</p>
<p><img src="/images/107_psc_PS_gradient_merge_layer.png" alt="107_psc_PS_gradient_merge_layer"></p>
<h2 id="用universe-sandbox软件做模拟效果图"><a href="#用universe-sandbox软件做模拟效果图" class="headerlink" title="用universe sandbox软件做模拟效果图"></a>用universe sandbox软件做模拟效果图</h2><p><img src="/images/107_psc_universe_sandbox.png" alt="107_psc_universe_sandbox"></p>
<h2 id="最后PS合成"><a href="#最后PS合成" class="headerlink" title="最后PS合成"></a>最后PS合成</h2><ol>
<li>添加背景</li>
<li>调整光谱背景的对比度</li>
<li>将python导出的光谱曲线导入</li>
<li>增加波长刻度</li>
<li>增加黑色的竖线,调整透明度</li>
<li>增加文字,外发光效果</li>
</ol>
<p><img src="/images/107_psc_PS_all_layers.png" alt="107_psc_PS_all_layers"></p>
<h1 id="后记"><a href="#后记" class="headerlink" title="后记"></a>后记</h1><p>我发现天文数据虽然很公开,但都是在自己平台发布自己天文设备采集到的数据,导致搜寻数据困难,而且数据格式不一,虽然有很多数据整合平台,但感觉反而更乱了,花了很大精力,开了无数个chrome标签页,爆了无数次内存,才终于找到可见光波段的光谱数据</p>
<p>滤波调参时,有一个重要参考,至少要将钠的D1、D2两条很近的吸收带分辨出来</p>
<p>虽然全球各地有很多远程天文台可以租用,总有夜晚晴朗的天文台,但仍然很容易观测失败,例如天空突然飞来一堆云、对焦失败、赤道仪故障星点拉线等</p>
<p><strong>用恒星光谱作为礼物可能是个还不错的选择,但这不是流水线式的工作,最重要的还是真诚。</strong></p>
</div>
<footer class="post-footer">
<div class="post-eof"></div>
</footer>
</article>
<article itemscope itemtype="http://schema.org/Article" class="post-block" lang="en">
<link itemprop="mainEntityOfPage" href="https://shaffer.cn/golang/golang-panic-problem-2/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="image" content="/images/avatar.jpg">
<meta itemprop="name" content="Shaffer John">
<meta itemprop="description" content="一只生之无趣死之乏味的丧家之犬">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="朱晓峰">
</span>
<header class="post-header">
<h2 class="post-title" itemprop="name headline">
<a href="/golang/golang-panic-problem-2/" class="post-title-link" itemprop="url">Golang panic疑难杂症(二)</a>
</h2>
<div class="post-meta">
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar"></i>
</span>
<span class="post-meta-item-text">Posted on</span>
<time title="Created: 2021-08-16 23:55:00" itemprop="dateCreated datePublished" datetime="2021-08-16T23:55:00+08:00">2021-08-16</time>
</span>
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar-check"></i>
</span>
<span class="post-meta-item-text">Edited on</span>
<time title="Modified: 2024-07-07 01:19:51" itemprop="dateModified" datetime="2024-07-07T01:19:51+08:00">2024-07-07</time>
</span>
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-folder"></i>
</span>
<span class="post-meta-item-text">In</span>
<span itemprop="about" itemscope itemtype="http://schema.org/Thing">
<a href="/categories/golang/" itemprop="url" rel="index"><span itemprop="name">golang</span></a>
</span>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<h1 id="panic日志"><a href="#panic日志" class="headerlink" title="panic日志"></a>panic日志</h1><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x512f5d]</span><br><span class="line"></span><br><span class="line">goroutine 48438156 [running]:</span><br><span class="line">encoding/json.(*encodeState).marshal.func1(0xc009decb40)</span><br><span class="line"> /go/src/encoding/json/encode.go:326 +0x9a</span><br><span class="line">panic(0xd41f60, 0x187d170)</span><br><span class="line"> /go/src/runtime/panic.go:967 +0x15d</span><br><span class="line">encoding/json.(*encodeState).string(0xc004eba100, 0x0, 0x7, 0x1)</span><br><span class="line"> /go/src/encoding/json/encode.go:1003 +0x5d</span><br><span class="line">encoding/json.stringEncoder(0xc004eba100, 0xce54e0, 0xc008bfeea0, 0x198, 0x100)</span><br><span class="line"> /go/src/encoding/json/encode.go:644 +0x358</span><br><span class="line">encoding/json.structEncoder.encode(0xc00451c000, 0x13, 0x21, 0xc0058d99e0, 0xc004eba100, 0xe29520, 0xc008bfeea0, 0x199, 0xd30100)</span><br><span class="line"> /go/src/encoding/json/encode.go:758 +0x2bb</span><br><span class="line">encoding/json.ptrEncoder.encode(0xc0058d9a70, 0xc004eba100, 0xd26880, 0xc008bfeea0, 0x16, 0xd20100)</span><br><span class="line"> /go/src/encoding/json/encode.go:914 +0x116</span><br><span class="line">encoding/json.(*encodeState).reflectValue(0xc004eba100, 0xd26880, 0xc008bfeea0, 0x16, 0xc009de0100)</span><br><span class="line"> /go/src/encoding/json/encode.go:358 +0x82</span><br><span class="line">encoding/json.(*encodeState).marshal(0xc004eba100, 0xd26880, 0xc008bfeea0, 0x450100, 0x0, 0x0)</span><br><span class="line"> /go/src/encoding/json/encode.go:330 +0xf0</span><br><span class="line">encoding/json.Marshal(0xd26880, 0xc008bfeea0, 0x18cf180, 0x8, 0xc0029067a8, 0xc002038c40, 0x15)</span><br><span class="line"> /go/src/encoding/json/encode.go:161 +0x52</span><br><span class="line">...</span><br></pre></td></tr></table></figure>
<p>可以看到,是json encode string的时候发生的panic,这种情况比较少见。</p>
<p>重点是这一行日志:</p>
<p><code>encoding/json.(*encodeState).string(0xc004eba100, 0x0, 0x7, 0x1)</code></p>
<p><code>string()</code>函数的入参有0x0和0x7</p>
<p>实际上这里的0x0是指字符串变量指向内容的指针为0,表示长度的变量为0x7</p>
<h1 id="原理"><a href="#原理" class="headerlink" title="原理"></a>原理</h1><p><code>string</code>类型底层实现是一个指向内容的指针和一个表示长度的int</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// StringHeader is the runtime representation of a string.</span></span><br><span class="line"><span class="comment">// It cannot be used safely or portably and its representation may</span></span><br><span class="line"><span class="comment">// change in a later release.</span></span><br><span class="line"><span class="comment">// Moreover, the Data field is not sufficient to guarantee the data</span></span><br><span class="line"><span class="comment">// it references will not be garbage collected, so programs must keep</span></span><br><span class="line"><span class="comment">// a separate, correctly typed pointer to the underlying data.</span></span><br><span class="line"><span class="keyword">type</span> StringHeader <span class="keyword">struct</span> {</span><br><span class="line"> Data <span class="type">uintptr</span></span><br><span class="line"> Len <span class="type">int</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>对字符串的赋值,分为两部分,所以在go里对字符串进行并发读写的话,有可能会导致指向的内容和长度不匹配,导致一些奇怪的问题。</p>
<p>比如两个协程同时并发读写同一个字符串变量,一个赋值为”123”,另一个赋值为”123456”,该字符串有可能为”123”,但长度是6。</p>
<p>但是就算并发读写,Data指针指向的一直是正常地址,只是长度不一定匹配,又怎么会导致nil pointer的呢?</p>
<p>其实,在go里,有一种特殊优化,如果你把字符串赋值为””,即空字符串的话,Data指针是0,也即空指针。</p>
<p>我们来验证一下:</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"fmt"</span></span><br><span class="line"> <span class="string">"time"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> AudioClips <span class="keyword">struct</span> {</span><br><span class="line"> Param *TableStruct</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> TableStruct <span class="keyword">struct</span> {</span><br><span class="line"> RequestId <span class="type">string</span></span><br><span class="line"> Type <span class="type">string</span></span><br><span class="line"> Organization <span class="type">string</span></span><br><span class="line"> BtId <span class="type">string</span></span><br><span class="line"> AppId <span class="type">string</span></span><br><span class="line"> TokenId <span class="type">string</span></span><br><span class="line"> Channel <span class="type">string</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">f</span><span class="params">(org, serviceId, appId, channel <span class="type">string</span>)</span></span> {</span><br><span class="line"> curTime := time.Now().Format(layout)</span><br><span class="line"> fmt.Sprintf(<span class="string">"%s@%s@%s@%s@%s@%s"</span>, curTime, org, serviceId, appId, channel, <span class="string">"length"</span>)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> layout <span class="type">string</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> a := AudioClips{</span><br><span class="line"> Param: &TableStruct{</span><br><span class="line"> AppId: <span class="string">""</span>,</span><br><span class="line"> Organization: <span class="string">"org"</span>,</span><br><span class="line"> Channel: <span class="string">"channel"</span>,</span><br><span class="line"> },</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">for</span> {</span><br><span class="line"> f(a.Param.Organization, <span class="string">"serviceId"</span>, a.Param.AppId, a.Param.Channel)</span><br><span class="line"> }</span><br><span class="line"> }()</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> {</span><br><span class="line"> <span class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> {a.Param.AppId = <span class="string">""</span>}()</span><br><span class="line"> time.Sleep(<span class="number">10</span>*time.Nanosecond)</span><br><span class="line"> <span class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> {a.Param.AppId = <span class="string">"default"</span>}()</span><br><span class="line"> time.Sleep(<span class="number">10</span>*time.Nanosecond)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>运行后发生panic</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line">$ go run main.go</span><br><span class="line">panic: runtime error: invalid memory address or nil pointer dereference</span><br><span class="line">[signal 0xc0000005 code=0x0 addr=0x0 pc=0xe570a2]</span><br><span class="line"></span><br><span class="line">goroutine 34 [running]:</span><br><span class="line">fmt.(*buffer).writeString(...)</span><br><span class="line"> C:/Program Files/Go/src/fmt/print.go:82</span><br><span class="line">fmt.(*fmt).padString(0xc0000202b0, 0x0, 0x7)</span><br><span class="line"> C:/Program Files/Go/src/fmt/format.go:110 +0x98</span><br><span class="line">fmt.(*fmt).fmtS(0xc0000202b0, 0x0, 0x7)</span><br><span class="line"> C:/Program Files/Go/src/fmt/format.go:359 +0x68</span><br><span class="line">fmt.(*pp).fmtString(0xc000020270, 0x0, 0x7, 0x21700000073)</span><br><span class="line"> C:/Program Files/Go/src/fmt/print.go:446 +0x1d2</span><br><span class="line">fmt.(*pp).printArg(0xc000020270, 0xead6c0, 0xc0003de2b0, 0x73)</span><br><span class="line"> C:/Program Files/Go/src/fmt/print.go:694 +0x875</span><br><span class="line">fmt.(*pp).doPrintf(0xc000020270, 0xec9c4d, 0x11, 0xc000097f18, 0x6, 0x6)</span><br><span class="line"> C:/Program Files/Go/src/fmt/print.go:1026 +0x168</span><br><span class="line">fmt.Sprintf(0xec9c4d, 0x11, 0xc000097f18, 0x6, 0x6, 0x0, 0x0)</span><br><span class="line"> C:/Program Files/Go/src/fmt/print.go:219 +0x6d</span><br><span class="line">main.f(0xec799a, 0x3, 0xec8574, 0x9, 0x0, 0x7, 0xec7fd9, 0x7)</span><br><span class="line"> C:/Projects/test/11/main.go:43 +0x1de</span><br><span class="line">main.main.func1(0xc000194000)</span><br><span class="line"> C:/Projects/test/11/main.go:59 +0x8a</span><br><span class="line">created by main.main</span><br><span class="line"> C:/Projects/test/11/main.go:57 +0x91</span><br><span class="line">exit status 2</span><br></pre></td></tr></table></figure>
<p>看下汇编代码,略有精简</p>
<p><code>go tool compile -S -N -l main.go</code></p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line">...</span><br><span class="line">"".main.func2 STEXT size=92 args=0x8 locals=0x8</span><br><span class="line"> 0x0000 00000 (main.go:47) TEXT "".main.func2(SB), ABIInternal, $8-8</span><br><span class="line"> 0x0000 00000 (main.go:47) MOVQ (TLS), CX</span><br><span class="line"> 0x0009 00009 (main.go:47) CMPQ SP, 16(CX)</span><br><span class="line"> 0x000d 00013 (main.go:47) PCDATA $0, $-2</span><br><span class="line"> 0x000d 00013 (main.go:47) JLS 85</span><br><span class="line"> 0x000f 00015 (main.go:47) PCDATA $0, $-1</span><br><span class="line"> 0x000f 00015 (main.go:47) SUBQ $8, SP</span><br><span class="line"> 0x0013 00019 (main.go:47) MOVQ BP, (SP)</span><br><span class="line"> 0x0017 00023 (main.go:47) LEAQ (SP), BP</span><br><span class="line"> 0x001b 00027 (main.go:47) MOVQ "".a+16(SP), CX</span><br><span class="line"> 0x0020 00032 (main.go:47) TESTB AL, (CX)</span><br><span class="line"> 0x0022 00034 (main.go:47) MOVQ $0, 72(CX)</span><br><span class="line"> 0x002a 00042 (main.go:47) LEAQ 64(CX), DI</span><br><span class="line"> 0x002e 00046 (main.go:47) CMPL runtime.writeBarrier(SB), $0</span><br><span class="line"> 0x0035 00053 (main.go:47) JEQ 57</span><br><span class="line"> 0x0037 00055 (main.go:47) JMP 76</span><br><span class="line"> 0x0039 00057 (main.go:47) MOVQ $0, 64(CX)</span><br><span class="line"> 0x0041 00065 (main.go:47) JMP 67</span><br><span class="line"> 0x0043 00067 (main.go:47) PCDATA $0, $-1</span><br><span class="line"> 0x0043 00067 (main.go:47) PCDATA $1, $-1</span><br><span class="line"> 0x0043 00067 (main.go:47) MOVQ (SP), BP</span><br><span class="line"> 0x0047 00071 (main.go:47) ADDQ $8, SP</span><br><span class="line"> 0x004b 00075 (main.go:47) RET</span><br><span class="line">...</span><br></pre></td></tr></table></figure>
<p><code>MOVQ $0, 72(CX)</code> 表示把立即数0存入CX地址+72,即把长度0存入Len</p>
<p><code>MOVQ $0, 64(CX)</code> 表示把立即数0存入CX地址+64,即指向字符串内容的指针值为0,也就是nil pointer</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line">...</span><br><span class="line">"".main.func3 STEXT size=100 args=0x8 locals=0x8</span><br><span class="line"> 0x0000 00000 (main.go:49) TEXT "".main.func3(SB), ABIInternal, $8-8</span><br><span class="line"> 0x0000 00000 (main.go:49) MOVQ (TLS), CX</span><br><span class="line"> 0x0009 00009 (main.go:49) CMPQ SP, 16(CX)</span><br><span class="line"> 0x000d 00013 (main.go:49) PCDATA $0, $-2</span><br><span class="line"> 0x000d 00013 (main.go:49) JLS 93</span><br><span class="line"> 0x000f 00015 (main.go:49) PCDATA $0, $-1</span><br><span class="line"> 0x000f 00015 (main.go:49) SUBQ $8, SP</span><br><span class="line"> 0x0013 00019 (main.go:49) MOVQ BP, (SP)</span><br><span class="line"> 0x0017 00023 (main.go:49) LEAQ (SP), BP</span><br><span class="line"> 0x001b 00027 (main.go:49) MOVQ "".a+16(SP), CX</span><br><span class="line"> 0x0020 00032 (main.go:49) TESTB AL, (CX)</span><br><span class="line"> 0x0022 00034 (main.go:49) MOVQ $7, 72(CX)</span><br><span class="line"> 0x002a 00042 (main.go:49) LEAQ 64(CX), DI</span><br><span class="line"> 0x002e 00046 (main.go:49) PCDATA $0, $-2</span><br><span class="line"> 0x002e 00046 (main.go:49) PCDATA $1, $-2</span><br><span class="line"> 0x002e 00046 (main.go:49) CMPL runtime.writeBarrier(SB), $0</span><br><span class="line"> 0x0035 00053 (main.go:49) JEQ 57</span><br><span class="line"> 0x0037 00055 (main.go:49) JMP 79</span><br><span class="line"> 0x0039 00057 (main.go:49) LEAQ go.string."default"(SB), AX</span><br><span class="line"> 0x0040 00064 (main.go:49) MOVQ AX, 64(CX)</span><br><span class="line"> 0x0044 00068 (main.go:49) JMP 70</span><br><span class="line"> 0x0046 00070 (main.go:49) PCDATA $0, $-1</span><br><span class="line"> 0x0046 00070 (main.go:49) PCDATA $1, $-1</span><br><span class="line"> 0x0046 00070 (main.go:49) MOVQ (SP), BP</span><br><span class="line"> 0x004a 00074 (main.go:49) ADDQ $8, SP</span><br><span class="line"> 0x004e 00078 (main.go:49) RET</span><br><span class="line">...</span><br></pre></td></tr></table></figure>
<p><code>MOVQ $7, 72(CX)</code> 表示把立即数7存入CX地址+72,即把长度0存入Len</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">LEAQ go.string."default"(SB), AX</span><br><span class="line">MOVQ AX, 64(CX)</span><br></pre></td></tr></table></figure>
<p>表示把指向”default”的地址存入AX,再把AX存入64(CX),即把指向字符串内容的指针赋值为”default”的地址</p>
<p>所以在json encode这个字符串的时候,可能会发生字符串有长度,但指向内容的指针是空指针,</p>
<p>可以通过len(str)>0这样的判断,但真正去取值的时候就发生了nil pointer panic。</p>
<h1 id="更进一步"><a href="#更进一步" class="headerlink" title="更进一步"></a>更进一步</h1><p>所以可见,如果某个字符串变量存储了很多的内容,可以通过赋值为空字符串进行释放。</p>
<p>那么问题又来了,如果按上面的分析,赋值为空字符串,指针就会为空,那为什么如下代码没有发生panic</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">a := <span class="string">""</span></span><br><span class="line">p := &a</span><br></pre></td></tr></table></figure>
<p>其实编译器会判断到如果这个空字符串在后续是有取指针操作的话,加上如下汇编代码:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">0x003c 00060 (main.go:9) MOVQ "".&p+24(SP), DI</span><br><span class="line">0x0041 00065 (main.go:9) MOVQ "".&a+16(SP), AX</span><br><span class="line">0x0046 00070 (main.go:9) CMPL runtime.writeBarrier(SB), $0</span><br><span class="line">0x004d 00077 (main.go:9) JEQ 81</span><br><span class="line">0x004f 00079 (main.go:9) JMP 95</span><br><span class="line">0x0051 00081 (main.go:9) MOVQ AX, (DI)</span><br><span class="line">0x0054 00084 (main.go:9) JMP 86</span><br></pre></td></tr></table></figure>
<p>以保证可以正常取到指针</p>
</div>
<footer class="post-footer">
<div class="post-eof"></div>
</footer>
</article>
<article itemscope itemtype="http://schema.org/Article" class="post-block" lang="en">
<link itemprop="mainEntityOfPage" href="https://shaffer.cn/golang/golang-panic-problem-1/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="image" content="/images/avatar.jpg">
<meta itemprop="name" content="Shaffer John">
<meta itemprop="description" content="一只生之无趣死之乏味的丧家之犬">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="朱晓峰">
</span>
<header class="post-header">
<h2 class="post-title" itemprop="name headline">
<a href="/golang/golang-panic-problem-1/" class="post-title-link" itemprop="url">Golang panic疑难杂症(一)</a>
</h2>
<div class="post-meta">
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar"></i>
</span>
<span class="post-meta-item-text">Posted on</span>
<time title="Created: 2021-08-16 21:52:31" itemprop="dateCreated datePublished" datetime="2021-08-16T21:52:31+08:00">2021-08-16</time>
</span>
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar-check"></i>
</span>
<span class="post-meta-item-text">Edited on</span>
<time title="Modified: 2024-07-07 01:19:51" itemprop="dateModified" datetime="2024-07-07T01:19:51+08:00">2024-07-07</time>
</span>
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-folder"></i>
</span>
<span class="post-meta-item-text">In</span>
<span itemprop="about" itemscope itemtype="http://schema.org/Thing">
<a href="/categories/golang/" itemprop="url" rel="index"><span itemprop="name">golang</span></a>
</span>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<p>项目里经常出现<code>panic: assignment to entry in nil map</code>的错误,问题其实很简单,但第一眼不一定能看出来。</p>
<p>复现demo:</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"math/rand"</span></span><br><span class="line"> <span class="string">"runtime"</span></span><br><span class="line"> <span class="string">"sync"</span></span><br><span class="line"> <span class="string">"time"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> Task <span class="keyword">struct</span> {</span><br><span class="line"> Result <span class="type">string</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(t *Task)</span></span> f(wg *sync.WaitGroup) <span class="keyword">map</span>[<span class="type">string</span>]<span class="keyword">interface</span>{} {</span><br><span class="line"> <span class="keyword">defer</span> wg.Done()</span><br><span class="line"> <span class="comment">// do something</span></span><br><span class="line"> time.Sleep(<span class="number">10</span>*time.Microsecond)</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">map</span>[<span class="type">string</span>]<span class="keyword">interface</span>{}{t.Result: rand.Int()}</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">run</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">var</span> wg sync.WaitGroup</span><br><span class="line"> tasks := []Task{</span><br><span class="line"> {Result: <span class="string">"task1"</span>},</span><br><span class="line"> {Result: <span class="string">"task2"</span>},</span><br><span class="line"> }</span><br><span class="line"> result := <span class="built_in">make</span>([]<span class="keyword">map</span>[<span class="type">string</span>]<span class="keyword">interface</span>{}, <span class="number">2</span>)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> idx, task := <span class="keyword">range</span> tasks {</span><br><span class="line"> wg.Add(<span class="number">1</span>)</span><br><span class="line"> <span class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">(i <span class="type">int</span>, t Task)</span></span> {</span><br><span class="line"> result[i] = t.f(&wg)</span><br><span class="line"> }(idx, task)</span><br><span class="line"> }</span><br><span class="line"> wg.Wait()</span><br><span class="line"></span><br><span class="line"> result[<span class="number">0</span>][<span class="string">"test"</span>] = <span class="number">0</span></span><br><span class="line"> result[<span class="number">1</span>][<span class="string">"test"</span>] = <span class="number">1</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> runtime.GOMAXPROCS(<span class="number">2</span>)</span><br><span class="line"> <span class="keyword">for</span> i := <span class="number">0</span>; i < <span class="number">10000</span>; i++ {</span><br><span class="line"> run()</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>代码逻辑是期望并发执行两个任务,然后使用得到的结果,但却发生了panic。</p>
<p>原因是在错误的地方使用了<code>defer wg.Done()</code>,内部函数f执行结束后,还没有将返回值赋值给<code>result[i]</code>,主线程就继续运行了,进而发生nil map panic。</p>
<p>前人挖的坑,后人的kpi。</p>
</div>
<footer class="post-footer">
<div class="post-eof"></div>
</footer>
</article>
<article itemscope itemtype="http://schema.org/Article" class="post-block" lang="en">
<link itemprop="mainEntityOfPage" href="https://shaffer.cn/uncategorized/qq_group_invite/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="image" content="/images/avatar.jpg">
<meta itemprop="name" content="Shaffer John">
<meta itemprop="description" content="一只生之无趣死之乏味的丧家之犬">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="朱晓峰">
</span>
<header class="post-header">
<h2 class="post-title" itemprop="name headline">
<a href="/uncategorized/qq_group_invite/" class="post-title-link" itemprop="url">欢迎志同道合的朋友一起交流</a>
</h2>
<div class="post-meta">
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar"></i>
</span>
<span class="post-meta-item-text">Posted on</span>
<time title="Created: 2021-07-12 01:53:33" itemprop="dateCreated datePublished" datetime="2021-07-12T01:53:33+08:00">2021-07-12</time>
</span>
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar-check"></i>
</span>
<span class="post-meta-item-text">Edited on</span>
<time title="Modified: 2024-07-07 01:19:51" itemprop="dateModified" datetime="2024-07-07T01:19:51+08:00">2024-07-07</time>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<p>QQ群号:116356047</p>
<p>技术、投资、游戏等各种爱好都可以分享讨论。</p>
<p><img src="/images/qrcode_qq_group.png"></p>
</div>
<footer class="post-footer">
<div class="post-eof"></div>
</footer>
</article>
<article itemscope itemtype="http://schema.org/Article" class="post-block" lang="en">
<link itemprop="mainEntityOfPage" href="https://shaffer.cn/uncategorized/gitlab-ee-upgrade-13-to-14/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="image" content="/images/avatar.jpg">
<meta itemprop="name" content="Shaffer John">
<meta itemprop="description" content="一只生之无趣死之乏味的丧家之犬">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="朱晓峰">
</span>
<header class="post-header">
<h2 class="post-title" itemprop="name headline">
<a href="/uncategorized/gitlab-ee-upgrade-13-to-14/" class="post-title-link" itemprop="url">Gitlab EE 从13.x版本升级至14.x注意点</a>
</h2>
<div class="post-meta">
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar"></i>
</span>
<span class="post-meta-item-text">Posted on</span>
<time title="Created: 2021-07-12 01:31:04" itemprop="dateCreated datePublished" datetime="2021-07-12T01:31:04+08:00">2021-07-12</time>
</span>
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar-check"></i>
</span>
<span class="post-meta-item-text">Edited on</span>
<time title="Modified: 2024-07-07 01:19:51" itemprop="dateModified" datetime="2024-07-07T01:19:51+08:00">2024-07-07</time>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<p>Gitlab 14.x相较于13.x删除了<code>unicorn</code>,使用<code>puma</code>。所以需要注释掉<code>gitlab.rb</code>中所有关于<code>unicorn</code>选项,打开<code>puma</code>的相关选项。</p>
<p>提供一份配置示例如下:</p>
<figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">################################################################################</span></span><br><span class="line"><span class="comment">## GitLab Unicorn</span></span><br><span class="line"><span class="comment">##! Tweak unicorn settings.</span></span><br><span class="line"><span class="comment">##! Docs: https://docs.gitlab.com/omnibus/settings/unicorn.html</span></span><br><span class="line"><span class="comment">################################################################################</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># unicorn['enable'] = true</span></span><br><span class="line"><span class="comment"># unicorn['worker_timeout'] = 60</span></span><br><span class="line"><span class="comment">###! Minimum worker_processes is 2 at this moment</span></span><br><span class="line"><span class="comment">###! See https://gitlab.com/gitlab-org/gitlab-ce/issues/18771</span></span><br><span class="line"><span class="comment"># unicorn['worker_processes'] = 2</span></span><br><span class="line"><span class="comment"># unicorn['worker_memory_limit_min'] = "250 * 1 << 20"</span></span><br><span class="line"><span class="comment"># unicorn['worker_memory_limit_max'] = "400 * 1 << 20"</span></span><br><span class="line"></span><br><span class="line"><span class="comment">################################################################################</span></span><br><span class="line"><span class="comment">## GitLab Puma</span></span><br><span class="line"><span class="comment">##! Tweak puma settings. You should only use Unicorn or Puma, not both.</span></span><br><span class="line"><span class="comment">##! Docs: https://docs.gitlab.com/omnibus/settings/puma.html</span></span><br><span class="line"><span class="comment">################################################################################</span></span><br><span class="line"></span><br><span class="line">puma[<span class="string">'enable'</span>] = <span class="literal">true</span></span><br><span class="line"><span class="comment"># puma['ha'] = false</span></span><br><span class="line">puma[<span class="string">'worker_timeout'</span>] = <span class="number">300</span></span><br><span class="line">puma[<span class="string">'worker_processes'</span>] = <span class="number">4</span></span><br><span class="line">puma[<span class="string">'min_threads'</span>] = <span class="number">1</span></span><br><span class="line">puma[<span class="string">'max_threads'</span>] = <span class="number">16</span></span><br></pre></td></tr></table></figure>
</div>
<footer class="post-footer">
<div class="post-eof"></div>
</footer>
</article>
<article itemscope itemtype="http://schema.org/Article" class="post-block" lang="en">
<link itemprop="mainEntityOfPage" href="https://shaffer.cn/golang/insights-into-empty-struct/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="image" content="/images/avatar.jpg">
<meta itemprop="name" content="Shaffer John">
<meta itemprop="description" content="一只生之无趣死之乏味的丧家之犬">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="朱晓峰">
</span>
<header class="post-header">
<h2 class="post-title" itemprop="name headline">
<a href="/golang/insights-into-empty-struct/" class="post-title-link" itemprop="url">空struct引发的一些探索</a>
</h2>
<div class="post-meta">
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar"></i>
</span>
<span class="post-meta-item-text">Posted on</span>
<time title="Created: 2020-11-22 16:22:19" itemprop="dateCreated datePublished" datetime="2020-11-22T16:22:19+08:00">2020-11-22</time>
</span>
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar-check"></i>
</span>
<span class="post-meta-item-text">Edited on</span>
<time title="Modified: 2024-07-07 01:19:51" itemprop="dateModified" datetime="2024-07-07T01:19:51+08:00">2024-07-07</time>
</span>
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-folder"></i>
</span>
<span class="post-meta-item-text">In</span>
<span itemprop="about" itemscope itemtype="http://schema.org/Thing">
<a href="/categories/golang/" itemprop="url" rel="index"><span itemprop="name">golang</span></a>
</span>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<h3 id="背景"><a href="#背景" class="headerlink" title="背景"></a>背景</h3><p>偶然发现项目代码中有如下的写法:</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">type</span> Student <span class="keyword">struct</span> {</span><br><span class="line"> Name <span class="type">string</span></span><br><span class="line"> Age <span class="type">int</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">NewStudent</span><span class="params">(s Student)</span></span> *Student {</span><br><span class="line"> <span class="keyword">return</span> &s</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>我们且不去探讨“这样写法有什么作用”、“为什么要这样写”,我们先就代码本身讨论。</p>
<p>众所周知,从严格意义上来说,golang函数调用中只有值传递的方式传递参数,所以这个函数会返回一个新的指针,并指向一个新的对象。</p>
<p>然后我就在思考,这种函数会被编译器特殊优化么?</p>
<h3 id="探索"><a href="#探索" class="headerlink" title="探索"></a>探索</h3><p>写一个简单demo测试一下:</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">"fmt"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> A <span class="keyword">struct</span>{} <span class="comment">// 空struct</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">NewStructA</span><span class="params">(a A)</span></span> *A {</span><br><span class="line"> <span class="keyword">return</span> &a</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> a := A{}</span><br><span class="line"> newA := NewStructA(a)</span><br><span class="line"> fmt.Printf(<span class="string">"a: %p, newA: %p\n"</span>, &a, newA)</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>运行结果很amazing啊</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ go run main.go</span><br><span class="line">a: 0x5a6d88, newA: 0x5a6d88</span><br></pre></td></tr></table></figure>
<p>两个指针居然完全相等,这不合常理啊,讲道理,值传递的话,return返回出来的应该是一个新对象的指针啊</p>
<p>不过仔细观察一下,这个例子中,A是一个空struct,函数里也不可能修改传进来的<code>A的实例a</code>,所以即使两个是相同的指针也没有任何影响。</p>
<p>那我们就去看看编译器是怎么做的吧。</p>
<p><code>-gcflags='-S'</code> 列出汇编代码</p>
<p><code>FUNCDATA</code>和<code>PCDATA</code>是编译器产生的,用于保存一些给垃圾收集的信息,在本例中可以过滤掉</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">$ go build -gcflags=<span class="string">'-S'</span> main.go 2>&1 | grep -v <span class="string">"FUNCDATA\|PCDATA"</span> | grep -A10 <span class="string">'"".NewStructA STEXT'</span></span><br><span class="line"></span><br><span class="line"><span class="string">""</span>.NewStructA STEXT nosplit size=13 args=0x8 locals=0x0</span><br><span class="line"> 0x0000 00000 (D:\projects\<span class="built_in">test</span>\go\struct\1\main.go:7) TEXT <span class="string">""</span>.NewStructA(SB), NOSPLIT|ABIInternal, <span class="variable">$0</span>-8</span><br><span class="line"> 0x0000 00000 (D:\projects\<span class="built_in">test</span>\go\struct\1\main.go:8) LEAQ runtime.zerobase(SB), AX</span><br><span class="line"> 0x0007 00007 (D:\projects\<span class="built_in">test</span>\go\struct\1\main.go:8) MOVQ AX, <span class="string">""</span>.~r1+8(SP)</span><br><span class="line"> 0x000c 00012 (D:\projects\<span class="built_in">test</span>\go\struct\1\main.go:8) RET</span><br><span class="line"> 0x0000 48 8d 05 00 00 00 00 48 89 44 24 08 c3 H......H.D$..</span><br><span class="line"> rel 3+4 t=16 runtime.zerobase+0</span><br><span class="line">...</span><br></pre></td></tr></table></figure>
<p><code>$0-8</code>中0代表的是分配的栈帧大小为0,8代表的是各个实参和返回值的总大小</p>
<p><img src="/images/caller.png" alt="caller"></p>
<p><code>LEAQ runtime.zerobase(SB), AX</code></p>
<p>把<code>runtime.zerobase(SB)</code>的地址传送到<code>AX</code>寄存器</p>
<p><code>MOVQ AX, "".~r1+8(SP)</code></p>
<p>把<code>AX</code>寄存器中的数据移动到<code>~r1+8(SP)</code>即返回值的位置</p>
<p><code>runtime.zerobase</code>是个什么东西?</p>
<p>翻看源码找到<code>runtime/malloc.go</code>826行</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// base address for all 0-byte allocations</span></span><br><span class="line"><span class="keyword">var</span> zerobase <span class="type">uintptr</span></span><br></pre></td></tr></table></figure>
<p>是一个用于所有对内存零占用的结构的地址</p>
<p>所以空struct的地址就是zerobase,所以函数<code>NewStructA</code>才会返回相同的地址。</p>
<p>而且实际上前文所说的<code>$0-8中的8代表的是各个实参和返回值的总大小</code>,这个<code>8</code>也可以理解了,实参其实并没有占用栈空间,是返回值(空struct的指针)占用了栈空间的8个字节。</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"fmt"</span></span><br><span class="line"> <span class="string">"unsafe"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> A <span class="keyword">struct</span>{}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> a := A{}</span><br><span class="line"> fmt.Println(unsafe.Sizeof(a)) <span class="comment">// print 0</span></span><br><span class="line"> fmt.Println(unsafe.Sizeof(&a)) <span class="comment">// print 8</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h3 id="空struct特性"><a href="#空struct特性" class="headerlink" title="空struct特性"></a>空struct特性</h3><ol>
<li><p>以前一直不理解<code>chan struct{}</code>的意图,其实可以理解为<code>struct{}</code>是golang中最小的数据结构</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">done := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="keyword">struct</span>{})</span><br><span class="line"><span class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="comment">// ...</span></span><br><span class="line"> done <- <span class="keyword">struct</span>{}{}</span><br><span class="line">}()</span><br><span class="line"><- done</span><br></pre></td></tr></table></figure>
</li>
<li><p><code>map[string]struct{}</code>比<code>map[string]bool</code>更节约空间</p>
</li>
<li><pre><code class="go">var x [1000000000]struct{}
fmt.Println(unsafe.Sizeof(x)) // print 0
</code></pre>
</li>
</ol>
<h3 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h3><ul>
<li><p><a href="https://golang.org/doc/asm">A Quick Guide to Go’s Assembler</a></p>
</li>
<li><p><a href="https://blog.csdn.net/u010853261/article/details/101060546">golang汇编语言基础</a></p>
</li>
<li><p><a href="https://blog.csdn.net/Tencent_TEG/article/details/108413692">Golang 汇编入门知识总结</a></p>
</li>
<li><p><a href="https://blog.csdn.net/qq_34777600/article/details/87195673">空结构体struct{}解析</a></p>
</li>
</ul>
<h3 id="附录"><a href="#附录" class="headerlink" title="附录"></a>附录</h3><table>
<thead>
<tr>
<th>助记符</th>
<th>指令种类</th>
<th>用途</th>
<th>示例</th>
</tr>
</thead>
<tbody><tr>
<td>MOVQ</td>
<td>传送</td>
<td>数据传送</td>
<td>MOVQ 48, AX // 把 48 传送到 AX</td>
</tr>
<tr>
<td>LEAQ</td>
<td>传送</td>
<td>地址传送</td>
<td>LEAQ AX, BX // 把 AX 有效地址传送到 BX</td>
</tr>
<tr>
<td>PUSHQ</td>
<td>传送</td>
<td>栈压入</td>
<td>PUSHQ AX // 将 AX 内容送入栈顶位置</td>
</tr>
<tr>
<td>POPQ</td>
<td>传送</td>
<td>栈弹出</td>
<td>POPQ AX // 弹出栈顶数据后修改栈顶指针</td>
</tr>
<tr>
<td>ADDQ</td>
<td>运算</td>
<td>相加并赋值</td>
<td>ADDQ BX, AX // 等价于 AX+=BX</td>
</tr>
<tr>
<td>SUBQ</td>
<td>运算</td>
<td>相减并赋值</td>
<td>SUBQ BX, AX // 等价于 AX-=BX</td>
</tr>
<tr>
<td>CMPQ</td>
<td>运算</td>
<td>比较大小</td>
<td>CMPQ SI CX // 比较 SI 和 CX 的大小</td>
</tr>
<tr>
<td>CALL</td>
<td>转移</td>
<td>调用函数</td>
<td>CALL runtime.printnl(SB) // 发起调用</td>
</tr>
<tr>
<td>JMP</td>
<td>转移</td>
<td>无条件转移指令</td>
<td>JMP 0x0185 //无条件转至 0x0185 地址处</td>
</tr>
<tr>
<td>JLS</td>
<td>转移</td>
<td>条件转移指令</td>
<td>JLS 0x0185 //左边小于右边,则跳到 0x0185</td>
</tr>
</tbody></table>
</div>
<footer class="post-footer">
<div class="post-eof"></div>
</footer>
</article>
<article itemscope itemtype="http://schema.org/Article" class="post-block" lang="en">
<link itemprop="mainEntityOfPage" href="https://shaffer.cn/uncategorized/mac-idea-terminal-alt-key-solution/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="image" content="/images/avatar.jpg">
<meta itemprop="name" content="Shaffer John">
<meta itemprop="description" content="一只生之无趣死之乏味的丧家之犬">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="朱晓峰">
</span>
<header class="post-header">
<h2 class="post-title" itemprop="name headline">
<a href="/uncategorized/mac-idea-terminal-alt-key-solution/" class="post-title-link" itemprop="url">解决mac下IntelliJ IDEA的terminal无法使用快速按单词移动光标</a>
</h2>
<div class="post-meta">
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar"></i>
</span>
<span class="post-meta-item-text">Posted on</span>
<time title="Created: 2020-06-23 10:21:42" itemprop="dateCreated datePublished" datetime="2020-06-23T10:21:42+08:00">2020-06-23</time>
</span>
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar-check"></i>
</span>
<span class="post-meta-item-text">Edited on</span>
<time title="Modified: 2024-07-07 01:19:51" itemprop="dateModified" datetime="2024-07-07T01:19:51+08:00">2024-07-07</time>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<p>在mac下idea的terminal没办法使用option+左右方向键跳过单词,google后找到以下解决方案:</p>
<h3 id="非zsh用户"><a href="#非zsh用户" class="headerlink" title="非zsh用户"></a>非zsh用户</h3><p>Add this to ~/.zshrc:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">"\e\eC": forward-word</span><br><span class="line">"\e\eD": backward-word</span><br></pre></td></tr></table></figure>
<h3 id="zsh用户"><a href="#zsh用户" class="headerlink" title="zsh用户"></a>zsh用户</h3><p>Add this to ~/.zshrc:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">bindkey "\e\eOD" backward-word</span><br><span class="line">bindkey "\e\eOC" forward-word</span><br></pre></td></tr></table></figure>
</div>