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

Rust有GC,并且速度很快

開(kāi)發(fā) 后端
Rust越來(lái)越受歡迎。因此,不管Rust是否對(duì)我們都具有戰(zhàn)略意義,包括我自己在內(nèi)的一組同事對(duì)其進(jìn)行了為期半天的評(píng)估,以建立我們自己的觀點(diǎn)。我們按照標(biāo)準(zhǔn)入門(mén)書(shū)進(jìn)行了一些編碼,查看了一些框架,并觀看了“ Considering Rust”演示文稿。。

Rust越來(lái)越受歡迎。因此,不管Rust是否對(duì)我們都具有戰(zhàn)略意義,包括我自己在內(nèi)的一組同事對(duì)其進(jìn)行了為期半天的評(píng)估,以建立我們自己的觀點(diǎn)。我們按照標(biāo)準(zhǔn)入門(mén)書(shū)進(jìn)行了一些編碼,查看了一些框架,并觀看了“ Considering Rust”演示文稿。??偟慕Y(jié)論大致是這樣的:是的,一種不錯(cuò)的新編程語(yǔ)言,但是沒(méi)有一個(gè)成熟的生態(tài)系統(tǒng),也沒(méi)有任何垃圾收集,對(duì)于我們的項(xiàng)目而言,這將是太麻煩和無(wú)用的。我的直覺(jué)與關(guān)于垃圾收集的評(píng)估不一致。因此,我做了一些進(jìn)一步的挖掘和測(cè)試,并得出了當(dāng)前的結(jié)論:Rust確實(shí)進(jìn)行了垃圾收集,但是使用的是非常聰明的方式。

 

[[351165]]

垃圾收集簡(jiǎn)史

當(dāng)您查看Rust的網(wǎng)站并閱讀介紹時(shí),您會(huì)突然發(fā)現(xiàn)一個(gè)驕傲的聲明,Rust沒(méi)有垃圾收集器。如果您與我同齡,這會(huì)引起一些不好的回憶。有時(shí)候,您必須使用手動(dòng)分配內(nèi)存,malloc()然后稍后再釋放它。如果過(guò)早釋放它,則會(huì)遇到諸如無(wú)效的內(nèi)存訪問(wèn)異常之類的攻擊。如果忘記釋放它,則會(huì)造成內(nèi)存泄漏,從而使應(yīng)用程序受阻。很少有人在第一次就做對(duì)。這根本沒(méi)什么好玩的。

在研究Rust采取的方法之前,讓我們簡(jiǎn)短地看看垃圾的實(shí)際含義。在Wikipedia中,有一個(gè)很好的定義:垃圾包括數(shù)據(jù)………在其上運(yùn)行的程序在以后的任何計(jì)算中都不會(huì)使用。這意味著只有開(kāi)發(fā)人員才能決定是否可以釋放存儲(chǔ)某些數(shù)據(jù)的內(nèi)存段。但是,應(yīng)用程序的運(yùn)行時(shí)可以自動(dòng)檢測(cè)垃圾的子集。如果在某個(gè)時(shí)間點(diǎn)不再存在對(duì)內(nèi)存段的引用,則程序?qū)o(wú)法訪問(wèn)該段。并且,因此可以安全地刪除它。

為了實(shí)際實(shí)現(xiàn)這種支持,運(yùn)行時(shí)必須分析應(yīng)用程序中的所有活動(dòng)引用,并且必須檢查所有已分配的內(nèi)存引用(如果可以針對(duì)當(dāng)前應(yīng)用程序狀態(tài)訪問(wèn)它們)。這是一項(xiàng)計(jì)算量很大的任務(wù)。在Java誕生的第一天,JVM突然凍結(jié),不得不在相當(dāng)長(zhǎng)的時(shí)間內(nèi)進(jìn)行垃圾回收。如今,有很多用于垃圾收集的復(fù)雜算法,它們通常與應(yīng)用程序同時(shí)運(yùn)行。但是,計(jì)算復(fù)雜度仍然相同。

從好的方面來(lái)說(shuō),應(yīng)用程序開(kāi)發(fā)人員無(wú)需考慮手動(dòng)釋放內(nèi)存段。永遠(yuǎn)不會(huì)有無(wú)效的內(nèi)存訪問(wèn)異常。她仍然可以通過(guò)引用數(shù)據(jù)來(lái)創(chuàng)建內(nèi)存泄漏,現(xiàn)在不再需要。(恕我直言,主要的示例是自寫(xiě)的緩存實(shí)現(xiàn)。老人的建議:切勿使用ehcache之類的方法。)但是,隨著垃圾收集器的引入,內(nèi)存泄漏的情況越來(lái)越少了。

