Avatar billede dsj Nybegynder
02. marts 2004 - 13:25 Der er 45 kommentarer og
1 løsning

Hjælp til register-arkitektur

Jeg arbejder pt. på et highscore-register, som indeholder 50.000 records, og 100.000 skal kunne klares uden problemer, dvs. hurtigt uden det vilde CPU-forbrug, og helst med det lavest mulige RAM-forbrug.

Funktionalitetskravende bringer mig lidt i et dilema, som måske skyldes manglende viden:
1. Et udsnit af registeret skal kunne udtrækkes ud fra start-position og længde (antal records)
2. En given records position skal kunne findes i det samlede register
3. Records skifter ofte position, dog drejer det sig om mindre ændringer
4. Én gang i døgnet resettes registeret, hvilket resulterer i en sandsynlig stor omrokering af alle records.

Records indeholder et minimum af informationer: et ID, et navn og en score - og sorteres ud fra: 1. Højeste score og 2. mindst ID er bedst.

Jeg har lavet en masse forsøg og har bl.a. haft gang i java.util.TreeSet, som er en udemærket og lynhurtig struktur, hvis der skal flyttes rundt, men når når kommer til behandling af positionering, er den elending, den bygger nemlig på chainede entries og har ingen metoder der vedrører index.

Derfor har jeg omskrevet ArrayList til at være en sorteret struktur, der finder index ud fra samme metode, som anvendes i merge-sort (hedder det binær søgemetode?), dvs. at index findes ud fra en tidskompleksitet på O(log(n)), hvor n er antallet af records. Faktisk kræver kun 17 søgninger, at finde en records index blandt 100.000 records. Mit problem er så, at denne struktur er elendig til omrokeringer, da hele det indre array skal kopieres, hvis der indsættes på index 0, eller andre lave positioner. Derfor har jeg opdelt registeret i flere, mindre registre (ArrayList's), og så flytter jeg records'ne rundt mellem delregistrene, hvis det er nødvendigt.

Stadig væk er TreeSet LAAANGT hurtigst til at flytte elementer rundt. Hvis nu jeg kører en test med henholdsvis TreeSet og min egen, sorterede ArrayList, og indlæser 100.000 records fra databasen, tilfældigt sorteret, opbygges 4 TreeSet med 25.000 records i hver på ca. 6 sekunder, mens det tager 20 ArrayList'er med 5.000 records i hver, ca. 90 sekunder.

Nogen der kan hjælpe med ideer, evt. links til noget teoretisk materiale om registre osv. ???
Avatar billede jakoba Nybegynder
02. marts 2004 - 13:30 #1
Det lyder som et job for en database. Har du adgang til SQL ?  Hvis du har vil jeg anbefale det. det er langt 'billigere' end at hive sådan en megafil ind i memory.
Avatar billede dsj Nybegynder
02. marts 2004 - 13:40 #2
Nej, der køres mod MySQL, og den er mildest talt elendig. En record's score er en beregnet værdi, og gemmes ikke som sådan i databasen. Da scoren ofte ændres, vil jeg godt undgå at køre høj kobling mod databasen...
Avatar billede dsj Nybegynder
02. marts 2004 - 13:44 #3
For at kunne gøre det ønskede, skal der anvendes ORDER BY, som MySQL er ekstremt dårlig til at køre, idet den udfører det som en fil-sortering.

I øvrigt er jeg forberedt på et højere RAM-forbrug, bare det ikke eksploderer og stadig går hurtigere.
Avatar billede dsj Nybegynder
02. marts 2004 - 13:46 #4
Det jeg har tænkt er, at lægger jeg arbejdet over til databasen, opbygges så at sige registeret hver gang et udtræk gennemføres, mens et cached og vedligeholdt register bør kræve mindre i længden.
Avatar billede dsj Nybegynder
02. marts 2004 - 13:49 #5
En vigtig information kunne også være, at highscore-registeret faktisk består af 3 registre, som dog alle fungerer ens. Fordelen ved det cachede register er, at en record blot refereres til, men kun eksisterer én gang. Skal der køres mod databasen, vil hvert udtræk med records start:0 og længde:50 kræve 3 udtræk, ét for hvert register: total, uge og dag.
Avatar billede arne_v Ekspert
02. marts 2004 - 15:15 #6
Jeg vil antage at opslag på brugernavn ikke er noget problem, da det bare er
en normal hash map. Hurtig opslag. Records skifter ikke brugernavn.

Det tricky er at få alle 3 hurtige:
  - opslag af nummer X
  - opslag af nummer X-Y
  - flytte nummer X op mellem nummer Z og Z+1

Mit forslag til det vil være et specielt træ med følgende node attributter:
  - parent
  - left child
  - rigth child
  - nodes below
  - object

Det skulle gøre både find og flyt til log(n) operationer.

Jeg tror ikke at brugernavn->placering og flyt af placering begge kan laves
som log(n).
Avatar billede dsj Nybegynder
02. marts 2004 - 16:02 #7
En record identificeres altid ud fra ID, ikke navn, hvorfor placeringen findes ud fra ID. Navnet er kun til for at kunne sammensætte det rigtige output uden at skulle slå navnet op andet steds. Alle Records er registeret i et HashMap ud fra ID (Integer).

Og ja, det svære er at få alle tre funktionaliteter hurtige. TreeSet er monster-hurtig til add/remove, og derfor gennemfører jeg omrokeringer ved at køre remove/add, og hvis jeg vil finde en placering, må jeg tælle ud fra gennemløb af en iterator.

Hvordan skulle man ud fra dit forslag finde placeringen, og Records fra placering x og længde n?
Avatar billede arne_v Ekspert
02. marts 2004 - 16:04 #8
Det er "nodes below" der er tricket !
Avatar billede dsj Nybegynder
02. marts 2004 - 16:05 #9
Tror du at dit foreslåede træ vil kunne klare 100.000 records uden at skulle opdeles i del-registre? - den ville jeg nemlig MEGET gerne slippe for :)
Avatar billede dsj Nybegynder
02. marts 2004 - 16:06 #10
Ahh, nodes below er altså en int ?
Avatar billede dsj Nybegynder
02. marts 2004 - 16:07 #11
Normalt har noder i maps en parent og en child, hvor kommer right og left child ind i billedet ?
Avatar billede arne_v Ekspert
02. marts 2004 - 16:08 #12
Ja - nodes below we en int.

