Vra

Ek ken hierdie vraag het gewees gedoen maar ek het 'n effens ander kinkel daaraan.Verskeie het daarop gewys dat dit voortydige optimalisering is, wat heeltemal waar is as ek net ter wille van praktiese en praktiese onthalwe vra.My probleem is gewortel in 'n praktiese probleem maar ek is nog steeds nuuskierig nietemin.


Ek skep 'n klomp SQL-stellings om 'n skrif te skep (soos daarin sal op skyf gestoor word) om 'n databasisskema te herskep (maklik baie baie honderde tabelle, aansigte, ens.).Dit beteken my string aaneenskakeling is slegs byvoeging.StringBuilder, volgens MSDN, werk deur 'n interne buffer te hou (sekerlik 'n char[]) en kopieer string karakters daarin en hertoewysing die skikking soos nodig.

My kode het egter baie herhalende stringe ("SKEP TABEL [", "GAAN ", ens.) wat beteken dat ek voordeel daaruit kan trek geïnterneer word maar nie as ek StringBuilder gebruik nie, aangesien hulle elke keer gekopieer sou word.Die enigste veranderlikes is in wese tabelname en sulkes wat reeds bestaan ​​as stringe in ander voorwerpe wat reeds in die geheue is.

So sover ek kan sê dat nadat my data ingelees is en my voorwerpe geskep is wat die skema-inligting bevat, kan al my stringinligting hergebruik word deur internering, ja?

As ons dit aanvaar, sou 'n lys of gekoppelde lys van stringe dan nie vinniger wees omdat hulle wysers na geïnterneerde stringe behou nie?Dan is dit net een oproep na String.Concat() vir 'n enkele geheue toekenning van die hele string wat presies die korrekte lengte is.

'n Lys sal string[] geïnterneerde wysers moet hertoewys en 'n gekoppelde lys sal nodusse moet skep en wysers moet wysig, so dit is nie "vry" om te doen nie, maar as ek aaneenskakeling van baie duisende geïnterneerde snare dan sal dit lyk asof hulle meer doeltreffend sal wees.

Ek veronderstel nou dat ek met 'n paar heuristiek oor karaktertellings vir elke SQL-stelling kan vorendag kom en elke tipe kan tel en 'n rowwe idee kry en my StringBuilder-kapasiteit vooraf instel om die hertoewysing van sy char te vermy[] maar ek sal met 'n redelike marge moet oorskiet om die waarskynlikheid van hertoewysing te verminder.

So vir hierdie geval, wat die vinnigste sou wees om 'n enkele aaneengeskakelde string te kry:

  • StringBuilder
  • Lys<string> van geïnterneerde snare
  • Gekoppelde Lys<string> van geïnterneerde stringe
  • StringBuilder met 'n kapasiteit heuristiek
  • Iets anders?

As 'n aparte vraag (Ek gaan dalk nie altyd skyf toe nie) na bogenoemde:sou 'n enkele StreamWriter na 'n uitvoerlêer nog vinniger wees?Alternatiewelik, gebruik 'n lys of gekoppelde lys en skryf dit dan na 'n lêer uit die lys in plaas daarvan om eers in die geheue aaneen te skakel.

EDIT:Soos versoek, die verwysing (.NET 3.5) na MSDN.Dit sê: “Nuwe data word aan die einde van die buffer aangeheg indien kamer beskikbaar is;anders word 'n nuwe, groter buffer toegewys, data van die oorspronklike buffer word na die nuwe buffer gekopieer, dan word die nuwe data aan die nuwe buffer gevoeg." Dit beteken vir my 'n char[] wat hertoegewys word om dit groter te maak (wat vereis dat ou data na die skikking verander word gekopieer) en dan bygevoeg word.

Was dit nuttig?

Oplossing

Vir jou aparte vraag, Win32 het 'n WriteFileGather funksie, wat doeltreffend 'n lys van (geïnterneerde) stringe na skyf kan skryf - maar dit sal 'n noemenswaardige verskil maak slegs wanneer dit asinchroon genoem word, aangesien die skyfskryf alles behalwe uiters groot samevoegings sal oorskadu.

Vir jou hoofvraag:tensy jy megagrepe se skrif bereik, of tienduisende skrifte, moenie bekommerd wees nie.

Jy kan van StringBuilder verwag om die toekenningsgrootte op elke hertoewysing te verdubbel.Dit sou beteken dat die groei van 'n buffer van 256 grepe na 1MB net 12 hertoewysings is - redelik goed, gegewe dat jou aanvanklike skatting 3 ordes van grootte van die teiken was.

Suiwer as 'n oefening, sommige skattings:Die bou van 'n buffer van 1MB sal ongeveer 3 MB geheue vee (1MB bron, 1MB teiken, 1 MB as gevolg van kopiëring tydens reëling).

'n Gekoppelde lysimplementering sal ongeveer 2MB vee (en dit ignoreer die 8 grepe / objek bokoste per string verwysing).So jy spaar 1 MB geheue lees/skryf, in vergelyking met 'n tipiese geheue bandwydte van 10Gbit/s en 1MB L2 kas.)

Ja, 'n lysimplementering is moontlik vinniger, en die verskil sal saak maak as jou buffers 'n orde van grootte groter is.

Vir die veel meer algemene geval van klein stringe, is die algoritmiese wins weglaatbaar, en maklik geneutraliseer deur ander faktore:die StringBuilder-kode is waarskynlik reeds in die kodekas, en 'n lewensvatbare teiken vir mikrooptimerings.Die gebruik van 'n string intern beteken ook geen kopie as die finale string by die aanvanklike buffer pas nie.

Die gebruik van 'n gekoppelde lys sal ook die hertoewysingsprobleem van O(aantal karakters) na O(aantal segmente) afbring - jou lys stringverwysings het dieselfde probleem as 'n string karakters!