Rust如何處理內(nèi)存段

乍一看,Rust看起來(lái)很像C,尤其是其引用和取消引用。但是它具有處理內(nèi)存的獨(dú)特方法。每個(gè)內(nèi)存段均由一個(gè)引用擁有。從開(kāi)發(fā)人員的角度來(lái)看,始終只有一個(gè)變量擁有數(shù)據(jù)。如果此變量超出范圍且不再可訪問(wèn),則將所有權(quán)轉(zhuǎn)移到其他變量或釋放內(nèi)存。

使用這種方法,不再需要計(jì)算所有數(shù)據(jù)的可達(dá)性。取而代之的是,每次關(guān)閉命名上下文時(shí)(例如,通過(guò)從函數(shù)調(diào)用返回),都會(huì)使用簡(jiǎn)單的算法來(lái)驗(yàn)證所用內(nèi)存的可訪問(wèn)性。聽(tīng)起來(lái)好極了,以至于每個(gè)有經(jīng)驗(yàn)的開(kāi)發(fā)人員都可能立即想到一個(gè)問(wèn)題:?jiǎn)栴}在哪里?

問(wèn)題在于,開(kāi)發(fā)人員必須照顧所有權(quán)。開(kāi)發(fā)人員不必在整個(gè)應(yīng)用程序中漫不經(jīng)心地散布對(duì)數(shù)據(jù)的引用,而必須標(biāo)記所有權(quán)。如果所有權(quán)沒(méi)有明確定義,則編譯器將打印錯(cuò)誤并停止工作。

為了進(jìn)行評(píng)估,與傳統(tǒng)的垃圾收集器相比,該方法是否真的有用,我看到兩個(gè)問(wèn)題:

  • 開(kāi)發(fā)人員在開(kāi)發(fā)時(shí)標(biāo)記所有權(quán)有多難?如果她所有的精力都集中在與編譯器進(jìn)行斗爭(zhēng)而不是解決域問(wèn)題上,那么這種方法所帶來(lái)的好處遠(yuǎn)不止于幫助。
  • 與傳統(tǒng)的垃圾收集器相比,Rust解決方案的速度快多少?如果收益不大,那為什么還要打擾呢?

為了回答這兩個(gè)問(wèn)題,我在Rust和Kotlin中執(zhí)行了一項(xiàng)任務(wù)。該任務(wù)對(duì)于企業(yè)環(huán)境而言是典型的,會(huì)產(chǎn)生大量垃圾。第一個(gè)問(wèn)題是根據(jù)我的個(gè)人經(jīng)驗(yàn)和觀點(diǎn)回答的,第二個(gè)是通過(guò)具體測(cè)量得出的。

任務(wù):處理數(shù)據(jù)庫(kù)

我選擇的任務(wù)是模擬典型的以數(shù)據(jù)庫(kù)為中心的任務(wù),計(jì)算所有員工的平均收入。每個(gè)員工都被加載到內(nèi)存中,并且平均值會(huì)循環(huán)計(jì)算。我知道您絕對(duì)不應(yīng)在現(xiàn)實(shí)生活中這樣做,因?yàn)閿?shù)據(jù)庫(kù)可以自己更快地完成此任務(wù)。但是,首先,我看到這種情況在現(xiàn)實(shí)生活中經(jīng)常發(fā)生,其次,對(duì)于某些NoSQL數(shù)據(jù)庫(kù),您必須在應(yīng)用程序中執(zhí)行此操作,其次,這只是一些代碼,用于創(chuàng)建大量需要收集的垃圾。

我選擇了JVM上的Kotlin作為基于垃圾收集的編程語(yǔ)言的代表。JVM具有高度優(yōu)化的垃圾收集器,如果您習(xí)慣Kotlin,則使用Java就像在石器時(shí)代工作一樣。

您可以在GitHub上找到代碼:https://github.com/akquinet/GcRustVsJvm

用Kotlin處理

計(jì)算得出一系列員工,總結(jié)他們的薪水,計(jì)算員工數(shù)量,最后除以這些數(shù)字:

 

  1. fun computeAverageIncomeOfAllEmployees( 
  2.       employees : Sequence<Employee> 
  3.     ) : Double  
  4.    val (nrOfEmployees, sumOfSalaries) = employees 
  5.        .fold(Pair(0L, 0L), 
  6.          { (counter, sum), employee -> 
  7.               Pair(counter + 1, sum + employee.salary) 
  8.          }) 
  9.    return sumOfSalaries.toDouble() /  
  10.           nrOfEmployees.toDouble() 
  11.  } 

