Verbeteringen in profilering in Go 1.18 · Felix Geisendörfer

Verbeteringen in profilering in Go 1.18 · Felix Geisendörfer


Verbeteringen in profilering in Go 1.18 · Felix Geisendörfer

Gepubliceerd:

Zonder twijfel zal Go 1.18 een van de meest opwindende releases worden sinds Go 1. Je hebt waarschijnlijk gehoord van belangrijke functies zoals generieke geneesmiddelen en fuzzing, maar daar gaat dit bericht niet over. In plaats daarvan zullen we het hebben over profilering en enkele opmerkelijke verbeteringen benadrukken waar we naar uit kunnen kijken.

Een punt van persoonlijke vreugde voor mij is dat ik heb kunnen bijdragen aan verschillende verbeteringen als onderdeel van mijn werk bij Datadog en dat deze onze nieuwe Connecting Go Profiling With Tracing-functionaliteit aanzienlijk zullen verbeteren.

Als u nog niet bekend bent met Go-profilering, kunt u ook onze Gids voor Go-profilering lezen voordat u in dit bericht duikt.

Betere CPU-profileringsnauwkeurigheid

Laten we beginnen met de belangrijkste profileringswijziging in Go 1.18: de CPU-profiler voor Linux is een stuk nauwkeuriger geworden. Vooral CPU-bursts op multi-coresystemen werden vroeger onderschat, maar dat is nu opgelost.

Deze wijziging gaat terug tot eind 2019 (go1.13) toen Rhys Hiltner GH 35057 opende om te melden dat hij een Go-service zag die gebruik maakte van 20 CPU-kernen volgens topmaar alleen 2.4 kernen volgens de CPU-profiler van Go. Toen hij dieper graafde, ontdekte Rhys dat dit probleem werd veroorzaakt door het wegvallen van POSIX-signalen in de kernel.

Om het probleem te begrijpen, is het de moeite waard om te weten dat de CPU-profiler van Go 1.17 bovenop de setitimer(2) syscall is gebouwd. Hierdoor kan de Go-runtime de Linux-kernel vragen om voor elke melding een melding te ontvangen 10ms het programma draait op een CPU. Deze meldingen worden afgeleverd als SIGPROF signalen. Elke levering van een signaal zorgt ervoor dat de kernel het programma stopt en de signaalhandlerroutine van de runtime aanroept op een van de gestopte threads. De signaalhandler neemt een stacktracering van de huidige goroutine en voegt deze toe aan het CPU-profiel. Dit alles kost minder dan ~10usec waarna de kernel de uitvoering van het programma hervat.

Dus wat was er mis? Nou, gezien de programmarapportage van Rhys 20 CPU-kernen die worden gebruikt door topzou de verwachte signaalsnelheid moeten zijn 2,000 per seconde. Het resulterende profiel bevatte echter slechts een gemiddelde van 240 stapelt sporen per seconde. Uit verder onderzoek met behulp van Linux Event Tracing bleek dat de verwachte hoeveelheid signal:signal_generate Er werden gebeurtenissen waargenomen, maar niet genoeg signal:signal_deliver evenementen. Het verschil tussen het genereren en afleveren van signalen lijkt in eerste instantie misschien verwarrend, maar is te wijten aan het feit dat standaard POSIX-signalen niet in de wachtrij staan, zie signal(7). Er kan slechts één signaal tegelijk in afwachting zijn van bezorging. Als een ander signaal van dezelfde soort wordt gegenereerd terwijl er al een signaal in behandeling is, wordt het nieuwe signaal verwijderd.

Dit verklaart echter nog steeds niet waarom er tijdens precies hetzelfde zoveel signalen zouden worden gegenereerd < ~10usec signaalafhandelingsvenster zodat ze boven op elkaar terecht zouden komen. Het volgende stukje van deze puzzel is dus het feit dat de resolutie van systeemaanroepen die de CPU-tijd meten, beperkt zijn tot de softwareklok van de kernel, die de tijd meet in jiffieszie tijd(7). De duur van een handomdraai hangt af van de kernelconfiguratie, maar is standaard 4ms (250Hz). Wat dit betekent is dat de kernel niet eens in staat is om de wens van Go te honoreren om elke keer een signaal te ontvangen 10ms. In plaats daarvan moet er worden afgewisseld 8ms En 12ms zoals te zien is in het onderstaande histogram (details).

