# Analys av Facett
Datum: 2026-10-04
Granskad revision: `33f16430`
## Bedömning
Facett är tydligt optimerat på renderingssidan, men det finns konkreta förbättringar att göra i datahantering, minnesåtgång och tester. Grunden är bra, men vissa funktioner kan bli långsamma när datamängderna växer.
## Det som redan är bra
- Tabellerna virtualiserar rader och kolumner: bara den synliga delen ritas.
- Flera grafvyer behåller geometrin på GPU:n och uppdaterar främst kameran under interaktion.
- Det finns gemensamma komponentkontrakt, separat tillståndshantering och omfattande testinfrastruktur.
- Tunga komponenter som dokument-, video- och 3D-modellvisning är valbara beroenden.
Projektets befintliga mätningar visar omkring 60 FPS för flera grafvyer med 100 000 noder på angiven hårdvara. Detta är tidigare mätningar, inte nya resultat från denna granskning, och gäller de konfigurationer och arbetslaster som rapporterna anger.
## Prioriterade förbättringar
### 1. Cacha tabellens summeringar
**Prioritet: hög. Kodfynd verifierat; tidskostnaden har inte mätts i denna granskning.**
När summaraden är påslagen går `column_sum()` igenom alla rader i den aktuella filtrerade radordningen. `draw_total()` anropar funktionen vid varje bildruta för respektive visad numerisk kolumn.
Det innebär att arbetet växer med antalet filtrerade rader och visade numeriska kolumner, även om själva tabellkroppen bara ritar synliga celler. Stora tabeller förlorar därmed en viktig del av vinsten med virtualiseringen.
**Förslag:** cacha summorna och uppdatera dem när data eller filter ändras. Säkerställ att redigering och inklistring också invaliderar relevanta summor.
Kod:
- [Grid](facett-grid/src/lib.rs): `column_sum()`, rad 703; summaradens rendering, rad 1166; `draw_total()`, rad 1551.
### 2. Undvik fullständig omräkning vid varje filterändring
**Prioritet: hög. Kodfynd verifierat; faktisk användarlatens behöver mätas.**
`Msg::SetFilter` anropar `rebuild_order()` synkront. Funktionen bygger först radordningen från sorteringsinställningen över samtliga källrader och filtrerar sedan resultatet. När sortering är aktiv görs alltså sorteringen om även om bara söktexten har ändrats.
Sökningen använder `cell_raw(...).to_ascii_lowercase()`, vilket skapar strängar och gemenkopior när cellerna undersöks. Filterresultatets radindex kopieras dessutom till `filtered` samtidigt som `order` behålls.
**Förslag:**
- Behåll sorteringsresultatet mellan filterändringar.
- Minska strängallokeringarna i sökningen.
- Kör stora sökningar i bakgrunden med avbrytning eller generationskontroll så att inaktuella resultat inte ersätter nyare resultat.
- Undvik dubbla indexvektorer om samma information kan representeras enklare.
Kod:
- [Grid](facett-grid/src/lib.rs): `rebuild_order()`, rad 655; `Msg::SetFilter`, rad 772.
- [Datamodell](facett-grid/src/data.rs): `cell_raw()`, rad 235.
- [Sortering](facett-grid/src/sort.rs): `sorted_order()`, rad 99.
### 3. Minska dubbel lagring vid Arrow-import
**Prioritet: hög vid stora dataset. Kodfynd verifierat; minnesåtgången har inte profilerats.**
Arrow-importen materialiserar kolumnerna till egna Rust-vektorer. Textvärden kopieras till separata strängar. Samtidigt behåller griden originalets `RecordBatch` genom delade Arrow-buffertar.
Att delningen av originalets buffertar och den typade kolumnkopieringen är zero-copy innebär därför inte att hela importen är zero-copy. De materialiserade kolumnerna tillkommer i minnet.
**Förslag:** läs direkt från typade Arrow-kolumner där det passar och lagra redigeringar separat. Mät särskilt texttunga tabeller och många kolumner innan representationen ändras.
Kod:
- [Grid-konstruktorer](facett-grid/src/lib.rs): `from_record_batch()` och `from_record_batch_arc()`, från rad 397.
- [Arrow-adapter](facett-grid/src/arrow.rs): `grid_data_from_batch()` och `column_from_array()`.
### 4. Mät kompletta användarinteraktioner
**Prioritet: medel. Testlucka verifierad i det granskade prestandatestet.**
Gridens prestandatest mäter framför allt beräkningen av vilka rader som är synliga. Det visar att fönsterberäkningen inte växer med hela datasetet, men styrker i sig inte ett generellt påstående om 60 FPS för kompletta bildrutor.
Testfilen kontrollerar också importtid och delning av Arrow-kolumner, men fångar inte exempelvis kostnaden för summering vid varje bildruta eller filterrespons under inmatning.
**Förslag:** mät hela bildrutor, scrollning och filterrespons med summor, textkolumner och realistiska datamängder. Följ minnesåtgång och långsamma bildrutor, inte enbart genomsnittlig FPS.
Källa:
- [Gridens prestandatest](facett-grid/tests/perf.rs), särskilt `building_the_window_is_o_visible_not_o_total()`, rad 39.
### 5. Utvärdera synlighetsfiltrering och detaljnivå i Flow
**Prioritet: medel för större grafer. Optimeringsmöjlighet; vinsten är inte uppmätt.**
Flow-renderaren skickar samtliga kanter till GPU-renderingen med 96 vertexpositioner per kant varje bildruta. Att geometrin ligger kvar på GPU:n sparar uppladdningar, men eliminerar inte arbetet med att rita den.
**Förslag:** utvärdera synlighetsfiltrering och detaljnivå efter zoom. Mät vinsten på representativa stora grafer innan implementation väljs.
Källa:
- [Flow-renderaren](facett-flow/src/gpu.rs), ritkommandon från rad 280.
## Tre reproducerade korrekthetsproblem
De befintliga testerna i `data.rs`, `sort.rs` och `arrow.rs` kördes tillsammans med tre kompletterande gränsfall direkt mot projektets källfiler, i en separat testharness med befintliga kompilerade beroenden.
**Resultat: 15 befintliga tester passerade och de tre kompletterande gränsfallen misslyckades.** Detta är en riktad kontroll av tre moduler, inte resultatet för hela projektets testsvit.
### Stora UInt64-värden ändras vid import
Arrow-adaptern begränsar `UInt64` till `i64::MAX`. Därmed ändras datavärdet som används för visning och sortering.
Reproducerat exempel:
```text
Indata: 18446744073709551615
Importerad text: 9223372036854775807
```
**Förslag:** bevara osignerade heltal i en egen kolumntyp eller annan förlustfri representation.
Källa: [Arrow-konvertering](facett-grid/src/arrow.rs), från rad 56.
### Minsta i64-värdet får dubbla minustecken
`group_int()` negerar negativa värden, men absolutbeloppet av `i64::MIN` kan inte representeras som `i64`.
Reproducerat i optimerad körning:
```text
Förväntat: -9,223,372,036,854,775,808
Faktiskt: --9,223,372,036,854,775,808
```
**Förslag:** formatera absolutbeloppet som ett osignerat värde och hantera tecknet separat.
Källa: [Talformatering](facett-grid/src/data.rs), rad 127.
### Nullvärden hamnar först vid fallande sortering
Kodens dokumenterade avsikt är att nullvärden alltid ska hamna sist. Den fallande sorteringen reverserar dock även nulljämförelsen.
Reproducerat exempel:
```text
Indata: [3, null, 1]
Förväntat: [3, 1, null]
Faktiskt: [null, 3, 1]
```
**Förslag:** hantera nullplaceringen separat från riktningen för jämförelsen av vanliga värden.
Källa: [Sortering](facett-grid/src/sort.rs), rad 108.
## Rekommenderad arbetsordning
1. Rätta korrekthetsproblemen och lägg till regressionstester.
2. Cacha summeringen.
3. Optimera filtreringen och återanvänd sorteringsresultat.
4. Mät minnesåtgång, kompletta bildrutor och användarinteraktioner.
5. Prioritera därefter större ändringar i Arrow-lagring och GPU-rendering utifrån mätresultaten.
## Omfattning och begränsningar
- Inga projektfiler ändrades under analysen.
- Full testsvit och nya GPU-mätningar ingick inte.
- Kodfynd om onödigt arbete är verifierade, men deras faktiska tids- och minneskostnad behöver mätas.
- De citerade FPS-resultaten är projektets tidigare dokumenterade mätningar för specifika konfigurationer.