這里沒(méi)什么令人興奮的。(您可能會(huì)注意到一種函數(shù)式編程風(fēng)格。這是因?yàn)槲曳浅O矚g函數(shù)式編程。但這不是本文的主題。)垃圾是在創(chuàng)建雇員時(shí)創(chuàng)建的。我在這里創(chuàng)建隨機(jī)雇員,以避免使用真實(shí)的數(shù)據(jù)庫(kù)。但是,如果您使用JPA,則將具有相同數(shù)量的對(duì)象創(chuàng)建。

 

  1. fun lookupAllEmployees( 
  2.         numberOfAllEmployees : Long 
  3.      ): Sequence<Employee> 
  4.    return (1L..numberOfAllEmployees) 
  5.             .asSequence() 
  6.             .map { createRandomEmployee() } 

隨機(jī)對(duì)象的創(chuàng)建也非常簡(jiǎn)單。字符串是從字符列表創(chuàng)建的charPool。

 

  1. fun createRandomEmployee(): Employee = 
  2.    Employee( 
  3.      createRandomStringOf80Chars(), 
  4.      createRandomStringOf80Chars(), 
  5.      ... // code cut Out 
  6.    ) 
  7. fun createRandomStringOf80Chars() = 
  8.    (1..80) 
  9.       .map { nextInt(0, charPool.size) } 
  10.       .map(charPool::get) 
  11.       .joinToString(""

Rust版本的一個(gè)小驚喜是我必須如何處理前面提到的字符列表。因?yàn)橹恍枰粋€(gè)單例,所以將其存儲(chǔ)在一個(gè)伴隨對(duì)象中。這里是它的輪廓:

 

  1. class EmployeeServices { 
  2.   
  3.     companion object { 
  4.         private val charPool: List<Char>  
  5.            = ('a'..'z') + ('A'..'Z') + ('0'..'9'
  6.   
  7.         fun lookupAllEmployees(...) ... 
  8.         fun createRandomEmployee(): Employee ... 
  9.         fun computeAverageIncomeOfAllEmployees(...) ... 
  10.     } 

現(xiàn)在,以Rust方式處理

我偶然發(fā)現(xiàn)的第一件事是,將這個(gè)單例字符列表放在何處。Rust支持直接嵌入二進(jìn)制文件中的靜態(tài)數(shù)據(jù)和可以由編譯器內(nèi)聯(lián)的常量數(shù)據(jù)。兩種選擇僅支持一小部分表達(dá)式來(lái)計(jì)算單例的值。我計(jì)算允許的字符池的解決方案是這樣的:

  1. let char_pool = ('a'..'z').collect::>(); 

由于向量的計(jì)算基于類型推斷,因此無(wú)法將其指定為常量或靜態(tài)。我目前的理解是,Rust的慣用方法是添加功能需要處理的所有對(duì)象作為參數(shù)。因此,用于計(jì)算Rust中平均工資的主要調(diào)用如下所示:

 

  1. let average =  
  2.   compute_average_income_of_all_employees( 
  3.     lookup_all_employees( 
  4.             nr_of_employees, &char_pool, 
  5.   ) ); 

通過(guò)這種方法,所有依賴項(xiàng)都變得清晰了。具有C經(jīng)驗(yàn)的開(kāi)發(fā)人員會(huì)立即認(rèn)識(shí)到地址運(yùn)算符&,該運(yùn)算符將內(nèi)存地址作為指針?lè)祷?,并且是高效且可能無(wú)法維護(hù)的代碼的基礎(chǔ)。當(dāng)我的許多同事與Rust一起玩時(shí),這種基于C的負(fù)面體驗(yàn)被投射到Rust。

我認(rèn)為這是不公平的。C的&運(yùn)算符設(shè)計(jì)帶來(lái)的問(wèn)題是,始終存在不可預(yù)測(cè)的副作用,因?yàn)閼?yīng)用程序的每個(gè)部分都可以存儲(chǔ)指向存儲(chǔ)塊的指針。另外,每個(gè)部分都可以釋放內(nèi)存,從而可能導(dǎo)致所有其他部分引發(fā)異常。

在Rust中,&操作員的工作方式有所不同。每個(gè)數(shù)據(jù)始終由一個(gè)變量擁有。如果使用&此所有權(quán)創(chuàng)建了對(duì)數(shù)據(jù)的引用,則該所有權(quán)將轉(zhuǎn)移到引用范圍內(nèi)。只有所有者可以訪問(wèn)數(shù)據(jù)。如果所有者超出范圍,則可以釋放數(shù)據(jù)。