Sinds afwisselend 8ms En 12ms lukt nog steeds 10ms gemiddeld genomen is het op zichzelf geen probleem. Wat echter een probleem is, is wat er gebeurt als bijv 10 threads draaien op verschillende CPU-kernen, waardoor ze worden gebruikt 40ms van CPU-tijd binnen een 4ms even raam. Dus wanneer de kernel zijn controles uitvoert, moet hij genereren 4 SIGPROF signalen tegelijk, waardoor ze op één na allemaal worden weggelaten in plaats van afgeleverd. Of met andere woorden, setitimer(2) – de draagbare API van de kernel die wordt geadverteerd voor profilering – blijkt ongeschikt te zijn voor het profileren van drukke multi-core systemen :(.

Echter, gezien het feit dat de man-pagina’s de korte beperkingen van CPU-tijdgerelateerde syscalls en de semantiek van procesgestuurde signalen uitleggen, is het niet helemaal duidelijk of dit door de kernelbeheerders als een bug zou worden herkend. Maar zelfs als setitimer(2) opgelost zou zijn, zou het lang duren voordat mensen hun kernels zouden upgraden, dus bracht Rhys het idee van gebruik naar voren timer_create(2) die boekhouding per thread van in behandeling zijnde signalen biedt.

Helaas kreeg het idee geen feedback van de Go-beheerders, dus het probleem bleef sluimeren totdat Rhys en ik er in mei 2021 opnieuw over begonnen te praten. op twitter. We besloten samen te werken, waarbij Rhys werkte aan de kritieke wijzigingen in de CPU-profiler, en ik aan een betere testdekking voor cgo en andere randgevallen, weergegeven in het onderstaande diagram.

Een andere bijzaak van mij was het creëren van een zelfstandig C-programma genaamd proftest om het signaalgeneratie- en leveringsgedrag van te observeren setitimer(2) En timer_create(2) wat een ander bias-probleem uit 2016 bevestigde: setitimer(2)De procesgerichte signalen zijn niet eerlijk verdeeld over CPU-consumerende threads. In plaats van één draad heeft de neiging om vastgeschroefd te raken en ontvangt minder signalen dan alle andere threads. Nu, om eerlijk te zijn, signal(7) stelt dat de kernel een willekeurige thread kiest waaraan procesgerichte signalen moeten worden afgegeven wanneer meerdere threads in aanmerking komen. Maar aan de andere kant vertoont macOS dit soort vooroordelen niet, en ik denk dat dit het punt is waarop ik moet stoppen met het maken van excuses namens de Linux-kernel :).

Maar het goede nieuws is dat, afgezien van de snelle resolutie, timer_create(2) lijkt niet te lijden aan een van de kwalen waar hij last van heeft setitimer(2). Dankzij signaalaccounting per thread levert het op betrouwbare wijze de juiste hoeveelheid signalen en vertoont het geen voorkeur voor bepaalde threads. Het enige probleem is dat timer_create(2) vereist dat de CPU-profiler op de hoogte is van alle threads. Dit is gemakkelijk voor threads die zijn gemaakt door de Go-runtime, maar wordt lastig wanneer cgo-code zijn eigen threads voortbrengt. Rhys-patch lost dit op door te combineren timer_create(2) En setitimer(2). Wanneer een signaal wordt ontvangen, controleert de signaalbehandelaar de oorsprong van het signaal en verwijdert dit als dit niet de beste signaalbron is die beschikbaar is voor de huidige thread. Bovendien heeft de patch ook een aantal slimme stukjes om vooringenomenheid tegen kortstondige threads te vermijden en is hij in het algemeen gewoon een geweldig stukje techniek.

Hoe dan ook, dankzij veel beoordelingen en feedback van de Go-beheerders is de Rhys-patch samengevoegd en geleverd met Go 1.18, waarmee GH 35057 en GH 14434 zijn hersteld. Mijn testcases hebben het niet gehaald, deels omdat het moeilijk is om niet-vliegende beweringen te doen over profileringsgegevens, maar vooral omdat we niet het risico wilden lopen dat die patches de aandacht van upstream zouden afleiden van de belangrijkste veranderingen zelf. Niettemin was deze open source community-samenwerking een van mijn favoriete computerervaringen van 2021!

Bugfixes voor Profiler-labels

Met Profiler-labels (ook wel pprof-labels of -tags genoemd) kunnen gebruikers willekeurige sleutel/waarde-paren associëren met de momenteel actieve goroutine, die worden overgenomen door onderliggende goroutines. Deze labels komen ook terecht in CPU- en Goroutine-profielen, waardoor deze profielen kunnen worden opgedeeld op basis van labelwaarden.