Jeg tænker binært træ - og min erfaring er at selvom man kan nøjes med to
referancer så gør tre koden noget pænere.
Avatar billede arne_v Ekspert
02. marts 2004 - 16:10 #13
Jeg tror at hvis man har nodes below, så kan man finde nummer X ved at gå
ned i træet og dermed log(n) udfra nodes below værdierne.
Avatar billede arne_v Ekspert
02. marts 2004 - 16:12 #14
Og enten virker det med 1 million eller så virker det slet ikke.
Avatar billede dsj Nybegynder
02. marts 2004 - 16:15 #15
Det ka vi li !

Den må jeg lige tykke lidt på - det er pænt længe siden jeg arbejdede med binære træer. Har faktisk aldrig arbejdet med det, kun lært det... nåh, må lige grave lidt papirer frem :)
Avatar billede dsj Nybegynder
02. marts 2004 - 16:46 #16
Lig for at være sikker; Left er mindre, right er større?
Avatar billede dsj Nybegynder
02. marts 2004 - 16:47 #17
Og below er så antallet af left-noder?
Avatar billede arne_v Ekspert
02. marts 2004 - 16:51 #18
parent, left og rigth peger på andre noder

left er "mindre"

rigth er "større"
Avatar billede arne_v Ekspert
02. marts 2004 - 16:52 #19
nodes below er antal noder nedenunder i træet.
Avatar billede arne_v Ekspert
02. marts 2004 - 16:54 #20
Eksempel:

perfekt balanceret 7 node træ:

  4
2  6
1 3 5 7

vil have nodes below værdier:

  6
2  2
0 0 0 0
Avatar billede arne_v Ekspert
02. marts 2004 - 16:54 #21
(man kunne også inkludere noden selv i antallet, men det er ligemeget)
Avatar billede arne_v Ekspert
02. marts 2004 - 16:56 #22
Pointen er at når man står ved en node så kan man checke:
  nodes below left node
  nodes below rigth node
og beslutte sig for om man skal gå til højre eller venstre.
Avatar billede dsj Nybegynder
02. marts 2004 - 16:57 #23
Root ikke den største i denne sammenhæng, altså første parent i træet?
Avatar billede dsj Nybegynder
02. marts 2004 - 16:58 #24
Ohh, så den største værdi er i right leaf?
Avatar billede arne_v Ekspert
02. marts 2004 - 16:59 #25
Tilbage til eksemplet.

Vi skal finde node 5.

node 4
left = 2 + selv = 3
rigth = 2 + selv = 3
5 > left + selv => gem left + selv som gammel & gå til højre

