偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

Golang | 是返回Struct還是返回Struct的指針

網(wǎng)絡(luò) 通信技術(shù)
對(duì)于這個(gè)問(wèn)題,我想大部分人的回答,肯定都是返回指針,因?yàn)檫@樣可以避免結(jié)構(gòu)體的拷貝,使代碼的效率更高,性能更好。

[[414652]]

當(dāng)我們定義一個(gè)函數(shù)時(shí),是返回結(jié)構(gòu)體呢,還是返回指向結(jié)構(gòu)體的指針呢?

對(duì)于這個(gè)問(wèn)題,我想大部分人的回答,肯定都是返回指針,因?yàn)檫@樣可以避免結(jié)構(gòu)體的拷貝,使代碼的效率更高,性能更好。

但真的是這樣嗎?

在回答這個(gè)問(wèn)題之前,我們先寫(xiě)幾個(gè)示例,來(lái)確定一些基本事實(shí):

上圖中,函數(shù)f返回的是結(jié)構(gòu)體S的指針,即一個(gè)地址,這個(gè)可以通過(guò)其匯編來(lái)確認(rèn):

看上圖中的選中行。

第一行是調(diào)用函數(shù)f,其結(jié)果,即結(jié)構(gòu)體S的指針,或結(jié)構(gòu)體S的地址,是放到ax寄存器中返回的。

第二行用0x8(ax),即ax中的地址加8的形式,來(lái)獲得結(jié)構(gòu)體S中a2字段的值,然后將該值和0x2相比,以進(jìn)行后續(xù)邏輯。

由此可見(jiàn),返回結(jié)構(gòu)體指針的形式,確實(shí)是只傳遞了一個(gè)地址。

我們?cè)賮?lái)看下返回結(jié)構(gòu)體的情況:

這次函數(shù)f返回的是S,而不是*S,看看這樣寫(xiě)其匯編是什么樣子:

上圖main函數(shù)的匯編中,通過(guò)調(diào)用函數(shù)f,初始化了main函數(shù)棧中,0x0(sp)到0x50(sp)的內(nèi)存段,該內(nèi)存段共有80個(gè)字節(jié),正好對(duì)應(yīng)于結(jié)構(gòu)體S的大小。

在函數(shù)f返回后,sp寄存器存放的,正是函數(shù)f初始化的結(jié)構(gòu)體S的地址。

接著,我們看上圖中的選中行,該段邏輯通過(guò)runtime.duffcopy函數(shù),將棧中內(nèi)存段0x0(sp)到0x50(sp)的值,拷貝到了內(nèi)存段0x50(sp)到0xa0(sp)的部分,即將函數(shù)f初始化的結(jié)構(gòu)體S,從內(nèi)存地址0x0(sp),拷貝到了0x50(sp)。

然后,通過(guò)0x58(sp),即sp中的地址加上0x58的形式,獲得拷貝后的結(jié)構(gòu)體S中,a2字段的值,最后將其和0x2比較,以進(jìn)行后續(xù)邏輯。

由上可見(jiàn),當(dāng)函數(shù)返回結(jié)構(gòu)體時(shí),確實(shí)存在著一次結(jié)構(gòu)體的拷貝操作。

對(duì)比以上兩個(gè)示例我們看到,返回指針的確會(huì)更好些,因?yàn)檫@樣節(jié)省了一次結(jié)構(gòu)體的拷貝操作。

但這樣性能就真的更好嗎?

寫(xiě)個(gè)benchmark測(cè)試下:

執(zhí)行看下結(jié)果:

這兩個(gè)benchmark的時(shí)間幾乎是相等的,其結(jié)果并不像我們預(yù)料的那樣,返回指針的形式會(huì)更快些。

為什么呢?

看下這兩個(gè)benchmark對(duì)應(yīng)的匯編:

它們居然都被優(yōu)化成了空跑for循環(huán)了,難怪這兩個(gè)測(cè)試耗時(shí)是一樣的。

加上編譯器指令//go:noinline,防止f1/f2函數(shù)被內(nèi)聯(lián),進(jìn)而被過(guò)度優(yōu)化:

如上圖的第9行和第14行。

再來(lái)看下測(cè)試程序的匯編,確保以上操作是有效的。

先看下函數(shù)f1及其對(duì)應(yīng)的benchmark:

再看下函數(shù)f2及其對(duì)應(yīng)的benchmark:

這次這兩個(gè)都沒(méi)有問(wèn)題。

再來(lái)跑下benchmark:

這次結(jié)果顯示,f2函數(shù),即返回結(jié)構(gòu)體形式,比f(wàn)1函數(shù),即返回指針的形式,居然快了將近5倍,意不意外?

這是為什么呢?