Dus, IMO is die implementering van StringBuilder die regte keuse, geoptimaliseer vir die algemene geval, en verswak meestal vir onverwags groot teikenbuffers.Ek sou verwag dat 'n lysimplementering eers vir baie klein segmente sal afbreek, wat eintlik die uiterste soort scenario is waarvoor StringBuilder probeer optimaliseer.

Tog sal dit interessant wees om 'n vergelyking van die twee idees te sien, en wanneer die lys vinniger begin wees.

Ander wenke

As ek so iets implementeer, sou ek nooit 'n StringBuilder (of enige ander in die geheue buffer van jou skrif) bou nie.Ek sou dit eerder na jou lêer uitstroom en alle stringe inlyn maak.

Hier is 'n voorbeeld van pseudo-kode (nie sintakties korrek of iets nie):

FileStream f = new FileStream("yourscript.sql");
foreach (Table t in myTables)
{
    f.write("CREATE TABLE [");
    f.write(t.ToString());
    f.write("]");
    ....
}

Dan sal jy nooit 'n in die geheue voorstelling van jou skrif nodig hê nie, met al die kopiëring van stringe.

Menings?

In my ervaring het ek StringBuilder behoorlik toegewys, beter as alles anders vir groot hoeveelhede stringdata.Dit is selfs die moeite werd om 'n bietjie geheue te mors deur jou skatting met 20% of 30% te oorskiet om hertoewysing te voorkom.Ek het tans nie harde nommers om dit te rugsteun deur my eie data te gebruik nie, maar kyk na hierdie bladsy vir meer.

Soos Jeff egter daarvan hou om uit te wys, moenie voortydig optimaliseer nie!

EDIT:Soos @Colin Burnett uitgewys het, stem die toetse wat Jeff gedoen het nie saam met Brian se toetse nie, maar die punt om Jeff se plasing te koppel was oor voortydige optimalisering in die algemeen.Verskeie kommentators op Jeff se bladsy het probleme met sy toetse opgemerk.

Eintlik StringBuilder gebruik 'n voorbeeld van String intern. String is in werklikheid veranderlik binne die System vergadering, en daarom StringBuilder kan bo-op gebou word.Jy kan maak StringBuilder 'n bietjie meer effektief deur 'n redelike lengte toe te ken wanneer jy die instansie skep.Op hierdie manier sal jy die aantal grootteveranderingsbewerkings uitskakel/verminder.

Snaarinternering werk vir snare wat tydens samestellingstyd geïdentifiseer kan word.As jy dus baie stringe genereer tydens die uitvoering, sal hulle nie geïnterneer word nie, tensy jy dit self doen deur die interneringsmetode op string te bel.

Interning sal jou net baat as jou snare identies is.Byna identiese snare baat nie by internering nie, so "SOMESTRINGA" en "SOMESTRINGB" sal twee verskillende stringe wees, selfs al is hulle geïnterneer.

As al (of die meeste) van die stringe wat aaneengeskakel word geïnterneer is, kan jou skema jou 'n prestasie-hupstoot gee, aangesien dit moontlik minder geheue kan gebruik en 'n paar groot stringkopieë kan red.

Of dit egter werklik perf verbeter of nie, hang af van die volume data wat jy verwerk, want die verbetering is in konstante faktore, nie in die orde van grootte van die algoritme nie.

Die enigste manier om dit regtig te vertel, is om jou toepassing op beide maniere te laat loop en die resultate te meet.Tensy jy egter onder aansienlike geheuedruk verkeer en 'n manier nodig het om grepe te bespaar, sal ek nie die moeite doen nie en sal net stringbouer gebruik.

A StringBuilder gebruik nie a nie char[] om die data te stoor, gebruik dit 'n interne veranderbare string.Dit beteken dat daar geen ekstra stap is om die finale string te skep soos dit is wanneer jy 'n lys stringe aaneenskakel nie, die StringBuilder gee net die interne stringbuffer terug as 'n gewone string.

Die hertoewysings wat die StringBuilder doen om die kapasiteit te verhoog, beteken dat die data gemiddeld 'n ekstra 1,33 keer gekopieer word.As jy 'n goeie skatting oor die grootte kan verskaf wanneer jy die skep StringBuilder jy kan dit nog verder verminder.

Om egter 'n bietjie perspektief te kry, moet jy kyk wat dit is wat jy probeer optimaliseer.Wat die meeste van die tyd in jou program sal neem, is om die data werklik na skyf te skryf, so selfs al kan jy jou stringhantering optimeer om twee keer so vinnig te wees as om 'n StringBuilder (wat baie onwaarskynlik is), sal die algehele verskil steeds net 'n paar persent wees.

Het jy C++ hiervoor oorweeg?Is daar 'n biblioteekklas wat reeds T/SQL-uitdrukkings bou, verkieslik in C++ geskryf.

Die stadigste ding van snare is malloc.Dit neem 4KB per string op 32-bis platforms.Oorweeg om die aantal stringvoorwerpe wat geskep is, te optimaliseer.

As jy C# moet gebruik, sal ek iets soos hierdie aanbeveel:

string varString1 = tableName;
string varString2 = tableName;

StringBuilder sb1 = new StringBuilder("const expression");
sb1.Append(varString1);

StringBuilder sb2 = new StringBuilder("const expression");
sb2.Append(varString2);

string resultingString = sb1.ToString() + sb2.ToString();

Ek sal selfs so ver gaan as om die rekenaar die beste pad vir objek-instansiasie te laat evalueer met afhanklikheidsinspuitingsraamwerke, as perf SO belangrik is.

Gelisensieer onder: CC-BY-SA met toeskrywing
Nie verbonde aan StackOverflow
scroll top