S4 fa plasser

S4 fa plasser h1>

Fa via App Store Les dette innlegget i var app!

Fa en referanse til en struktur i array.

Jeg vil endre et felt av en struktur som er inne i en matrise uten a matte sette hele strukturen. I eksemplet nedenfor vil jeg sette ett felt med element 543 i matrisen. Jeg onsker ikke a kopiere hele elementet (fordi kopiering av MassiveStruct ville skade ytelse).

Er det en mate a gjore det pa C #? Eller ma jeg alltid kopiere hele strukturen ut av matrisen, endre kopien, og deretter sette den modifiserte kopien tilbake i matrisen.

Det eneste problemet er at du prover a ringe en instansmetode fra en statisk metode, uten en forekomst av P.

Lag en statisk metode (eller opprett en forekomst av P som du skal ringe til), og det gar bra. Det handler om a lese kompilatoren feil 🙂

Nar det er sagt, vil jeg sterkt anbefale deg a:

Unnga a lage massive strukturer hvis det er mulig. Unnga a lage mutable structs hvis det er mulig. Unnga offentlige felt.

[rediger 2017: se viktige kommentarer angaende C # 7 pa slutten av dette innlegget]

Etter mange ars bryting med dette noyaktige problemet, vil jeg oppsummere de fa teknikkene og losningene jeg har funnet. Stilistiske smaker til side, arrays of structs er egentlig den eneste masselagringsmetoden som er tilgjengelig i C #. Hvis appen din virkelig behandler millioner av mellomstore objekter under hoye gjennomstromningsforhold, er det lite annet valg.

Jeg er enig med @kaalus at objektoverskrifter og GC-trykk raskt kan montere; Grammatikkbehandlingssystemet kan manipulere 8-10 gigabyte (eller mer) strukturanalyser pa mindre enn et minutt nar man analyserer eller genererer lange naturlige sprak setninger. Cue koret av «C # er ikke ment for disse problemene, bytte til monteringssprak, wire-wrap opp en FPGA, etc.» I stedet la oss kjore noen tester.

Forst av alt er det avgjorende a ha full forstaelse av hele spekteret av verdiktype (struct) ledelsesproblemer og klassen vs. struct avvente sote flekker. Ogsa selvfolgelig boksing, pinning / usikker kode, faste buffere, GCHandle, IntPtr og mer, men viktigst av alt etter min mening, klok bruk av klarte pekere.

Din beherskelse av dette emnet vil ogsa inkludere kunnskap om at, hvis du skulle inkludere en eller flere referanser til administrerte typer (i motsetning til bare bevarbare primitiver), sa er alternativene dine for a fa tilgang til strukturen med usikre pekere sterkt redusert. Dette er ikke et problem for den administrerte pekermetoden jeg nevner nedenfor. Sa generelt, inkludert objekt referanser er bra og endres ikke mye om denne diskusjonen.

A, og hvis du virkelig trenger a bevare din usikre tilgang, kan du bruke en GCHandle i Normal modus for a lagre objektreferanser i strukturen pa ubestemt tid. Heldigvis setter GCHandle inn i din struktur ikke utloser det usikre tilgangsbudet. (Merk at GCHandle selv er en verdi-type, og du kan til og med definere og ga til byen med.

. og sa videre. Som en verdi type, er GCHandle avbildet direkte inn i strukturen din, men apenbart er det ikke noen referansetyper det lagrer. De er ute i bunken, ikke inkludert i det fysiske oppsettet av matrisen din. Endelig, pa GCHandle, v r klar over sin kopi-semantikk, fordi du vil ha en minnelekkasje hvis du ikke til slutt frigjor hver GCHandle du tildeler.

@Ani minner oss om at noen mennesker anser mutable structs «ondskap», men det er egentlig det faktum at de er ulykkelige utsatt, det er problemet. Faktisk, med henvisning til OP-eksemplet,

er akkurat det vi prover a oppna: Fa tilgang til vare dataposter pa stedet. (Legg merke til at syntaksen for et skjult array er identisk, men jeg snakker bare om ikke-skarpe arrayer av brukerdefinerte verdityper her.) For mine egne programmer anser jeg det generelt for en alvorlig feil hvis jeg moter en overdimensjonert gjengivelse struct som (ved et uhell) har blitt helt avbildet ut av sin array lagringsrad:

Savidt hvor stor (bred) strukturen din kan eller burde v re, spiller det ingen rolle, fordi du skal v re forsiktig sa du aldri lar dem gjore det jeg nettopp viste, det vil si migrere ut av deres rekkevidde. Det er lett nok a definere en struktur for a overlegge vart array (faktisk tenker pa strukturen som en vakuum «minnesmal» – i motsetning til en datafylke, innkapslingsmaskin – oppfordrer til riktig tenkning.)

Denne har 6 int s for totalt 24 byte. Du vil vurdere og v re oppmerksom pa pakkealternativer for a fa en justeringsvennlig storrelse. Men overdreven polstring kan kutte inn i minnesbudsjettet: fordi et viktigere hensyn er 85.000 byte grensen pa ikke-LOH gjenstander. Pass pa at rekordstorrelsen multiplisert med det forventede antallet rader ikke overskrider denne grensen.

Sa for dette eksempelet, vil du v re best anbefales a beholde ditt utvalg av rec s til ikke mer 3000 rader hver. Forhapentligvis kan soknaden din bli designet rundt dette sote stedet. Dette er ikke sa begrensende nar du husker at – alternativt – hver rad ville v re et separat soppeloppsamlet objekt, i stedet for bare ett array. Du har kuttet din formspredning med tre storrelsesordener, noe som er bra for en dags arbeid. Saledes. .NET-miljoet her styrer oss sterkt med en ganske spesifikk begrensning: Det ser ut til at hvis du malretter mot programmets minnesignal mot monolitiske tildelinger i 30-70 KB-serien, sa kan du virkelig komme unna med mange og mange, og faktisk blir du i stedet begrenset av et tynnere sett med ytelse flaskehalser (nemlig bandbredde pa maskinbussen).

Sa na har du en enkelt .NET referansetype (array) med 3000 6-tuples i fysisk sammenhengende tabul r lagring. Forst og fremst ma vi v re forsiktige med a aldri «plukke opp» en av strukturene. Som Jon Skeet bemerker ovenfor, «Massive strukturer vil ofte utfore verre enn klasser», og dette er helt riktig. Det er ingen bedre mate a forlamme minnesbussen enn a begynne a kaste plumpverdetyper rundt willy-nilly.

Sa la oss kapitalisere pa et sjeldent nevnt aspekt av strukturen: Alle objekter (og felt av de objekter eller strukturer) av alle rader i hele arrayet blir alltid initialisert til standardverdiene. Du kan begynne a plugge verdier i, en om gangen, i en hvilken som helst rad eller kolonne (felt), hvor som helst i arrayet. Du kan legge igjen noen felt til standardverdiene, eller erstatte nabofelt uten a forstyrre en i midten. Gone er den irriterende manuelle initialiseringen som kreves med stakk-residente (lokale variabel) strukturer for bruk.

Noen ganger er det vanskelig a opprettholde felt-for-felt tiln rming fordi. NET prover alltid a fa oss til a sprenge i en helt ny «d-up struct» – men for meg er denne sakalte «initialiseringen» bare et brudd pa vart tabu (mot a plukke hele strukturen ut av matrisen), i et annet skjema.

Na kommer vi til kernen i saken. A fa tilgang til tabelldataene dine pa stedet, sorger for at du minimerer data-shuffling busywork. Men ofte er dette en ubeleilig problemfri. Array-tilgangene kan v re sakte i. NET, pa grunn av begrensningskontroll. Sa hvordan opprettholder du en «arbeidende» pekeren inn i interioret i en matrise, for a unnga at systemet kontinuerlig rekomputerer indekseringsforskyvningene.

Evaluering.

La oss evaluere ytelsen til fem forskjellige metoder for manipulering av individuelle felter innenfor rekkevidde for verdier for arrayoppbevaring. Testen nedenfor er utformet for a male effektiviteten ved a intuitivt fa tilgang til datafeltene i en struktur som er plassert pa en rekke indekser, pa stedet – det er «der de ligger» uten a ekstrahere eller omskrive hele strukturen (arrayelementet). Fem forskjellige tilgangsmetoder blir sammenlignet, med alle andre faktorer holdt like.

De fem metodene er som folger:

Normal, direkte array tilgang via firkantede parenteser og feltet spesifiserende punktum. Merk at, i .NET, er arrayer en spesiell og unik primitiv av Common Type System. Som @Ani nevner ovenfor, kan denne syntaksen ikke brukes til a endre et enkelt felt i en referansefilm, for eksempel en liste, selv nar den er parameterisert med en verdi-type. Ved hjelp av den ikke-dokumenterte __makeref C # sprak sokeordet. Behandlet peker via en delegat som bruker ref-sokeordet «Usikre» pekere Samme som nr. 3, men bruker en C # -funksjon i stedet for en delegat.

For jeg gir C # testresultater, her er test sele implementering. Disse testene ble kjort pa .NET 4.5, en AnyCPU-utgivelsesbygging som kjorer pa x64, Workstation gc. (Merk at fordi testen ikke er interessert i effektiviteten av a tildele og de-allokere selve arrayet, gjelder ikke LOH-overveien nevnt ovenfor.)

Fordi koden fragmenter som implementerer testen for hver bestemt metode er long-ish, gir jeg resultatene forst. Tiden er «flatt»; lavere betyr bedre.

Jeg var overrasket over at disse resultatene var sa utvetydige. TypedReferences er tregeste, antagelig fordi de slar rundt typen informasjon sammen med pekeren. Tatt i betraktning IL-koden for den belaborerte «Normal» versjonen, utforte den overraskende godt. Modusoverganger ser ut til a skade usikker kode til det punktet der du virkelig ma rettferdiggjore, planlegge og male hvert sted du skal distribuere det.

Men hendene ned raskeste tider oppnas ved a utnytte ref-sokeordet i funksjonsparameterpassering med det formal a peke pa en indre del av arrayet, og dermed eliminere «per-field-access» arrayindekseringsberegningen.

Kanskje utformingen av testen min favoriserer denne, men testscenariene er representative for empiriske bruksmonstre i appen min. Det som overrasket meg om disse tallene er at fordelen med a bo i administrert modus – mens du ogsa har poengene dine – ble ikke avbrutt ved a matte ringe en funksjon eller pakalle gjennom en delegat.

Vinneren.

Raskeste en: (Og kanskje enklest ogsa?)

Men det har ulempen at du ikke kan holde relatert logikk sammen i programmet: Implementeringen av funksjonen er delt pa to C # -funksjoner, f og test_f.

Vi kan lose dette problemet med bare et lite offer i ytelse. Den neste er i utgangspunktet identisk med det foregaende, men inneb rer en av funksjonene i den andre som en lambda-funksjon.

Et n rt annet.

Hvis du erstatter den statiske funksjonen i det forrige eksempelet med en inline-delegat, ma du bruke ref-argumenter, som i sin tur utelukker bruken av Func & lt; T & gt; lambda syntaks; i stedet ma du bruke en eksplisitt delegat fra gammeldags .NET.

Ved a legge til denne globale erkl ringen en gang:

. vi kan bruke det gjennom hele programmet til a reflektere direkte til elementer i array rec [], og fa tilgang til dem inline:

Ogsa, selv om det kan se ut som en ny lambda-funksjon blir instantiated pa hver samtale, vil dette ikke skje hvis du er forsiktig: Nar du bruker denne metoden, ma du ikke «lukke» over noen lokale variabler (det vil si, referer til variabler som er utenfor lambda-funksjonen, inne i kroppen), eller gjor noe annet som vil b re din delegerte forekomst fra a v re statisk. Hvis en lokal variabel kommer til a falle inn i lambdaen din, og lambdaen blir dermed forfremmet til en forekomst / klasse, vil du sannsynligvis merke en forskjell som det forsoker a skape fem millioner delegater.

Sa lenge du holder lambda-funksjonen fri for disse bivirkningene, vil det ikke v re flere forekomster; Det som skjer her er at nar C # bestemmer at en lambda ikke har noen uklar eksistens, oppretter den latisk (og caches) en statisk singleton. Det er litt uheldig at en ytelsesvariasjon dette drastiske er skjult fra vart syn som en stille optimalisering. Samlet sett liker jeg denne metoden. Det er raskt og rotfritt – bortsett fra de bisarre parentesene, ingen av dem kan utelates her.

Og resten.

For fullstendighet, her er resten av testene: normal bracketing-plus-prikk; TypedReference; og usikre pekere.

For minneintensivt arbeid i store C # -apper, er det greit a bruke administrerte pekere for a fa direkte tilgang til feltene for verdiktype elementelementer pa stedet.

Hvis du er virkelig serios med ytelse, kan dette v re nok grunn til a bruke C ++ / CLI (eller CIL, for den saks skyld) i stedet for C # for de aktuelle delene av appen din, fordi disse sprakene gir deg mulighet til direkte a erkl re administrerte pekere i en funksjon kroppen.

I C # er den eneste maten a opprette en administrert peker pa, a erkl re en funksjon med et ref eller ut-argument, og sa vil callee observere den administrerte pekeren. For a fa ytelsesfordelene i C # ma du derfor bruke en av de (overste to) metodene som er vist ovenfor.

Dessverre, disse distribuerer kludge av a dele en funksjon i flere deler bare for a fa tilgang til et arrayelement. Selv om det er betydelig mindre elegant enn den tilsvarende C ++ / CLI-koden, vil tester tyde pa at selv i C #, for applikasjoner med hoy ytelse, oppnar vi fortsatt en stor ytelsesfordel versus naiv verdi-type array-tilgang.

[redigere 2017: Mens utgivelsen av C # 7 i Visual Studio 2017 muligens gir en liten grad av forkunnskap til denne artikkelen sin formaning, gjor de spesifikke metodene som er beskrevet ovenfor, helt foreldet samtidig. Kort sagt, de nye ref locals funksjonen i spraket tillatelser koden som dette:

. som helt eliminerer behovet for kludger som de jeg diskuterte ovenfor, og gir mye bedre tilgang til den bestpresterende metodikken til de jeg har gjennomgatt. Ytelsen med de nye funksjonene bor bare v re bedre enn vinneren av metoder sammenlignet over.

Ironisk nok tilfoyer C # 7 ogsa lokale funksjoner, en funksjon som umiddelbart vil lose klagen om darlig innkapsling jeg hevet for to av de nevnte hackene. Heldigvis er hele virksomheten til a sprede dedikerte funksjoner bare for a fa tilgang til klarte pekere, helt fullstendig.

Mens Jon Skeet er korrekt om hvorfor programmet ikke samler, kan du bare gjore det:

. og den vil operere direkte pa strukturen i arrayet i stedet for pa en kopi.

V r oppmerksom pa at denne ideen bare fungerer for arrays, andre samlinger som lister returnerer en kopi ut fra indekser-getter (gir deg en kompilatorfeil hvis du prover noe lignende pa den resulterende verdien).

Pa et annet notat er mutable structs ansett som onde. Er det en sterk grunn til at du ikke vil gjore S til en klasse?

Du kan prove a bruke en videresending tom struktur som ikke inneholder de faktiske dataene, men bare holder en indeks til et dataproviderobjekt. Pa denne maten kan du lagre store mengder data uten a komplisere objektgrafen. Jeg er veldig sikker pa at det skal v re ganske enkelt i ditt tilfelle a erstatte din gigantiske struktur med en videresending emtpy struct sa lenge du ikke prover a marshalere den til uhandtert kode.

Ta en titt pa denne strukturen. Det kan inneholde sa mye data i det som du onsker. Trikset er at du lagrer de faktiske dataene i et annet objekt. Pa denne maten far du referansesemisjon og fordelene med strukturer som bruker mindre minne enn klassobjekter og raskere GC-sykluser pa grunn av en enklere objektgraf (hvis du har mange forekomster (millioner) av dem rundt).