Çfarë është grupimi i objekteve? Përmirësimi i performancës së kujtesës në C#
Shpërndarja e memories në C# është relativisht e shtrenjtë dhe është një pikë kyçe e optimizimit për çdo aplikacion kritik për performancën. Grumbullimi i objekteve është një teknikë që mund të ndihmojë në zvogëlimin e shpenzimeve të përgjithshme të një aplikacioni me memorie intensive.
Si e përmirëson performancën bashkimi i objekteve?
Në fund të fundit, performanca e kompjuterëve në thelb është e kufizuar në dy gjëra: shpejtësia e përpunimit të CPU dhe performanca e kujtesës. E para mund të përmirësohet me algoritme më efikase që përdorin më pak cikle ore, por shpesh është më e vështirë të optimizohet përdorimi i kujtesës, veçanërisht kur punoni me grupe të dhënash të mëdha dhe shkallë shumë të shkurtra kohore.
Grumbullimi i objekteve është një teknikë e përdorur për të reduktuar alokimet të kujtesës. Shpesh nuk ka nevojë të shpërndani memorie, por mund të mos keni nevojë të shpërndani aq shpesh sa bëni. Alokimet e objekteve janë të ngadalta për disa arsye—memoria themelore ndahet në grumbull (që është shumë më e ngadaltë se alokimet e grupit të llojit të vlerës) dhe objektet komplekse mund të kenë konstruktorë që kërkojnë performancë intensive. Plus, për shkak se është memorie e bazuar në grumbull, mbledhësi i plehrave do të duhet ta pastrojë atë, gjë që mund të dëmtojë performancën nëse e aktivizoni shumë shpesh.
Për shembull, le të themi se keni një lak që funksionon shumë herë dhe shpërndan një objekt të ri si një listë për çdo ekzekutim. Kjo është shumë memorie që përdoret dhe nuk po pastrohet derisa të përfundojë e gjitha dhe të ketë përfunduar mbledhja e mbeturinave. Kodi i mëposhtëm do të ekzekutohet 10,000 herë dhe do të lërë 10,000 lista pa pronar të alokuara në memorie në fund të funksionit.
Kur mbledhësi i plehrave përfundimisht të funksionojë, do ta ketë shumë të vështirë të pastrojë gjithë këtë mbeturinë, gjë që do të ndikojë negativisht në performancën ndërsa pret që GC të përfundojë.
Në vend të kësaj, një qasje më e arsyeshme është ta inicializoni atë një herë dhe ta ripërdorni objektin. Kjo siguron që po ripërdorni të njëjtën hapësirë në memorie, në vend që ta harroni atë dhe ta lini grumbulluesin e plehrave të merret me të. Nuk është magji, dhe ju do të duhet të pastroni përfundimisht.
Për këtë shembull, qasja e riciklueshme do të ekzekutonte Lista e re
përpara ciklit për të bërë ndarjen e parë dhe më pas ekzekutimin e . Pastro
ose rivendosjen e të dhënave për të kursyer hapësirën e memories dhe mbeturina të krijuara. Pas përfundimit të këtij cikli, do të mbetet vetëm një listë në memorie, e cila është shumë më e vogël se 10,000 prej tyre.
Object Pooling është në thelb një zbatim i përgjithshëm i këtij koncepti. Është një koleksion objektesh që mund të ripërdoren. Nuk ka asnjë ndërfaqe zyrtare për një, por në përgjithësi ata kanë një ruajtje të brendshme të të dhënave dhe zbatojnë dy metoda: GetObject()
dhe ReleaseObject()
.
Në vend që të alokoni një objekt të ri, ju kërkoni një nga grupi i objekteve. Pishina mund të krijojë një objekt të ri nëse nuk e ka të disponueshëm. Më pas, kur të mbaroni me të, ju e lëshoni atë objekt përsëri në pishinë. Në vend që ta fusë objektin në plehra, grupi i objekteve e mban atë të ndarë, por e pastron atë nga të gjitha të dhënat. Herën tjetër që të ekzekutoni GetObject
, ai kthen objektin e ri bosh. Për objekte të mëdha, si Listat, bërja e kësaj është shumë më e lehtë në memorien themelore.
Duhet të siguroheni që po e lëshoni objektin përpara se ta përdorni përsëri, sepse shumica e pishinave do të kenë një numër maksimal të objekteve boshe që i mbajnë në dorë. Nëse përpiqeni të merrni 1000 objekte nga pishina, do ta thani atë dhe do të fillojë t'i ndajë ato normalisht sipas kërkesës, gjë që e prish qëllimin e një pishine.
Në rastet kritike për performancën, veçanërisht kur punoni shpesh me shumë të dhëna të përsëritura, bashkimi i objekteve mund të përmirësojë në mënyrë dramatike performancën. Megjithatë, kjo nuk është tërheqëse, dhe ka shumë raste që nuk do të dëshironit të bashkoni objektet. Alokimet me new
janë ende mjaft të shpejta, kështu që nëse nuk shpërndani pjesë të mëdha memorie shumë shpesh, ose nëse shpërndani objekte me konstruktorë të rëndë me performancë, shpesh është më mirë të përdorni vetëm i ri
në vend që të bashkohen në mënyrë të panevojshme, veçanërisht duke pasur parasysh se vetë grupi i shton shpenzimet dhe kompleksitetin shtesë alokimit. Alokimi i një objekti të vetëm të rregullt do të jetë gjithmonë më i shpejtë se ndarja e një objekti me një grup të vetëm; diferenca e performancës vjen kur ndani shumë herë.
Ju gjithashtu do të duhet të siguroheni që zbatimi juaj i bashkimit pastron siç duhet gjendjen e objektit. Nëse jo, mund të rrezikoni GetObject
të ktheni diçka që ka të dhëna të vjetruara. Ky mund të jetë një problem masiv nëse, për shembull, i ktheni të dhënat e një përdoruesi tjetër kur merrni dikë tjetër. Në përgjithësi, nuk është një problem nëse bëhet siç duhet, por është diçka që duhet mbajtur parasysh.
Nëse dëshironi të përdorni Object Pools, Microsoft ofron një zbatim në Microsoft.Extensions.ObjectPool
. Nëse dëshironi ta zbatoni vetë, mund të hapni burimin për të parë se si funksionon në DefaultObjectPool
. Në përgjithësi, do të keni një grup të ndryshëm objektesh për çdo lloj, me një numër maksimal objektesh për t'u mbajtur në pishinë.