其實(shí)在上圖中,就有一些線索。

看BenchmarkF1那行,其最后兩列顯示,每次調(diào)用f1函數(shù),都會(huì)有一次堆內(nèi)存分配操作,其分配內(nèi)存的大小為80字節(jié),正好對(duì)應(yīng)于結(jié)構(gòu)體S的大小,也就是說(shuō),f1函數(shù)中結(jié)構(gòu)體S的內(nèi)存,都是在堆上分配的。

而在BenchmarkF2中,就沒(méi)有發(fā)生堆內(nèi)存的分配操作,f2函數(shù)中的結(jié)構(gòu)體S,都是在棧上分配的。

這個(gè)也可以通過(guò)上面展示的,f1/f2函數(shù)的匯編代碼看到。

f1函數(shù)的匯編是通過(guò)runtime.newobject在堆上分配內(nèi)存的,而f2函數(shù)則是直接就在棧上把內(nèi)存分配好了,并沒(méi)有調(diào)用runtime.newobject函數(shù)。

那為什么在堆上分配內(nèi)存,會(huì)比在棧上分配內(nèi)存慢這么多呢?

有兩點(diǎn)原因,一是在堆上分配內(nèi)存的函數(shù)runtime.newobject,其本身邏輯就比較復(fù)雜,二是堆上分配的內(nèi)存,后期還要通過(guò)gc來(lái)對(duì)其進(jìn)行內(nèi)存回收,這些邏輯加起來(lái),遠(yuǎn)比在棧上分配內(nèi)存,外加一次拷貝操作要耗時(shí)的多。

有關(guān)go內(nèi)存是在堆上分配的,還是在棧上分配的,這個(gè)是在編譯過(guò)程中,通過(guò)逃逸分析來(lái)確定的,其主體思想是:

假設(shè)有變量v,及指向v的指針p,如果p的生命周期大于v的生命周期,則v的內(nèi)存要在堆上分配。

其實(shí)逃逸分析的具體邏輯,遠(yuǎn)比上面說(shuō)的復(fù)雜,如果有興趣研究代碼,可以從下面開(kāi)始入手:

當(dāng)然,我們也可以在編譯時(shí),通過(guò)加上-m參數(shù),來(lái)讓編譯器告訴我們,一個(gè)變量到底是分配在堆上,還是在棧上:

看上圖,f1函數(shù)中的&S{...}逃逸到了堆上,即是在堆上分配的。

以上是對(duì)80字節(jié)大小的結(jié)構(gòu)體,返回指針和返回值情況的比較,那如果結(jié)構(gòu)體字節(jié)數(shù)更小或更大會(huì)怎么樣呢?

經(jīng)過(guò)測(cè)試,1MiB字節(jié)以下,返回結(jié)構(gòu)體都更有優(yōu)勢(shì)。

那返回指針的方式是不是沒(méi)用了呢?也不是,如果你最終的結(jié)構(gòu)體,就是要存放到堆里,比如要存放到全局的map里,那返回指針優(yōu)勢(shì)就更大些,因?yàn)槠涫∪チ朔祷亟Y(jié)構(gòu)體時(shí)的拷貝操作。 

就這些,希望對(duì)你有所幫助。

本文轉(zhuǎn)載自微信公眾號(hào)「卯時(shí)卯刻」,可以通過(guò)以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系卯時(shí)卯刻公眾號(hào)。

 

責(zé)任編輯:武曉燕 來(lái)源: 卯時(shí)卯刻
相關(guān)推薦

2021-07-12 05:05:59

Golang語(yǔ)言字段

2023-06-09 08:16:09

GolangStruct Tag

2022-01-10 13:01:32

指針Struct內(nèi)存

2020-11-23 14:22:17

代碼Go存儲(chǔ)

2023-11-22 13:22:51

C++函數(shù)

2022-08-19 14:38:52

C語(yǔ)言結(jié)構(gòu)體struct

2009-10-10 14:40:03

C++中struct

2023-07-11 08:22:09

2023-03-06 08:01:25

structGo語(yǔ)言

2012-06-18 10:03:46

Visual Stud

2010-01-08 16:06:33

C++中struct

2021-12-19 23:58:51

Golang語(yǔ)言返回值

2021-04-15 08:55:51

Go struc代碼

2025-03-31 01:00:10

2023-07-04 08:56:07

指針類型Golang

2021-08-02 07:02:18

數(shù)據(jù)結(jié)構(gòu)體Struct

2022-10-20 18:43:32

C語(yǔ)言golang安全

2022-01-04 23:13:57

語(yǔ)言PanicGolang

2024-03-08 08:23:38

ping命令網(wǎng)絡(luò)

2023-03-14 07:39:13

返回值編寫(xiě)命令行工具
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)