node 6
left = 0 + selv = 1
rigth = 0 + selv = 1
5 = gemt + 1 => vi har fundet den til venstre
Avatar billede arne_v Ekspert
02. marts 2004 - 17:00 #26
root er hvor man starter - i toppen
Avatar billede arne_v Ekspert
02. marts 2004 - 17:00 #27
og dermed den først indsatte node
Avatar billede dsj Nybegynder
02. marts 2004 - 22:52 #28
Er det ikke en sandsynlig ineffektivitet, at root er den først indsatte node? Hvis nu det er den mindste der bliver indsat først, vil det venstre subtræ aldrig få nogen noder, og det højre i stedet få dem alle, eller er det en ubetydelighed?`

Eller er ideen, at man løbende balancerer det samlede træ?
Avatar billede dsj Nybegynder
02. marts 2004 - 23:51 #29
Er et "splay" tree noget jeg kunne overveje i denne situation for at øge performance, og arne, kan du ganske kort forklare hvad et splay tree er og går ud på?
Avatar billede arne_v Ekspert
03. marts 2004 - 08:33 #30
Binære træer skal ikek indsættes i rækkefølge.

Binære træer bliver rimeligt OK hvis de indsættes random.

Perfekte binære træer kan laves udfra et array.
Avatar billede arne_v Ekspert
03. marts 2004 - 08:33 #31
Da jeg ikke aner hvad et "splay" tree er, så ...
Avatar billede dsj Nybegynder
03. marts 2004 - 09:15 #32
Altså hvis man vil have perfekte binære træer, skal de bygges på et array i stedet for noder (med dårligere performance til følge) ?

Arne, du ka vidst godt snart tillade dig at smide et svar...

Det jeg har forstået omkring splay trees er, at de lægger gennem splay-operationer (noget med at rotere bytte om på left og right), placerer de mest benyttede nøgler som root, eller længere mod toppen af træet - for at forbedre performance.
Avatar billede arne_v Ekspert
03. marts 2004 - 09:41 #33
Nej - man laver sit træ af noder.

Men man kan lave et perfekt træ ved:

træ -> array -> træ
Avatar billede arne_v Ekspert
03. marts 2004 - 09:41 #34
træer er log(n) og jeg regner derfor med at de er gode nok standard
Avatar billede arne_v Ekspert
03. marts 2004 - 09:42 #35
og et svar
Avatar billede dsj Nybegynder
03. marts 2004 - 09:57 #36
Jeg testede lige lidt og fandt ud af, at hvis noderne indsættes i træet i rækkefølge, tager det sindsygt lang tid at indsætte bare 20.000 records (ca. 30 sekunder), og ender ofte med en stackOverflowError!!

Derimod tager det under 5 sekunder, hvis records indsættes random.

Måske det ville være en ide jævnligt at balancere træet ?
Avatar billede arne_v Ekspert
03. marts 2004 - 10:00 #37
Udfra din beskrivelse lyder det som at du skulle balancere træet efter
den der en gang i døgnet operation som laver en stor omrokering (punkt 4).
Avatar billede arne_v Ekspert
03. marts 2004 - 10:01 #38
Har du fået opdatering af nodes below og hurtig søg på nummer X til at virke ?
Avatar billede dsj Nybegynder
03. marts 2004 - 10:02 #39
Dette burde egentlig være et problem, splay-trees ville løse op for, idet root jævnligt udskiftes...
Avatar billede dsj Nybegynder
03. marts 2004 - 10:03 #40
Det eneste jeg har testet er at indsætte noder på træet, indlæst fra databasen i enten sortet eller random orden.
Avatar billede dsj Nybegynder
03. marts 2004 - 10:05 #41
Hurtig søgningen på X, skulle den også anvendes ved add(Node)?

Jeg har i øvrigt ikke fået det til at virke endnu med at below opdateres på alle parents, når en node tilføjes træet.
Avatar billede arne_v Ekspert
03. marts 2004 - 10:13 #42
Muligt - jeg kender som sagt ikke splay tree

OK

Ja

Når du fjerner en node skal du tælle nodes below ned på alle forfærdre

Når du tilføjer en node skal du tælle node sbelow op på alle forfædre
Avatar billede dsj Nybegynder
03. marts 2004 - 15:27 #43
Jeg har ikke helt forstået dit eksempel; det bruges til at finde en records placering, eller?

Hvad mener du med "gem sig selv som gammel"?
Avatar billede arne_v Ekspert
03. marts 2004 - 16:10 #44
gem (left + selv) som gammel
.
.
.
...gemt...
Avatar billede arne_v Ekspert
03. marts 2004 - 16:11 #45
Når du går til højre skal du ligge til det gemte.

Men ikke når du går til venstre.
Avatar billede dsj Nybegynder
04. marts 2004 - 14:28 #46
En lille opfølgning:

Det endelige register er blevet et splay-tree, som er en variant binært træ, med det arne forslog med, at alle node holder styr på antallet af underliggende noder 'below', for at optimere søgning efter en værdis index i træet.

Splay-trees virker ved, at hver gang en node tilgås, enten ved add, remove eller contains-operationer, flyttes den tilgåede node som træets root gennem en række højre- og venstre-roteringer. Den opnåede egenskab er, at træets gennemsnitlige dybde halveres og dermed en løbende og effektiv afbalancering.

Resultatet af at køre registeret som ét splay-tree indeholdende alle records er som følger:
- Et splay-tree optager præcis lige så meget hukommelse, som hvis TreeSet blev anvendt i stedet, altså ca. 36% mere end hvis TreeList* anvendes.
- Søgninger efter index ud fra værdi går lige så hurtigt som med TreeList.
- Indlæsning af værdier i splay-tree går lige så hurtigt som med TreeSet, altså meget hurtigere end TreeList.

Som nævnt har det samlede register med TreeSet eller TreeList været opdelt i henholdsvis 4 og 20 delregistre ved 100.000 records.

De gode egenskaber fra TreeSet og TreeList er altså blevet effektivt kombineret med splay-tree.

* TreeList er min egenudviklede ArrayList, der har de egenskaber at være sorteret, og kan finde index ved at søge binært.
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