Bij Datadog gebruiken we profilerlabels voor Connecting Go Profiling With Tracing en als onderdeel van het werken aan deze functie heb ik er veel op getest. Dit zorgde ervoor dat ik stapelsporen in mijn profielen waarnam waaraan labels moesten zijn bevestigd, maar dat was niet het geval. Ik ging verder door het probleem te melden als GH 48577 en de oorzaak nader te onderzoeken.

Gelukkig bleek het probleem heel simpel. De CPU-profiler keek soms naar de verkeerde goroutine bij het toevoegen van het label en vereiste in wezen slechts een oplossing van één regel:

-cpuprof.add(gp, stk(:n))
+cpuprof.add(gp.m.curg, stk(:n))

Dit kan in het begin een beetje verwarrend zijn gp is de huidige goroutine en wijst meestal naar dezelfde plaats als gp.m.curg (de huidige goroutine van de draad gp loopt door). Deze twee zijn echter niet hetzelfde wanneer Go van stapel moet wisselen (bijvoorbeeld tijdens het verwerken van signalen of het wijzigen van de grootte van de stapel van de huidige goroutine), dus als het signaal op een ongelukkig moment arriveert, gp verwijst naar een puur interne goroutine die namens de gebruiker wordt uitgevoerd, maar waarbij de labels ontbreken die bij de huidige stacktracering horen stk. Blijkbaar is dit een veel voorkomend probleem, dus het is zelfs gedocumenteerd in de runtime/HACKING.md-handleiding.

Gezien de eenvoud van het probleem heb ik er een patch van één regel voor ingediend. De patch werd echter begroet met het klassieke OSS-antwoord: “Kun je er alsjeblieft een test voor schrijven?” – geleverd door Michael Pratt. Ik twijfelde aanvankelijk aan de ROI van zo’n testcase, maar ik had het niet méér mis kunnen hebben. Het was absoluut heel moeilijk om een ​​niet-flaky test te schrijven om het probleem aan te tonen, en daarom moest de oorspronkelijke versie van de patch worden teruggedraaid. Het proces van het implementeren en verbeteren van de testcase hielp Michael echter om twee extra problemen met betrekking tot labels te identificeren, die hij uiteindelijk allebei oploste:

CL 369741 heeft een fout opgelost die één-voor-één optrad bij het coderen van de eerste batch pprof-monsters, waardoor een klein aantal monsters werd getagd met de verkeerde labels.

CL 369983 heeft een probleem opgelost waardoor systeemgoroutines (bijvoorbeeld voor GC) labels overnamen wanneer ze voortkwamen uit gebruikersgoroutines.

Omdat alle drie de problemen zijn opgelost, worden profilerlabels een stuk nauwkeuriger in Go 1.18. De grootste impact zal zijn voor programma’s die veel cgo gebruiken, aangezien C-aanroepen op een aparte stapel draaien gp != gp.m.curg. Maar zelfs reguliere programma’s ervaren 3-4% ontbrekende labels in Go 1.17 zullen profiteren van deze verandering en volledige nauwkeurigheid bereiken.

Bugfix voor Stack Trace

Een andere profileringsgerelateerde bug die we bij Datadog hebben ontdekt, is GH 49171. Het probleem manifesteerde zich in delta-allocatieprofielen met negatieve allocatietellingen. Toen we dieper gingen graven, kwamen we niet-deterministische programmateller-symbolisatie (pc) tegen als hoofdoorzaak. Wat dit betekent is dat exact dezelfde stacktracering (dezelfde programmatellers) verschillende symbolen (bijvoorbeeld bestandsnamen) zou bevatten in profielen die op een ander tijdstip zijn gemaakt, wat onmogelijk zou moeten zijn.

De bug was ook heel moeilijk zelfstandig te reproduceren, en het kostte me bijna een week voordat het lukte en het upstream rapporteerde. Uiteindelijk bleek het een compiler-regressie te zijn met inline sluitingen in Go 1.17, die werd opgelost door Than McIntosh.

Epiloog

Zoals u kunt zien, bevat Go 1.18 niet alleen boordevol opwindende nieuwe functies, maar ook een aantal belangrijke verbeteringen in de profilering. Laat het me weten als ik iets heb gemist of als je vragen of feedback hebt.

Bedankt voor het lezen en dank aan mijn collega Nick Ripley voor het beoordelen van dit bericht.

— Felix Geisendörfer


Abonneer u op deze blog via RSS of e-mail of ontvang kleine updates van mij via Twitteren.





Source link

