Avatar billede thread Nybegynder
05. august 2005 - 21:09 Der er 7 kommentarer

En I/O algoritme til en input parser

Jeg er igang med at udvikle et program, der, givet en input fil, læser dennes data ind i nogle records, som hver indeholder variabler, som brugeren kan definere med et unikt navn. Derefter har programmet en indbygget kode-engine, som brugeren kan bruge til nemt at udføre operationer på disse variabler. Når han så har gjort dette, kan han igen gemme dataen til en fil i det format, som han ønsker.
Som eksempel kan vi tage input filen med følgende data:
Thomas;18
Niels;32
Lene;17
Henrik;22

Dette er en basal semikolon-separeret fil, hvor brugeren så definerer, at hver 'record' indeholder alt mellem to linjeskift (dvs. første record indeholder 'Thomas;18', næste 'Niels;32' osv.). Derefter definerer brugeren sine variabel-navne. Han vælger at variablerne er separeret af semikolon, og definerer, at første kolonnes variabler skal have navnet 'Navn' og anden kolonnes variabler navnet 'Alder'. Dvs. hver record har 2 variabler: 'Navn' og 'Alder', hvor fx den første record har Navn="Thomas" og Alder=18 osv.
Programmet vil læse al data fra denne fil ind i hukommelsen så brugeren kan manipulere med disse variabler baseret på betingelser osv. vha. programmets indbyggede kode-engine. Som eksempel vil brugere udføre:
"Vælg alle 'Navn'-variabler, hvor den tilhørende 'Alder' er mindre eller lig 19 og sæt disses værdi lig "Teenager".

Dvs. programmet går derefter igennem alle variabler og tjekker om 'Alder' er mindre eller lig 19, og hvis den er, sættes 'Navn' til "Teenager".

Dette var en kort beskrivelse af, hvad programmet kan gøre - og her kommer så problemet: Hvordan skal man lave en begrænsning på, hvor meget data der må læses ind i hukommelsen på en gang. Fx hvis man maksimalt vil have, at programmet bruger 128mb hukommelse og input-filen fylder fx 512mb?
Først kan vi gå ud fra, at brugeren bare vil udføre sekventielle operationer magen til den beskrevet ovenfor. I dette tilfælde læser den jo bare filen fra begyndelsen og manipulerer kun med variabler inde for samme record - dvs. ingen problemer: den kan nemlig blot læse 128mb, udføre operationerne, læse de næste 128mb, udføre operationer på dem osv.
Men når man kommer til mere komplicerede operationer defineret af brugeren, bliver det sværere. Forestil dig fx følgende tilfælde:
"Vælg den sidste record i filen og sæt dens 'Navn'-værdi lig den første record's 'Navn'-værdi."
I dette tilfælde, hvis man læser 128mb af gangen, vil den første record jo ikke være læst ind i hukommelsen, når den bliver requested af den sidste variabel. Dvs. interaktioner mellem records, der ikke ligger i samme "hukommelses-blok" vil ikke kunne virke på denne måde.

Det, jeg har brug for, er nogle forslag til, hvordan man lettest kunne implementere en algoritme, der sørgede for, at man kunne sætte en maksimalt hukommelses-brug og stadig have samme funktionalitet af kode-enginen, uden at det skaber for mange læse-operationer fra input filen, da jeg jo at interesseret i, programmets udførelse er så optimeret som muligt.

Håber, det ikke var en for stor mundfuld.
På forhånd tak
Avatar billede bertelbrander Praktikant
06. august 2005 - 00:24 #1
Hvis vi starter med kun læse: Når programmet starter læser du hele filen og gemmer en pointer til starten af hver record i filen.
Så laver du en cache, dvs. en liste med de seneste xxx records, og information om hvornår de har været brugt.
Når du har brug for en record, checker du først i cachen, hvis den er der, opdaterer du dens tidsstempel og genbruger den fra cachen. Hvis den ikke er i cachen, checker du om du har hukommelse nok til at læse en ny record, hvis ikke sletter du fra cachen indtil du har plads til en ny record og læser en record fra fil, og putter den i cachen.

Med hensyn til at skrive: Hvis alle records fylder lige meget kan du skrive direkte til filen hver gang du opdaterer en record. Hvis dette ikke er tilfældet, må du gemme et antal records i en skrive buffer, og når du har nogle stykker skriver du hele filen. Eller du kan gemme records i en midlertidig fil, og kun genskrive hele den rigtige fil med mellemrum, denne metode er mere effiktiv, men mere besværlig.