在我們的示例中,char_pool使用&運(yùn)算符將的所有權(quán)轉(zhuǎn)移到函數(shù)的參數(shù)。當(dāng)該函數(shù)返回時(shí),所有權(quán)將歸還給變量char_pool。因此,它是一種類似于C的地址運(yùn)算符,但它增加了所有權(quán)的概念,從而使代碼更簡(jiǎn)潔。

Rust中的域邏輯

Rust的主要功能看起來(lái)與Kotlin差不多。由于隱含的數(shù)字類型,例如f6464位浮點(diǎn)數(shù),因此感覺(jué)有點(diǎn)基本。但是,這是您很快就會(huì)習(xí)慣的事情。

 

  1. fn compute_average_income_of_all_employees( 
  2.      employees: impl Iterator<Item=Employee> 
  3.    )  -> f64 
  4.     let (num_of_employees, sum_of_salaries) = 
  5.         employees.fold( 
  6.           (0u64, 0u64), 
  7.           |(counter, sum), employee| { 
  8.               return (counter + 1,  
  9.                       sum + employee.salary); 
  10.           }); 
  11.     return (sum_of_salaries as f64) /  
  12.            (num_of_employees as f64); 

恕我直言,這是一個(gè)很好的例子,可以證明Rust是一種非?,F(xiàn)代的干凈編程語(yǔ)言,并且對(duì)函數(shù)式編程風(fēng)格提供了很好的支持。

在Rust中創(chuàng)建垃圾

現(xiàn)在讓我們看一下程序的一部分,其中創(chuàng)建了許多對(duì)象,以后需要收集這些對(duì)象:

 

  1. fn lookup_all_employees<'a>( 
  2.      number_of_all_employees: u64, 
  3.      char_pool: &'a Vec<char
  4.    ) -> impl Iterator<Item=Employee> + 'a  
  5.     return 
  6.      (0..number_of_all_employees) 
  7.        .map(move | _ | { 
  8.          return create_random_employee(char_pool);  
  9.         }) 
  10.        .into_iter(); 

乍一看,這看起來(lái)很像Kotlin。它使用相同的功能樣式在循環(huán)中創(chuàng)建隨機(jī)雇員。返回類型是Iterator,類似于Kotlin中的序列,它是一個(gè)延遲計(jì)算的列表。

從第二個(gè)角度看,這些類型看起來(lái)很奇怪。這到底是'a什么?解決了懶惰評(píng)估的問(wèn)題。由于Rust編譯器無(wú)法知道何時(shí)實(shí)際評(píng)估返回值,并且返回值取決于借入的引用,因此現(xiàn)在存在確定何時(shí)char_pool可以釋放借入值的問(wèn)題。的'a注釋指定的壽命c(diǎn)har_pool必須至少只要是作為返回值的壽命。

對(duì)于習(xí)慣了經(jīng)典垃圾回收的開(kāi)發(fā)人員來(lái)說(shuō),這是一個(gè)新概念。在Rust中,她有時(shí)必須明確指定對(duì)象的生存期。垃圾收集器進(jìn)行所有清理時(shí)不需要的東西。

第三,您可以發(fā)現(xiàn)move關(guān)鍵字。它強(qiáng)制閉包獲取其使用的所有變量的所有權(quán)。由于char_pool(再次),這是必要的。Map是延遲執(zhí)行的,因此,從編譯器的角度來(lái)看,閉包可能會(huì)超出變量的壽命c(diǎn)har_pool。因此,關(guān)閉必須擁有它的所有權(quán)。

其余代碼非常簡(jiǎn)單。這些結(jié)構(gòu)是根據(jù)隨機(jī)創(chuàng)建的字符串創(chuàng)建的:

 

  1. fn create_random_employee( 
  2.      char_pool: &Vec<char
  3.    ) -> Employee  
  4.   return Employee { 
  5.     first_name:  
  6.       create_random_string_of_80_chars(char_pool), 
  7.     last_name:  
  8.       create_random_string_of_80_chars(char_pool), 
  9.     address: Address 
  10.     { // cut out .. }, 
  11.         salary: 1000, 
  12.     }; 
  13.   
  14. fn create_random_string_of_80_chars( 
  15.      char_pool: &Vec<char
  16.    ) -> String  
  17.     return (0..80) 
  18.         .map(|_| {  
  19.           char_pool[ 
  20.             rand::thread_rng() 
  21.                .gen_range(0, 
  22.                           char_pool.len())] 
  23.          }) 
  24.         .into_iter().collect(); 

那么,Rust有多難?

實(shí)施這個(gè)微小的測(cè)試程序非常復(fù)雜。Rust是一種現(xiàn)代的編程語(yǔ)言,使開(kāi)發(fā)人員能夠快速干凈地維護(hù)代碼。但是,它的內(nèi)存管理概念直接體現(xiàn)在語(yǔ)言的所有元素中,這是開(kāi)發(fā)人員必須理解的。

工具支持很好,恕我直言。大多數(shù)時(shí)候,您只需要執(zhí)行編譯器告訴您的操作即可。但是有時(shí)您必須實(shí)際決定要如何處理數(shù)據(jù)。

現(xiàn)在,值得嗎?

即使聽(tīng)起來(lái)有些令人信服,但我還是非常樂(lè)于做一些測(cè)量,看看現(xiàn)實(shí)是否也令人信服。因此,我為四個(gè)不同的輸入大小運(yùn)行了Rust和Kotlin應(yīng)用程序,測(cè)量了時(shí)間,并將結(jié)果放在對(duì)數(shù)比例圖中:

 

Rust有GC,并且速度很快

看著這些數(shù)字,我長(zhǎng)了很長(zhǎng)的臉。銹總是較慢;對(duì)于10 ^ 6個(gè)元素,一個(gè)非常糟糕的因子是11。這不可能。我檢查了代碼,沒(méi)有發(fā)現(xiàn)錯(cuò)誤。然后,我檢查了優(yōu)化情況,并發(fā)現(xiàn)了--release從dev模式切換到的標(biāo)志prod?,F(xiàn)在,結(jié)果看起來(lái)好多了:

 

Rust有GC,并且速度很快

這樣好多了?,F(xiàn)在,Rust總是比Kotlin快,并提供線性性能。在Kotlin上,我們看到了較長(zhǎng)時(shí)間運(yùn)行的代碼的典型性能改進(jìn),這可能是由于及時(shí)編譯引起的。從10 ^ 4的輸入大小來(lái)看,Rust大約比Kotlin快3倍??紤]到JVM的成熟以及過(guò)去幾十年在基礎(chǔ)架構(gòu)上投入的資源,這是非常令人印象深刻的(Java的第一版于1995年發(fā)布)。

對(duì)于我來(lái)說(shuō),令人驚訝的是與生產(chǎn)配置文件相比,開(kāi)發(fā)配置文件的速度要慢得多。40的系數(shù)是如此之大,以至于您永遠(yuǎn)都不應(yīng)將開(kāi)發(fā)配置文件用于發(fā)行。

結(jié)論

Rust是一種現(xiàn)代編程語(yǔ)言,具有您如今已習(xí)慣的所有舒適性。它具有一種新的內(nèi)存處理方法,這給開(kāi)發(fā)人員帶來(lái)了一點(diǎn)額外負(fù)擔(dān),同時(shí)還提供了出色的性能。

而且,要回答標(biāo)題的最初問(wèn)題,您不必手動(dòng)處理Rust中的垃圾。此垃圾收集由運(yùn)行時(shí)系統(tǒng)完成,但現(xiàn)在不再稱為垃圾收集器。

責(zé)任編輯:未麗燕 來(lái)源: 今日頭條
相關(guān)推薦

2024-01-15 11:56:55

lintersESLint

2024-03-11 15:47:11

RustPython代碼

2016-03-02 17:55:03

app用戶加載

2024-11-04 14:13:19

2021-08-07 09:35:18

Starlink網(wǎng)速寬帶

2019-01-02 09:10:13

RustGraydon Hoa社區(qū)

2023-05-04 07:34:37

Rust代碼CPU

2024-09-09 16:25:09

2023-12-07 12:21:04

GCJVM垃圾

2023-02-20 08:00:02

Rust語(yǔ)言系統(tǒng)

2024-01-09 16:14:39

RustGo切片

2017-09-12 13:04:00

AI人工智能機(jī)器人

2021-06-18 15:15:51

機(jī)器學(xué)習(xí)Rust框架

2024-01-09 15:51:56

Rust開(kāi)發(fā)Trait

2024-04-03 10:00:44

Rust編譯開(kāi)發(fā)

2024-04-01 11:43:51

Rust開(kāi)發(fā)插件

2020-07-15 08:00:52

Rust語(yǔ)言技巧

2009-09-28 10:37:35

2021-11-25 16:25:53

代碼開(kāi)發(fā)技術(shù)

2024-06-20 12:48:17

Rustfd
點(diǎn)贊
收藏

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