Postagens Similares

  • ਜੁਰਾਬਾਂ, ਇੱਕ ਐਡਿਟਿਵ ਕੰਬੀਨੇਟਰਿਕਸ ਸਮੱਸਿਆ ‘ਤੇ ਅਧਾਰਤ ਇੱਕ ਮੇਲ ਖਾਂਦੀ ਖੇਡ || ਗਣਿਤ ∩ ਪ੍ਰੋਗਰਾਮਿੰਗ

    ਕੀ ਤੁਸੀਂ ਇਹਨਾਂ ਛੇ ਵਿੱਚੋਂ ਕਾਰਡਾਂ ਦਾ ਇੱਕ ਸੈੱਟ ਲੱਭ ਸਕਦੇ ਹੋ, ਜਿਵੇਂ ਕਿ ਚੁਣੇ ਹੋਏ ਕਾਰਡਾਂ ‘ਤੇ ਜੁਰਾਬਾਂ ਨੂੰ ਮੇਲ ਖਾਂਦੇ ਜੋੜਿਆਂ ਵਿੱਚ ਵੰਡਿਆ ਜਾ ਸਕਦਾ ਹੈ? (ਇੱਕੋ ਜੁਰਾਬ ਦੇ ਡੁਪਲੀਕੇਟ ਜੋੜੇ ਠੀਕ ਹਨ) ਸਪੋਇਲਰ: ਜੇਕਰ ਕਾਰਡ ਇੰਡੈਕਸ ਕੀਤੇ ਗਏ ਹਨ 1 2 3 4 5 6 ਫਿਰ ਹੇਠਾਂ ਦਿੱਤੇ ਤਿੰਨ ਸਬਸੈੱਟ ਕੰਮ ਕਰਦੇ…

  • எனது சிறந்த துணை £100 கொள்முதல்

    20 ஏப்ரல் 2026 நான் சமீபத்தில் தி ஐடியா ரோஸ்டரியின் தனிப்பட்ட வாழ்க்கை கேம்சேஞ்சர்களைப் பற்றிய ஒரு அத்தியாயத்தைக் கேட்டுக்கொண்டிருந்தேன், அத்தியாயத்தின் முடிவில், ஹெர்மன் ஜேசனிடம் கேட்டார்: நீங்கள் இதுவரை £100க்கு குறைவாக வாங்கிய சிறந்த கொள்முதல் எது? ஜேசன் ஒரு முட்டை வேட்டையாடுபவர், ஹெர்மனுக்கு அது ஒரு காபி கிரைண்டர். இந்த விவாதம் என்னுடையது என்ன என்பதைப் பற்றி சிந்திக்க வைத்தது, முதலில் எனக்கு உறுதியாக தெரியவில்லை. ஆனால் சிறிது யோசனைக்குப் பிறகு அது என்னைத்…

  • プロパティの定義

    2012 年 3 月 子供の頃、私は大岡忠相という18世紀の日本の有名な裁判官についての物語の本を読みました。彼が判断した事件の1つは食料品店のオーナーから持ち込まれたものだった。お米しか買えない貧乏学生が、食料品店から漂ってくる美味しそうな料理の匂いを楽しみながらご飯を食べていました。オーナーは学生に、自分が楽しんでいた匂いの代金を支払ってほしいと考えました。 学生は彼の匂いを盗んでいました! RIAA や MPAA が音楽や映画を盗んだとして人々を非難しているのを聞くと、この話をよく思い出します。 私たちにとって、匂いを財産として扱うのはばかげているように思えます。しかし、匂いに対して料金を請求するシナリオは想像できます。私たちが月面基地に住んでいて、そこでは空気をリットル単位で購入しなければならないことを想像してみてください。航空会社が追加料金で香りを追加することは想像できます。 私たちにとって匂いを財産として扱うことがばかげているように思える理由は、それがうまくいかないからです。ただし、月面基地では機能するでしょう。 何が財産としてカウントされるかは、何が財産として扱われるかによって決まります。そして、それは変わる可能性があるだけでなく、すでに変わっています。人間は常に (人間の定義によっては常に) 身につけている小さなアイテムを所有物として扱ってきたかもしれません。しかし、狩猟採集民は、たとえば土地を私たちのように財産として扱いませんでした。 (1) 非常に多くの人が不動産には単一の不変の定義があると考えている理由は、その定義が非常にゆっくりと変化するためです。 (2) しかし、私たちは今、そのような変化の真っただ中にいます。レコードレーベルや映画スタジオはかつて、月面基地でチューブを通して空輸するように、自分たちが作った作品を配布していた。しかし、ネットワークの到来により、私たちは呼吸可能な大気のある惑星に移動したかのようです。データは今、匂いのように移動します。そして、希望的観測と短期的な欲望の組み合わせによって、レーベルとスタジオは自らを食料品店のオーナーの立場に置き、私たち全員が彼らの匂いを盗んだと非難しています。 (私が短期的な強欲と言っている理由は、レーベルやスタジオの根本的な問題は、レーベルやスタジオを経営する人々が資本ではなくボーナスによって動かされているということだからです。もし彼らが資本によって動かされているなら、テクノロジーの変化と戦うのではなく、それを利用する方法を探すでしょう。しかし、新しいものを構築するには時間がかかりすぎます。彼らのボーナスは今年の収益に依存しており、ボーナスを増やす最善の方法は、すでに行っているものからより多くのお金を引き出すことです。) では、これは何を意味するのでしょうか?コンテンツに課金できないようにすべきでしょうか?その質問には、「はい」または「いいえ」という単一の答えはありません。コンテンツに課金することが機能する場合、ユーザーはコンテンツに課金できるようにする必要があります。 しかし、私が「うまくいく」というのは、「彼らがそれをやり遂げることができるとき」よりももっと微妙なことを意味します。つまり、人々が社会を歪めることなくコンテンツに課金できるようになったときのことです。結局のところ、たとえその必要がなくなったとしても、ここでも私たち全員がチューブを通して呼吸し続けることを義務付ける法律の制定に向けたロビー活動が成功すれば、月面基地で匂いを販売している企業は地球上でも匂いを販売し続けることができるだろう。 レーベルやスタジオが取っているクレイジーな法的措置には、その雰囲気がかなり出ている。新聞や雑誌も同様にひどいものですが、少なくとも順調に​​衰退しています。 RIAA と MPAA は、可能であれば私たちにチューブを通して呼吸させるだろう。 結局のところ、それは常識に帰着します。無作為に選ばれた人々に対する模範的な懲罰として大規模訴訟を利用しようとしたり、可決されればインターネットを破壊する法律を求めてロビー活動をしたりして法制度を悪用しているとき、それはあなたが機能しない財産の定義を使用していることの明白な証拠です。 ここで、機能する民主主義と複数の主権国家が役立つのです。もし世界に単一の独裁政府があれば、レーベルやスタジオは財産の定義を自分たちの望むものにする法律を買収できるだろう。しかし幸いなことに、米国の著作権植民地ではない国がまだいくつかあり、米国でも、十分な数の政治家が依然として実際の有権者を恐れているようだ。 (3) 米国を運営している人々は、有権者や他国が自分たちの意志に従おうとしないのを気に入らないかもしれないが、最終的には、自分たちの目的のために法律を曲げようとする人々に単一の攻撃点がないことが私たちの利益になる。私有財産は非常に有用なアイデアであり、おそらく私たちの最大の発明の 1 つです。これまでのところ、その新しい定義が私たちに物質的な富の増加をもたらしてきました。 (4) 最新のものも同様であると考えるのが合理的だと思われます。少数の有力者がアップグレードするのが面倒だからという理由で、全員が古いバージョンを実行し続けなければならないとしたら、それは大惨事になります。 注意事項 (1) 狩猟採集民についてさらに詳しく知りたい場合は、エリザベス マーシャル トーマスの本を強くお勧めします。 無害な人々 そして 古いやり方。 (2) しかし、財産の定義の変化は主に技術の進歩によって引き起こされ、技術の進歩は加速しているため、おそらく財産の定義の変化の速度も加速するでしょう。つまり、社会がそのような変化に適切に対応できることがますます重要になります。なぜなら、変化はますます加速するからです。 (3) 私の知る限り、「著作権植民地」という用語はマイルズ・ピーターソンによって最初に使用されました。 (4) テクノロジーの状態は、単に財産の定義の関数ではありません。それらはお互いを制約します。しかし、そうだとしても、テクノロジーの状態に影響を与えずに(そしておそらく害を与えずに)財産の定義をいじることはできません。ソ連の歴史はそれを鮮やかに示しています。 ありがとう この草稿を読んでくれた Sam Altman と Geoff…

  • جدید کاروں کے بارے میں ایک طنز

    10 جولائی 2026 | 3 منٹ پڑھیں میں نے حال ہی میں ایک نیا Peugeot خریدا ہے اور ان کے آن لائن پلیٹ فارم پر سیٹ اپ کرنے کا تجربہ کم از کم کہنا تکلیف دہ رہا ہے۔ کل میں نے اپنا چمکدار نیا (میرے لیے) Peugeot E-3008 GT اٹھایا۔ یہ ایک خوبصورت کار ہے…

Deixe um comentário

O seu endereço de email não será publicado. Campos obrigatórios marcados com *