Den store test er at sortere hele databasen.
Avatar billede thread Nybegynder
06. august 2005 - 02:27 #2
Mange tak for svaret - var også i de baner, jeg selv tænkte. Simpelthen at gemme position (og måske længde) for hver record, så den hurtigt kan læses uafhængigt af de andre. Men for at kunne læse hver record individuelt, kræver det jo også, at positionerne på forhånd at læst ind i hukommelsen (og at der faktisk er plads til al den information - da det jo er noget i nærheden af 4 bytes der er krævet pr. record, til positionen). Hvis disse positioner skal være læst ind, skal filen jo i hvert fald læses igennem én gang - men hvis det er en meget basal sekventiel løkke, vil det kræve at læse filen 2 gange, hvilket ikke rigtigt er nødvendigt. Måske kunne man lave et request-system, så det først er når en record bliver requested til at blive læst, at den læser frem til dennes position i filen? Dette ville nemlig løse den enkelte sekventielle løkke, da det hele tiden vil være den næste record, der bliver requested, og derfor kun vil blive læst én gang.
Jeg vil prøve at begynde at implementere med en cache og se, hvordan det går - og så kommer der nok en masse flere spørgsmål herfra.

Mange tak
Avatar billede thread Nybegynder
06. august 2005 - 02:33 #3
Faktisk tænkte jeg også lidt på, hvis man forestiller sig at brugeren har kodet flg. 2 operationer:
1) Sæt alle 'Navn'-variabler til "xxx".
2) Læg 5 til alle 'Alder'-variabler.

Dette ville generere flg. pseudo-kode:
VariableCollection vars;
vars = SelectVariables("Navn");
vars.Set("xxx");
vars = SelectVariables("Alder");
vars.Add(5);

Men hvis jeg lader disse operationer udføres med det samme, vil det jo generere 2 efterfølgende løkker der går igennem hele filen - igen unødvendigt, da det kan gøres i én løkke. Så jeg skal finde på en metode, der kan optimere dette til at køre i en løkke - for når vi snakker store filer, hvor jeg ikke kan gemme det hele i hukommelsen, bliver det megen forøgelse i den tid, det vil tage, hvis den skal læse filen 2 gange i stedet for en.
Men her skal man jo også tage hensyn til, at det ikke altid vil kunne optimeres til én løkke, hvis variablerne skal bruge data fra hinanden osv.
Avatar billede bertelbrander Praktikant
06. august 2005 - 12:49 #4
Hvis ikke du har plads til en pointer til hver records position i memory bliver det meget besværligt og langsomt at finde en record, da du jo så skal søge hele filen hver gang. Jeg forstår ikke den med at skulle læse filen to gange og "meget basal sekventiel løkke". Jeg går ud fra at arbejdet med at læse hele filen igennem én gang ved start for at finde alle offsets (record start positioin) er meget lille i forhold til at skulle søge efter hver record hver gang man skal bruge den.

Man kunne også lade hver record have samme størrelse (vil kræve mere plads på disk),så kan du altid udregne position på en record.

Alt dette går fra at man har et index til en record. Når man skal søge i databasen er man vist nødt til at søge hele filen igennem.
Avatar billede thread Nybegynder
06. august 2005 - 14:22 #5
Jeg mener bare: brugeren kan, igennem sin egen kode, skrive, hvordan han vil manipulere med variablerne inden filen bliver gemt. Hvis han skriver to kommandoer som dem jeg skrev tidligere:
1) Sæt alle 'Navn'-variabler til "xxx".
2) Læg 5 til alle 'Alder'-variabler.
Da koden også bliver gennemgået sekventielt vil den først skulle gå igennem alle 'Navn'-variabler (dvs. læse hele filen igennem) og derefter køre den samme løkke for at lægge 5 til 'Alder'. Dvs. to løkker, der kræver meget, især, hvis filen jo er for stor til hukommelsen - men der er jo faktisk kun brug for én løkke, så jeg må kunne lave et eller andet der kan få dem til at køre i samme.

Og hvis du nu forestiller dig, at det kun er den første kommando brugeren vil udføre i en 512mb fil, hvor maksimalt brug er 128mb hukommelse, dvs.:
1) Sæt alle 'Navn'-variabler til "xxx".
Iflg. den hukommelses-algoritme vi lige har diskuteret, skulle programmet da:
1) Læse hele filen igennem og gemme alle record-offsets.
2) Gå hele filen igennem (igen) for at sætte alle 'Navn'-variabler.
Dette kræver 2x at gå igennem filen - og kun én gang er nødvendig.

Ved godt, det er lidt rodet, det, jeg snakker om - men det er bare de overvejelser jeg har mht. styring af hukommelses-bruget.
Avatar billede thread Nybegynder
06. august 2005 - 14:23 #6
Er måske bare lidt diskussion, jeg er ude efter end et faktisk svar. Er altid rart at høre flere sider til samme sag, og høre, om andre har nogle forbedrelser eller forslag.

Skal dog selvfølgelig nok give point for den rare support, der bliver ydet ;)
Avatar billede bertelbrander Praktikant
06. august 2005 - 15:05 #7
Man kan naturligvis godt løse dit eksempel med kun ét gennemløb, men det bliver let meget indviklet.

Det ville være lettere at få brugeren til at lave optimeringen:
foreach (x in collection):
  var = x.SelectVariable("Navn");
  var.Set("xxx");
  var = x.SelectVariable("Alder");
  var.Add(5);
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Kurser inden for grundlæggende programmering

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester