{"id":33793,"date":"2019-10-31T21:54:42","date_gmt":"2019-10-31T18:54:42","guid":{"rendered":"https:\/\/prohoster.info\/blog\/bitmap-indeksy-v-go-poisk-na-dikoj-skorosti\/"},"modified":"2019-10-31T21:54:42","modified_gmt":"2019-10-31T18:54:42","slug":"bitmap-indeksy-v-go-poisk-na-dikoj-skorosti","status":"publish","type":"post","link":"https:\/\/prohoster.info\/da\/blog\/administrirovanie\/bitmap-indeksy-v-go-poisk-na-dikoj-skorosti","title":{"rendered":"Bitmap-indekser i Go: s\u00f8gning med vild hastighed","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p><img decoding=\"async\" alt=\"Bitmap-indekser i Go: s\u00f8gning med vild hastighed\" src=\"\/wp-content\/uploads\/2019\/05\/86ef928e6741022b2c0e5885a031408a.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<\/p>\n<h2>Indledning<\/h2>\n<p>\nJeg pr\u00e6senterede dette foredrag p\u00e5 engelsk ved GopherCon Rusland 2019 i Moskva og p\u00e5 russisk ved m\u00f8det i Nizhny Novgorod. Det handler om bitmap-indekset \u2014 mindre almindeligt end B-tree, men ikke mindre interessant. Jeg deler <noindex><a rel=\"nofollow\" href=\"https:\/\/youtu.be\/WvlUH6MjUuI?list=PL3xVZC4USRNSO_kb2lh_J_no6C-KJ7Phg\">optagelsen<\/a><\/noindex> af pr\u00e6sentationen p\u00e5 konferencen p\u00e5 engelsk og den skriftlige transkription p\u00e5 russisk.<\/p>\n<p>Vi vil se p\u00e5, hvordan bitmap-indekset fungerer, hvorn\u00e5r det er bedre, hvorn\u00e5r det er v\u00e6rre end andre indekser, og i hvilke tilf\u00e6lde det er betydeligt hurtigere end dem; vi vil se, hvilke popul\u00e6re DBMS'er der allerede har bitmap-indekser; vi vil fors\u00f8ge at skrive vores eget i Go. Og som dessert vil vi bruge eksisterende biblioteker til at lave vores egen superhurtige specialiserede database.<\/p>\n<p>Jeg h\u00e5ber meget, at mit arbejde vil v\u00e6re nyttigt og interessant for jer. Lad os komme i gang!<br \/>\n<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h2>Introduktion<\/h2>\n<p>\n<center><div class=\"youtube-placeholder\" data-id=\"WvlUH6MjUuI\" onclick=\"loadVideo(this)\">\r\n        <img decoding=\"async\" src=\"https:\/\/img.youtube.com\/vi\/WvlUH6MjUuI\/hqdefault.jpg\" alt=\"Afspil video\" loading=\"lazy\" width=\"480\" height=\"360\" style=\"width:100%;height:auto;\">\r\n        <div class=\"play-button\"><\/div>\r\n    <\/div><\/center><br \/>\n<noindex><a rel=\"nofollow\" href=\"http:\/\/bit.ly\/bitmapindexes\">http:\/\/bit.ly\/bitmapindexes<\/a><\/noindex><br \/>\n<noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/mkevac\/gopherconrussia2019\">https:\/\/github.com\/mkevac\/gopherconrussia2019<\/a><\/noindex><\/p>\n<p>Hej alle! Klokken er seks om aftenen, og vi er alle super tr\u00e6tte. Det er et fantastisk tidspunkt at tale om den kedelige teori om databaseindekser, ikke? Bare rolig, jeg har et par linjer kode her og der. \ud83d\ude42<\/p>\n<p>Hvis vi skal v\u00e6re seri\u00f8se, er foredraget fyldt med information, og vi har ikke s\u00e5 meget tid. S\u00e5 lad os starte.<br \/>\n<img decoding=\"async\" alt=\"Bitmap-indekser i Go: s\u00f8gning med vild hastighed\" src=\"\/wp-content\/uploads\/2019\/05\/e778e13727700f0335a4b5558a0d8db3.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nI dag vil jeg tale om f\u00f8lgende:<\/p>\n<ul>\n<li>hvad indekser er;\n<\/li>\n<li>hvad bitmap-indekset er;\n<\/li>\n<li>hvor det bruges, og hvor det IKKE bruges, og hvorfor;\n<\/li>\n<li>en simpel implementering i Go og lidt kamp med compileren;\n<\/li>\n<li>en lidt mindre simpel, men meget mere performant implementering i Go-assembler;\n<\/li>\n<li>de 'problemer', bitmap-indekser har;\n<\/li>\n<li>eksisterende implementeringer.\n<\/li>\n<\/ul>\n<h2>S\u00e5 hvad er indekser?<\/h2>\n<p>\n<img decoding=\"async\" alt=\"Bitmap-indekser i Go: s\u00f8gning med vild hastighed\" src=\"\/wp-content\/uploads\/2019\/05\/b80e5b990c44814afe9150a9a82351fd.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nEt indeks er en separat datastruktur, som vi holder og opdaterer udover de prim\u00e6re data. Det bruges til at fremskynde s\u00f8gning. Uden indekser ville s\u00f8gning kr\u00e6ve en fuld gennemgang af dataene (en proces kaldet full scan), og denne proces har en line\u00e6r algoritmisk kompleksitet. Men databaser indeholder typisk enorme m\u00e6ngder data, og line\u00e6r kompleksitet er for langsomt. Ideelt set \u00f8nsker vi at f\u00e5 logaritmisk eller konstant.<\/p>\n<p>Det er et k\u00e6mpe komplekst emne, fyldt med nuancer og kompromiser, men efter at have set p\u00e5 \u00e5rtiers udvikling og forskning inden for forskellige databaser, er jeg klar til at p\u00e5st\u00e5, at der kun er et par bredt anvendte tilgange til at oprette databaser indekser.<\/p>\n<p><img decoding=\"async\" alt=\"Bitmap-indekser i Go: s\u00f8gning med vild hastighed\" src=\"\/wp-content\/uploads\/2019\/05\/08a74dbb365035cd99bc94d72644a1b7.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nDen f\u00f8rste tilgang er baseret p\u00e5 hierarkisk reduktion af s\u00f8geomr\u00e5det, hvor vi opdeler s\u00f8geomr\u00e5det i mindre dele.<\/p>\n<p>Vi g\u00f8r normalt dette ved hj\u00e6lp af forskellige typer tr\u00e6er. Et eksempel kunne v\u00e6re en stor kasse med materialer i dit skab, der indeholder mindre kasser med materialer delt op efter forskellige emner. Hvis du har brug for materialer, vil du helt sikkert s\u00f8ge i kassen m\u00e6rket 'Materialer' og ikke i den, der hedder 'Sm\u00e5kager', ikke sandt?<\/p>\n<p><img decoding=\"async\" alt=\"Bitmap-indekser i Go: s\u00f8gning med vild hastighed\" src=\"\/wp-content\/uploads\/2019\/05\/423fac47c748980b18af434644af4dae.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nDen anden tilgang er at fremh\u00e6ve det n\u00f8dvendige element eller gruppe af elementer med det samme. Vi g\u00f8r dette i hash maps eller omvendte indekser. Brugen af hash maps ligner meget det tidligere eksempel, men i stedet for en kasse med kasser har du i dit skab en bunke sm\u00e5 kasser med de endelige genstande.<\/p>\n<p><img decoding=\"async\" alt=\"Bitmap-indekser i Go: s\u00f8gning med vild hastighed\" src=\"\/wp-content\/uploads\/2019\/05\/088fed0a2a0feea4a23edbf0ca654805.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nDen tredje tilgang er at fjerne behovet for s\u00f8gning. Dette g\u00f8r vi ved hj\u00e6lp af Bloom-filtre eller cuckoo-filtre. De f\u00f8rste giver et svar \u00f8jeblikkeligt og fjerner behovet for at foretage en s\u00f8gning.<\/p>\n<p><img decoding=\"async\" alt=\"Bitmap-indekser i Go: s\u00f8gning med vild hastighed\" src=\"\/wp-content\/uploads\/2019\/05\/493bc4f20baacc0a5dc0faf15cc46285.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nDen sidste tilgang er at udnytte al kapacitet, som moderne hardware tilbyder. Det er netop det, vi g\u00f8r med bitmap-indekser. Ja, n\u00e5r vi bruger dem, skal vi nogle gange gennemg\u00e5 hele indekset, men vi g\u00f8r det super effektivt.<\/p>\n<p>Som jeg allerede har n\u00e6vnt, er emnet for DB-indekser omfattende og fuld af kompromiser. Det betyder, at vi nogle gange kan bruge flere tilgange samtidigt: hvis vi skal fremskynde s\u00f8gningen yderligere, eller hvis vi skal d\u00e6kke alle mulige s\u00f8getyper.<\/p>\n<p>I dag vil jeg tale om den mindst kendte tilgang af de n\u00e6vnte - bitmap-indekser.<\/p>\n<h2>Hvem er jeg til at tale om dette emne?<\/h2>\n<p>\n<img decoding=\"async\" alt=\"Bitmap-indekser i Go: s\u00f8gning med vild hastighed\" src=\"\/wp-content\/uploads\/2019\/05\/42a9d17a507c392bc202254a92dfaf41.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nJeg arbejder som teamleder hos Badoo (m\u00e5ske kender du bedre et af vores andre produkter - Bumble). Vi har over 400 millioner brugere verden over og mange funktioner, der hj\u00e6lper med at finde den bedste partner til dem. Det g\u00f8r vi ved hj\u00e6lp af skr\u00e6ddersyede services, der ogs\u00e5 benytter bitmap-indekser.<\/p>\n<h2>Hvad er s\u00e5 et bitmap-indeks?<\/h2>\n<p>\n<img decoding=\"async\" alt=\"Bitmap-indekser i Go: s\u00f8gning med vild hastighed\" src=\"\/wp-content\/uploads\/2019\/05\/14f20f3697a20c02f6b3510dc7f0ae4d.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nBitmap-indekser, som navnet antyder, bruger bitmapper eller bitm\u00e6ngder til at implementere en s\u00f8geindeks. Set fra oven best\u00e5r denne indeks af et eller flere s\u00e5danne bitmapper, der repr\u00e6senterer forskellige enheder (som mennesker) og deres egenskaber eller parametre (alder, \u00f8jenfarve osv.), samt en algoritme, der bruger bitoperationer (AND, OR, NOT) for at besvare en s\u00f8geforesp\u00f8rgsel.<br \/>\n<img decoding=\"async\" alt=\"Bitmap-indekser i Go: s\u00f8gning med vild hastighed\" src=\"\/wp-content\/uploads\/2019\/05\/f815720330a51b1f0798e45d23160d3b.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nDet siges, at bitmap-indekser er bedst egnet og meget effektive, n\u00e5r der er s\u00f8gninger, der kombinerer foresp\u00f8rgsler p\u00e5 mange kolonner med lav kardinalitet (forestil dig '\u00f8jenfarve' eller 'familieforhold' mod noget som 'afstand fra byens centrum'). Men senere vil jeg vise, at de ogs\u00e5 fungerer fremragende med kolonner med h\u00f8j kardinalitet.<\/p>\n<p>Lad os overveje et simpelt eksempel p\u00e5 en bitmap-indeks.<br \/>\n<img decoding=\"async\" alt=\"Bitmap-indekser i Go: s\u00f8gning med vild hastighed\" src=\"\/wp-content\/uploads\/2019\/05\/33fe23476e0931c10345d7175b83d68b.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nForestil dig, at vi har en liste over restauranter i Moskva med bin\u00e6re egenskaber som disse:<\/p>\n<ul>\n<li>n\u00e6r metro (near metro);\n<\/li>\n<li>har privat parkering (has private parking);\n<\/li>\n<li>har terrasse (has terrace);\n<\/li>\n<li>accepterer reservationer (accepts reservations);\n<\/li>\n<li>veganervenlig (vegan friendly);\n<\/li>\n<li>dyr (expensive).\n<\/li>\n<\/ul>\n<p>\n<img decoding=\"async\" alt=\"Bitmap-indekser i Go: s\u00f8gning med vild hastighed\" src=\"\/wp-content\/uploads\/2019\/05\/fcbad539ea12f79a9d06966ce308637e.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nLad os give hver restaurant et r\u00e6kkenummer, der begynder fra 0, og allokere hukommelse til 6 bitmapper (\u00e9n for hver egenskab). Derefter fylder vi disse bitmapper afh\u00e6ngigt af, om restauranten har denne egenskab eller ej. Hvis restaurant 4 har en terrasse, vil bit nr. 4 i bitmappen 'har terrasse' blive sat til 1 (hvis terrassen ikke er der, s\u00e5 til 0).<br \/>\n<img decoding=\"async\" alt=\"Bitmap-indekser i Go: s\u00f8gning med vild hastighed\" src=\"\/wp-content\/uploads\/2019\/05\/3709c426617d4364f392ee6b68f92b00.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nNu har vi den enkleste bitmap-indeks, man kan f\u00e5, og vi kan bruge den til at besvare foresp\u00f8rgsler som:<\/p>\n<ul>\n<li>\u00abVis mig restauranter, der er velegnede til veganere\u00bb;\n<\/li>\n<li>\u00abVis mig billige restauranter med terrasse, hvor der kan reserveres bord\u00bb.\n<\/li>\n<\/ul>\n<p><img decoding=\"async\" alt=\"Bitmap-indekser i Go: s\u00f8gning med vild hastighed\" src=\"\/wp-content\/uploads\/2019\/05\/90acb0f890686bd52c3db1fc667f0b0b.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<img decoding=\"async\" alt=\"Bitmap-indekser i Go: s\u00f8gning med vild hastighed\" src=\"\/wp-content\/uploads\/2019\/05\/0fd0b7fe9f7b7039022ad5c79fe873bc.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nHvordan? Lad os se. Den f\u00f8rste foresp\u00f8rgsel er meget enkel. Alt, hvad vi beh\u00f8ver, er at tage bitmappen 'veganervenlig' og konvertere den til en liste over restauranter, hvis bits er sat.<br \/>\n<img decoding=\"async\" alt=\"Bitmap-indekser i Go: s\u00f8gning med vild hastighed\" src=\"\/wp-content\/uploads\/2019\/05\/ef4a5cfd658ff4ef8bc0c638c4522d11.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<img decoding=\"async\" alt=\"Bitmap-indekser i Go: s\u00f8gning med vild hastighed\" src=\"\/wp-content\/uploads\/2019\/05\/9cc175bae55c16018fdf5ff95517a61a.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nDen anden foresp\u00f8rgsel er lidt mere kompliceret. Vi skal bruge bitoperationen NOT p\u00e5 bitmap'en \"dyr\", for at f\u00e5 en liste over billige restauranter, derefter AND'e den med bitmap'en \"kan reserveres\" og AND'e resultatet med bitmap'en \"har terrasse\". Den resulterende bitmap vil indeholde en liste over steder, der opfylder alle vores kriterier. I dette eksempel er det kun restauranten \"Ungdom\".<br \/>\n<img decoding=\"async\" alt=\"Bitmap-indekser i Go: s\u00f8gning med vild hastighed\" src=\"\/wp-content\/uploads\/2019\/05\/f1cdda0cbf7f15278553899cf876c17e.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<img decoding=\"async\" alt=\"Bitmap-indekser i Go: s\u00f8gning med vild hastighed\" src=\"\/wp-content\/uploads\/2019\/05\/126b50e0f622b6e36c461cd74e708c38.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nDer er meget teori her, men bare rolig, vi vil snart se koden.<\/p>\n<h2>Hvor bruges bitmap-indekser?<\/h2>\n<p>\n<img decoding=\"async\" alt=\"Bitmap-indekser i Go: s\u00f8gning med vild hastighed\" src=\"\/wp-content\/uploads\/2019\/05\/406132236c71a4f66ae79957b6e633b3.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nHvis du \"googler\" bitmap-indekser, vil 90% af svarene v\u00e6re p\u00e5 en eller anden m\u00e5de relateret til Oracle DB. Men andre DBMS m\u00e5 ogs\u00e5 st\u00f8tte s\u00e5dan en cool ting, ikke sandt? Ikke helt. <\/p>\n<p>Lad os tage et kig p\u00e5 listen over hovedmist\u00e6nkte.<br \/>\n<img decoding=\"async\" alt=\"Bitmap-indekser i Go: s\u00f8gning med vild hastighed\" src=\"\/wp-content\/uploads\/2019\/05\/d18d66451a8a26b0ddf121f8ec0204cd.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nMySQL underst\u00f8tter stadig ikke bitmap-indekser, men der er et forslag om at tilf\u00f8je denne mulighed (<noindex><a rel=\"nofollow\" href=\"https:\/\/dev.mysql.com\/worklog\/task\/?id=1524\">https:\/\/dev.mysql.com\/worklog\/task\/?id=1524<\/a><\/noindex>).<\/p>\n<p>PostgreSQL underst\u00f8tter ikke bitmap-indekser, men bruger enkle bitmaps og bitoperationer til at kombinere s\u00f8geresultater fra flere andre indekser.<\/p>\n<p>Tarantool har bitset-indekser; det underst\u00f8tter simpel s\u00f8gning p\u00e5 dem.<\/p>\n<p>Redis har enkle bitfelter<noindex><a rel=\"nofollow\" href=\"https:\/\/redis.io\/commands\/bitfield\"> (https:\/\/redis.io\/commands\/bitfield<\/a><\/noindex>) uden mulighed for at s\u00f8ge i dem.<\/p>\n<p>MongoDB underst\u00f8tter stadig ikke bitmap-indekser, men der er ogs\u00e5 et forslag om at tilf\u00f8je denne mulighed. <noindex><a rel=\"nofollow\" href=\"https:\/\/jira.mongodb.org\/browse\/SERVER-1723\">https:\/\/jira.mongodb.org\/browse\/SERVER-1723<\/a><\/noindex><\/p>\n<p>Elasticsearch bruger bitmaps internt<noindex><a rel=\"nofollow\" href=\"https:\/\/www.elastic.co\/blog\/frame-of-reference-and-roaring-bitmaps\"> (https:\/\/www.elastic.co\/blog\/frame-of-reference-and-roaring-bitmaps<\/a><\/noindex>).<\/p>\n<p>\n<img decoding=\"async\" alt=\"Bitmap-indekser i Go: s\u00f8gning med vild hastighed\" src=\"\/wp-content\/uploads\/2019\/05\/232d335963603d6e6dec98839fc9f486.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<\/p>\n<ul>\n<li>Men i vores hus er der en ny nabo: Pilosa. Det er en ny ikke-relational database skrevet i Go. Den indeholder kun bitmap-indekser og baserer alt p\u00e5 dem. Vi vil tale om den lidt senere.\n<\/li>\n<\/ul>\n<h2>Implementering i Go<\/h2>\n<p>\nMen hvorfor bruges bitmap-indekser s\u00e5 sj\u00e6ldent? F\u00f8r jeg svarer p\u00e5 dette sp\u00f8rgsm\u00e5l, vil jeg gerne demonstrere en meget simpel implementering af et bitmap-index i Go.<br \/>\n<img decoding=\"async\" alt=\"Bitmap-indekser i Go: s\u00f8gning med vild hastighed\" src=\"\/wp-content\/uploads\/2019\/05\/ae7f4c1a4a740fe709b05dfaee27ac9b.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nBitmaps er i bund og grund blot repr\u00e6senteret som datablokke. I Go lad os bruge byte-slices til dette.<\/p>\n<p>Vi har en bitmap for en restaurantkarakteristik, og hver bit i bitmap'en angiver, om den p\u00e5g\u00e6ldende restaurant har denne egenskab eller ej.<br \/>\n<img decoding=\"async\" alt=\"Bitmap-indekser i Go: s\u00f8gning med vild hastighed\" src=\"\/wp-content\/uploads\/2019\/05\/1ab628ee15887d6b3c6c99855aa610c8.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nVi har brug for to hj\u00e6lpefunktioner. Den ene vil blive brugt til at udfylde vores bitmaps med tilf\u00e6ldige data. Tilf\u00e6ldige, men med en bestemt sandsynlighed for, at restauranten har hver egenskab. For eksempel mener jeg, at der er meget f\u00e5 restauranter i Moskva, hvor det ikke er muligt at reservere et bord, og jeg tror, at cirka 20% af stederne er velegnede til vegetarer.<\/p>\n<p>Den anden funktion vil konvertere bitmapen til en liste over restauranter.<br \/>\n<img decoding=\"async\" alt=\"Bitmap-indekser i Go: s\u00f8gning med vild hastighed\" src=\"\/wp-content\/uploads\/2019\/05\/ab9c87a0ba63750116e2e7968f842a9a.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<img decoding=\"async\" alt=\"Bitmap-indekser i Go: s\u00f8gning med vild hastighed\" src=\"\/wp-content\/uploads\/2019\/05\/498cf7a33611d99b90197d4ee82e2834.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nFor at svare p\u00e5 foresp\u00f8rgslen \"Vis mig billige restauranter med terrasse, hvor det er muligt at reservere et bord\", har vi brug for to bitoperationer: NOT og AND.<\/p>\n<p>Vi kan forenkle vores kode lidt ved at bruge en mere kompleks operation AND NOT.<\/p>\n<p>Vi har funktioner til hver af disse operationer. Begge gennemg\u00e5r skiver, tager de tilsvarende elementer fra hver, kombinerer dem med en bitoperation og l\u00e6gger resultatet i en resultatarray.<br \/>\n<img decoding=\"async\" alt=\"Bitmap-indekser i Go: s\u00f8gning med vild hastighed\" src=\"\/wp-content\/uploads\/2019\/05\/ec5652a34f0370dfb03df199f341f153.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nOg nu kan vi bruge vores bitmaps og funktioner til at besvare s\u00f8geforesp\u00f8rgslen.<br \/>\n<img decoding=\"async\" alt=\"Bitmap-indekser i Go: s\u00f8gning med vild hastighed\" src=\"\/wp-content\/uploads\/2019\/05\/cf9500ccfe75995a6008191c16729689.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nYdelsen er ikke s\u00e5 h\u00f8j, selvom funktionerne er meget simple, og vi har sparet en del ved ikke at returnere en ny resultatarray ved hver funktionsopkald.<\/p>\n<p>Efter lidt profilering med pprof bem\u00e6rkede jeg, at Go-kompilatoren overs\u00e5 en meget simpel, men vigtig optimering: funktionsinlining.<br \/>\n<img decoding=\"async\" alt=\"Bitmap-indekser i Go: s\u00f8gning med vild hastighed\" src=\"\/wp-content\/uploads\/2019\/05\/9092f5ba3940d0a4f3fbfa90b364d716.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nSagen er, at Go-kompilatoren er ekstremt bange for l\u00f8kker, der g\u00e5r gennem skiver, og n\u00e6gter kategorisk at inline funktioner, der indeholder s\u00e5danne l\u00f8kker.<br \/>\n<img decoding=\"async\" alt=\"Bitmap-indekser i Go: s\u00f8gning med vild hastighed\" src=\"\/wp-content\/uploads\/2019\/05\/d1bddd62b61b367dd1f680f223fa0ce7.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nMen jeg er ikke bange og kan narre kompilatoren ved at bruge goto i stedet for en l\u00f8kke, som i de gode gamle dage.<\/p>\n<p><img decoding=\"async\" alt=\"Bitmap-indekser i Go: s\u00f8gning med vild hastighed\" src=\"\/wp-content\/uploads\/2019\/05\/5b1c3cb26b923972686047910ecd1c31.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<img decoding=\"async\" alt=\"Bitmap-indekser i Go: s\u00f8gning med vild hastighed\" src=\"\/wp-content\/uploads\/2019\/05\/1ca31d12dc90931b674c6a86a4ea23bd.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nOg som du kan se, s\u00e5 inline'er kompilatoren nu gladeligt vores funktion! I sidste ende form\u00e5r vi at spare omkring 2 mikrosekunder. Ikke d\u00e5rligt!<\/p>\n<p><img decoding=\"async\" alt=\"Bitmap-indekser i Go: s\u00f8gning med vild hastighed\" src=\"\/wp-content\/uploads\/2019\/05\/bdd6735d16082573e600bffe2cdd5662.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nDet andet flaskehals er ikke sv\u00e6rt at se, hvis man ser n\u00f8je p\u00e5 assemblerudgangen. Kompilatoren tilf\u00f8jede en gr\u00e6nsekontrol direkte inde i vores mest brugte l\u00f8kke. Sagen er, at Go er et sikkert sprog, og kompilatoren er bange for, at mine tre argumenter (de tre skiver) har forskellig st\u00f8rrelse. For s\u00e5 ville der v\u00e6re en teoretisk mulighed for, at der opst\u00e5r s\u00e5kaldt buffer overflow.<\/p>\n<p>Lad os berolige compileren ved at vise, at alle skiver har samme st\u00f8rrelse. Vi kan g\u00f8re dette ved at tilf\u00f8je en simpel kontrol i starten af vores funktion.<br \/>\n<img decoding=\"async\" alt=\"Bitmap-indekser i Go: s\u00f8gning med vild hastighed\" src=\"\/wp-content\/uploads\/2019\/05\/59b4ce687a11dd653e9f12fc3130d89a.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nN\u00e5r den ser dette, springer compileren glad over kontrollen, og vi sparer faktisk 500 nanosekunder.<\/p>\n<h2>Store batches<\/h2>\n<p>\nOkay, vi har form\u00e5et at f\u00e5 noget ydeevne ud af vores enkle implementering, men dette resultat er faktisk meget d\u00e5rligere end hvad der er muligt med det nuv\u00e6rende hardware.<\/p>\n<p>Alt hvad vi g\u00f8r, er grundl\u00e6ggende bitoperationer, og vores processorer udf\u00f8rer dem meget effektivt. Men desv\u00e6rre \"fodrer\" vi vores processor med meget sm\u00e5 stykker arbejde. Vores funktioner udf\u00f8rer operationer byte for byte. Vi kan let optimere vores kode til at arbejde med 8-byte stykker ved at bruge skiver af UInt64.<\/p>\n<p><img decoding=\"async\" alt=\"Bitmap-indekser i Go: s\u00f8gning med vild hastighed\" src=\"\/wp-content\/uploads\/2019\/05\/8d1b60ba4b7046c836601631cadd0b6a.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nSom du kan se, har denne lille \u00e6ndring accelereret vores program otte gange ved at \u00f8ge batch-st\u00f8rrelsen otte gange. Gevinsten er i bund og grund line\u00e6r.<\/p>\n<p><img decoding=\"async\" alt=\"Bitmap-indekser i Go: s\u00f8gning med vild hastighed\" src=\"\/wp-content\/uploads\/2019\/05\/0fc663412d04dd38b927de5c8776f69d.jpeg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<h2>Implementation i assembler<\/h2>\n<p>\n<img decoding=\"async\" alt=\"Bitmap-indekser i Go: s\u00f8gning med vild hastighed\" src=\"\/wp-content\/uploads\/2019\/05\/b3ef133167b89da983356b1c7389aecd.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nMen det er ikke slutningen. Vores processorer kan arbejde med stykker p\u00e5 16, 32 og endda 64 byte. S\u00e5danne \"bredde\" operationer kaldes single instruction multiple data (SIMD; \u00e9n instruktion, mange data), og processen med at \u00e6ndre koden, s\u00e5 den bruger s\u00e5danne operationer, kaldes vektorisering.<\/p>\n<p>Desv\u00e6rre er Go-compiler ikke s\u00e6rlig god til vektorisering. P\u00e5 nuv\u00e6rende tidspunkt er den eneste m\u00e5de at vektorisere kode i Go, at tage og l\u00e6gge dataoperationer manuelt ved hj\u00e6lp af Go-assembler.<\/p>\n<p><img decoding=\"async\" alt=\"Bitmap-indekser i Go: s\u00f8gning med vild hastighed\" src=\"\/wp-content\/uploads\/2019\/05\/91278be45df67e1f9572d68fab7ebad1.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nGo-assembler er et m\u00e6rkeligt v\u00e6sen. Du ved sikkert, at assembler er noget, der er meget bundet til den computerarkitektur, du skriver til, men s\u00e5dan er det ikke i Go. Go-assembler minder mere om IRL (intermediate representation language) eller mellemtrinssprog: det er praktisk taget platformuafh\u00e6ngigt. Rob Pike pr\u00e6senterede et fantastisk <noindex><a rel=\"nofollow\" href=\"https:\/\/www.youtube.com\/watch?v=KINIAgRpkDA\">pr\u00e6sentationen<\/a><\/noindex> om dette for et par \u00e5r siden p\u00e5 GopherCon i Denver.<\/p>\n<p>Derudover bruger Go et us\u00e6dvanligt format, Plan 9, som adskiller sig fra de anerkendte AT&amp;T- og Intel-formater.<br \/>\n<img decoding=\"async\" alt=\"Bitmap-indekser i Go: s\u00f8gning med vild hastighed\" src=\"\/wp-content\/uploads\/2019\/05\/526f75eb18edfc2f851f2725e9d6f69e.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nMan kan trygt sige, at det ikke er den sjoveste opgave at skrive Go-assembler manuelt.<\/p>\n<p>Men heldigvis er der allerede to h\u00f8jniveau v\u00e6rkt\u00f8jer, der hj\u00e6lper os med at skrive Go-assembler: PeachPy og avo. Begge v\u00e6rkt\u00f8jer genererer Go-assembler fra mere h\u00f8j-niveau kode skrevet i henholdsvis Python og Go.<br \/>\n<img decoding=\"async\" alt=\"Bitmap-indekser i Go: s\u00f8gning med vild hastighed\" src=\"\/wp-content\/uploads\/2019\/05\/b8a7aa2c585b805e9b1f2ada186b84e8.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nDisse v\u00e6rkt\u00f8jer forenkler ting som registerallokering, skrivning af loops, og generelt g\u00f8r det lettere at tr\u00e6de ind i verden af assemblerprogrammering i Go.<\/p>\n<p>Vi vil bruge avo, s\u00e5 vores programmer n\u00e6sten vil v\u00e6re almindelige Go-programmer.<br \/>\n<img decoding=\"async\" alt=\"Bitmap-indekser i Go: s\u00f8gning med vild hastighed\" src=\"\/wp-content\/uploads\/2019\/05\/128e4adef14e5f2cf00fb5b6302ef58e.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nHer er det enkleste eksempel p\u00e5 et avo-program. Vi har en main()-funktion, der definerer en funktion Add() indeni, hvis form\u00e5l er at l\u00e6gge to tal sammen. Der er hj\u00e6lpefunktioner til at hente parametre efter navn og til at f\u00e5 et ledigt og passende processorregister. Hver processoroperation har en tilsvarende funktion i avo, som man kan se med ADDQ. Og endelig ser vi en hj\u00e6lpefunktion til at gemme det resulterende v\u00e6rd.<br \/>\n<img decoding=\"async\" alt=\"Bitmap-indekser i Go: s\u00f8gning med vild hastighed\" src=\"\/wp-content\/uploads\/2019\/05\/01ccaa6aa6d394ef598ea2dbc9257d87.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nN\u00e5r vi kalder go generate, vil vi k\u00f8re programmet i avo, og der vil blive genereret to filer:<\/p>\n<ul>\n<li>add.s med den resulterende kode i Go-assembler;\n<\/li>\n<li>stub.go med funktionsoverskrifter for at forbinde de to verdener: Go og assembler.\n<\/li>\n<\/ul>\n<p>\n<img decoding=\"async\" alt=\"Bitmap-indekser i Go: s\u00f8gning med vild hastighed\" src=\"\/wp-content\/uploads\/2019\/05\/72a9443776ecf45eef6fb97a4e08acba.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nNu hvor vi har set, hvad og hvordan avo fungerer, lad os se p\u00e5 vores funktioner. Jeg har implementeret b\u00e5de de skalar og vektor (SIMD) versioner af funktionerne.<\/p>\n<p>Lad os f\u00f8rst se p\u00e5 de skalar versioner.<br \/>\n<img decoding=\"async\" alt=\"Bitmap-indekser i Go: s\u00f8gning med vild hastighed\" src=\"\/wp-content\/uploads\/2019\/05\/ec31dbb8b97b9d7c1012a120fa18cdaf.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nSom i det tidligere eksempel beder vi om at f\u00e5 et ledigt og korrekt register af almindelig brug, vi har ikke brug for at beregne forskydninger og st\u00f8rrelser for argumenterne. Alt dette klarer avo for os.<br \/>\n<img decoding=\"async\" alt=\"Bitmap-indekser i Go: s\u00f8gning med vild hastighed\" src=\"\/wp-content\/uploads\/2019\/05\/88085e927dd943ea0f808a28fb3ccf9b.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nTidligere brugte vi labels og goto (eller spring) for at forbedre ydeevnen og for at narre Go-kompilereren, men nu g\u00f8r vi det fra starten. Forskellen er, at loops er et mere h\u00f8jere begreb. I assembler har vi kun labels og spring.<br \/>\n<img decoding=\"async\" alt=\"Bitmap-indekser i Go: s\u00f8gning med vild hastighed\" src=\"\/wp-content\/uploads\/2019\/05\/9a4181248eb279d89c1445a820b06649.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nDen resterende kode skal allerede v\u00e6re bekendt og forst\u00e5elig. Vi emulerer en loop med labels og spring, tager en lille del af data fra vores to slices, kombinerer dem med en bitoperation (AND NOT i dette tilf\u00e6lde) og derefter placerer resultatet i den resulterende slice. Det er alt.<br \/>\n<img decoding=\"async\" alt=\"Bitmap-indekser i Go: s\u00f8gning med vild hastighed\" src=\"\/wp-content\/uploads\/2019\/05\/19275ead27f63092fc6596bed38a8d03.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nHer er den endelige kode i assembler. Vi beh\u00f8vede ikke at beregne forskydninger og st\u00f8rrelser (markeret med gr\u00f8nt) eller holde \u00f8je med de anvendte registre (markeret med r\u00f8dt).<br \/>\n<img decoding=\"async\" alt=\"Bitmap-indekser i Go: s\u00f8gning med vild hastighed\" src=\"\/wp-content\/uploads\/2019\/05\/ed68528a6f852634a5b536670c6315f0.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nHvis vi sammenligner ydeevnen af implementeringen i assembler med ydeevnen af den bedste implementering i Go, vil vi se, at de er ens. Og det er forventeligt. For vi har ikke gjort noget s\u00e6rligt \u2014 vi har blot genskabt det, som Go-kompilatoren ville have gjort.<\/p>\n<p>Desv\u00e6rre kan vi ikke f\u00e5 kompilatoren til at inline vores funktioner, der er skrevet i assembler. Go-kompilatoren mangler i \u00f8jeblikket denne funktion, selv om der har v\u00e6ret en anmodning om at tilf\u00f8je den i lang tid.<\/p>\n<p>Netop derfor er det umuligt at f\u00e5 nogen som helst fordele ved sm\u00e5 funktioner i assembler. Vi m\u00e5 enten skrive store funktioner, eller ogs\u00e5 bruge det nye math\/bits bibliotek, eller undg\u00e5 assembler helt.<\/p>\n<p>Lad os nu se p\u00e5 de vektoriserede versioner af vores funktioner.<br \/>\n<img decoding=\"async\" alt=\"Bitmap-indekser i Go: s\u00f8gning med vild hastighed\" src=\"\/wp-content\/uploads\/2019\/05\/7f67c3cc855908fb47c7900d6e5d7f54.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nTil dette eksempel har jeg valgt at anvende AVX2, s\u00e5 vi vil bruge operationer, der arbejder med 32-byte blokke. Kodestrukturen ligner meget den skalar version: indl\u00e6sning af parametre, anmodning om en ledig f\u00e6lles registreringsenhed osv.<br \/>\n<img decoding=\"async\" alt=\"Bitmap-indekser i Go: s\u00f8gning med vild hastighed\" src=\"\/wp-content\/uploads\/2019\/05\/021e0424a0d733ec7db9edeb98ce1f65.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nEn af nyhederne er, at de bredere vektoroperationer bruger s\u00e6rlige brede registre. I tilf\u00e6lde af 32-byte blokke er det registre med pr\u00e6fikset Y. Derfor ser du funktionen YMM() i koden. Hvis jeg havde brugt AVX-512 med 64-bit blokke, ville pr\u00e6fikset have v\u00e6ret Z.<\/p>\n<p>En anden nyhed er, at jeg har valgt at bruge en optimering kaldet loop unrolling, hvilket vil sige at udf\u00f8re otte loop-operationer manuelt, inden vi hopper tilbage til starten af loopet. Denne optimering reducerer antallet af brancher i koden og er begr\u00e6nset af antallet af ledige registre til r\u00e5dighed.<br \/>\n<img decoding=\"async\" alt=\"Bitmap-indekser i Go: s\u00f8gning med vild hastighed\" src=\"\/wp-content\/uploads\/2019\/05\/f54631d9e8c69f6f70ecace3133ae1e8.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nHvad med ydeevnen? Den er fremragende! Vi har opn\u00e5et en acceleration p\u00e5 cirka syv gange sammenlignet med den bedste l\u00f8sning i Go. Imponerende, ikke?<br \/>\n<img decoding=\"async\" alt=\"Bitmap-indekser i Go: s\u00f8gning med vild hastighed\" src=\"\/wp-content\/uploads\/2019\/05\/d7e85c933eecb243cfb49225b4d92c6c.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nMen selv denne implementering kunne potentielt v\u00e6re blevet accelereret ved hj\u00e6lp af AVX-512, prefetching eller JIT (just-in-time compiler) til foresp\u00f8rgselsplanl\u00e6ggeren. Men det er helt klart et emne for et separat foredrag.<\/p>\n<h2>Problemer med bitmap-indekser<\/h2>\n<p>\nNu hvor vi har gennemg\u00e5et en simpel implementering af bitmap-indeksen i Go og en langt mere effektiv i assembler, lad os endelig tale om, hvorfor bitmap-indekser s\u00e5 sj\u00e6ldent anvendes.<br \/>\n<img decoding=\"async\" alt=\"Bitmap-indekser i Go: s\u00f8gning med vild hastighed\" src=\"\/wp-content\/uploads\/2019\/05\/83d36a5c92ba90fd680fe8afb6cfc11f.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nI de gamle videnskabelige arbejder n\u00e6vnes der tre problemer med bitmap-indekser, men nyere forskning og jeg selv h\u00e6vder, at de ikke l\u00e6ngere er relevante. Lad os ikke dykke dybt ind i hvert af disse problemer, men overfladisk se p\u00e5 dem.<\/p>\n<h2>Problemet med h\u00f8j kardinalitet<\/h2>\n<p>\nS\u00e5 vi f\u00e5r at vide, at bitmap-indekser kun er egnede til felter med lav kardinalitet, dvs. dem med f\u00e5 v\u00e6rdier (f.eks. k\u00f8n eller \u00f8jenfarve), og grunden til dette er, at den almindelige repr\u00e6sentation for s\u00e5danne felter (\u00e9n bit pr. v\u00e6rdi) ved h\u00f8j kardinalitet ville optage for meget plads, og desuden ville disse bitmap-indekser v\u00e6re svagt (sj\u00e6ldent) udfyldt.<br \/>\n<img decoding=\"async\" alt=\"Bitmap-indekser i Go: s\u00f8gning med vild hastighed\" src=\"\/wp-content\/uploads\/2019\/05\/0787efc4d2cb4ea4d404ca7888b33697.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<img decoding=\"async\" alt=\"Bitmap-indekser i Go: s\u00f8gning med vild hastighed\" src=\"\/wp-content\/uploads\/2019\/05\/44ab4bc7c25d14fe2f53caf5d9da399d.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nNogle gange kan vi bruge en anden repr\u00e6sentation, f.eks. den standard, vi bruger til at repr\u00e6sentere tal. Men det var introduktionen af komprimeringsalgoritmer, der \u00e6ndrede alt. I l\u00f8bet af de sidste \u00e5rtier har forskere og akademikere udviklet et stort antal komprimeringsalgoritmer til bitmapper. Deres prim\u00e6re fordel er, at der ikke er behov for at dekomprimere bitmapper for at udf\u00f8re bit-operationer \u2014 vi kan udf\u00f8re bit-operationer direkte p\u00e5 de komprimerede bitmapper.<br \/>\n<img decoding=\"async\" alt=\"Bitmap-indekser i Go: s\u00f8gning med vild hastighed\" src=\"\/wp-content\/uploads\/2019\/05\/ecb54fbf15aa11271bbbab01ecbda880.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nFor nylig er der ogs\u00e5 begyndt at dukke hybridmetoder op, s\u00e5som roaring bitmapper. De bruger samtidig tre forskellige repr\u00e6sentationer til bitmapper \u2014 selve bitmapperne, arrays og s\u00e5kaldte bit runs \u2014 og balancerer mellem dem for at maksimere ydeevne og minimere hukommelsesforbruget.<\/p>\n<p>Du kan finde roaring bitmapper i de mest popul\u00e6re applikationer. Der findes allerede et stort antal implementationer til mange forskellige programmeringssprog, inklusive mere end tre implementationer til Go.<br \/>\n<img decoding=\"async\" alt=\"Bitmap-indekser i Go: s\u00f8gning med vild hastighed\" src=\"\/wp-content\/uploads\/2019\/05\/de2adfebc431ff48c996247b453f02ae.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nEn anden tilgang, der kan hj\u00e6lpe os med at h\u00e5ndtere h\u00f8j kardinalitet, kaldes gruppering (binning). Forestil dig, at du har et felt, der repr\u00e6senterer en persons h\u00f8jde. H\u00f8jde er et flydende tal, men vi mennesker t\u00e6nker ikke p\u00e5 det p\u00e5 den m\u00e5de. For os er der ingen forskel mellem en h\u00f8jde p\u00e5 185,2 cm og 185,3 cm.<\/p>\n<p>S\u00e5 vi kan gruppere lignende v\u00e6rdier i grupper inden for 1 cm.<\/p>\n<p>Og hvis vi ogs\u00e5 ved, at der meget sj\u00e6ldent er mennesker, der er mindre end 50 cm h\u00f8je eller h\u00f8jere end 250 cm, kan vi i princippet omdanne et felt med uendelig kardinalitet til et felt med en kardinalitet p\u00e5 omtrent 200 v\u00e6rdier.<\/p>\n<p>Selvf\u00f8lgelig kan vi, hvis det er n\u00f8dvendigt, foretage yderligere filtrering efterf\u00f8lgende.<\/p>\n<h2>Problemet med h\u00f8j b\u00e5ndbredde<\/h2>\n<p>\nDet n\u00e6ste problem med bitmap-indekser er, at opdatering af dem kan v\u00e6re meget kostbart.<\/p>\n<p>Databaser skal give mulighed for at opdatere data i det \u00f8jeblik, hvor hundreder af andre foresp\u00f8rgsler potentielt s\u00f8ger p\u00e5 disse data. Vi har brug for l\u00e5se for at undg\u00e5 problemer med samtidig adgang til data eller andre problemer med delt adgang. Og hvor der er \u00e9n stor l\u00e5s, er der et problem \u2014 lock contention, n\u00e5r denne l\u00e5s bliver en flaskehals.<br \/>\n<img decoding=\"async\" alt=\"Bitmap-indekser i Go: s\u00f8gning med vild hastighed\" src=\"\/wp-content\/uploads\/2019\/05\/0ae1bf925542d286f8b7b245c160a35e.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nDette problem kan l\u00f8ses eller omg\u00e5s ved hj\u00e6lp af sharding eller ved at bruge versionsindekser.<\/p>\n<p>Sharding er enkelt og almengyldigt. Du kan shard bitmap-indekset, ligesom du ville shard andre data. I stedet for \u00e9n stor l\u00e5s f\u00e5r du mange sm\u00e5 l\u00e5se, og derved undg\u00e5r du lock contention.<\/p>\n<p>Den anden m\u00e5de at l\u00f8se problemet p\u00e5 er ved at bruge versionsindekser. Du kan have \u00e9n kopi af indekset, som du bruger til at s\u00f8ge eller l\u00e6se, og \u00e9n til skrivning eller opdatering. Og en gang imellem (f.eks. hvert 100 ms eller 500 ms) duplikerer du dem og bytter dem om. Selvf\u00f8lgelig er denne tilgang kun anvendelig i de tilf\u00e6lde, hvor din applikation kan arbejde med et let forsinket s\u00f8geindeks.<\/p>\n<p>Disse to tilgange kan bruges samtidigt: du kan have et sharded versionsindeks.<\/p>\n<h2>Mere komplekse foresp\u00f8rgsler<\/h2>\n<p>Det sidste problem med bitmap-indekser er, at de, som vi har f\u00e5et at vide, ikke passer godt til mere komplekse typer foresp\u00f8rgsler, s\u00e5som foresp\u00f8rgsler 'over intervallet'.<\/p>\n<p>Det er sandt, hvis man t\u00e6nker over det, at bitoperationer som AND, OR osv. ikke egner sig s\u00e6rlig godt til foresp\u00f8rgsler af typen 'Vis mig hoteller med en pris pr. nat mellem 200 og 300 dollars'.<br \/>\n<img decoding=\"async\" alt=\"Bitmap-indekser i Go: s\u00f8gning med vild hastighed\" src=\"\/wp-content\/uploads\/2019\/05\/7bc2e129cad46fb5875c2b3018c39ff7.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nEn naiv og meget ufornuftig l\u00f8sning ville v\u00e6re at tage resultaterne for hver enkelt dollarv\u00e6rdi og kombinere dem med bitoperationen OR.<br \/>\n<img decoding=\"async\" alt=\"Bitmap-indekser i Go: s\u00f8gning med vild hastighed\" src=\"\/wp-content\/uploads\/2019\/05\/b9f8fc7945caa1866f8e0cfa8a04bd98.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nEn lidt mere korrekt l\u00f8sning ville v\u00e6re at bruge gruppering. For eksempel i grupper af 50 dollars. Det ville accelerere vores proces med 50 gange.<\/p>\n<p>Men problemet kan ogs\u00e5 nemt l\u00f8ses ved at bruge en repr\u00e6sentation, der er specielt designet til denne type foresp\u00f8rgsler. I videnskabelige arbejder kaldes det range-encoded bitmaps.<br \/>\n<img decoding=\"async\" alt=\"Bitmap-indekser i Go: s\u00f8gning med vild hastighed\" src=\"\/wp-content\/uploads\/2019\/05\/644a420628b21f220a7af1ff15c4031f.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nI denne repr\u00e6sentation s\u00e6tter vi ikke bare et enkelt bit for en v\u00e6rdi (for eksempel 200), men vi s\u00e6tter denne v\u00e6rdi og alt, hvad der er h\u00f8jere. 200 og derover. Det samme g\u00e6lder for 300: 300 og derover. Og s\u00e5 videre.<\/p>\n<p>Ved at bruge denne repr\u00e6sentation kan vi svare p\u00e5 denne type s\u00f8geforesp\u00f8rgsel ved at g\u00e5 igennem indekset kun to gange. F\u00f8rst f\u00e5r vi en liste over hoteller, hvor v\u00e6relset koster mindre end 300 dollars, og derefter fjerner vi dem, hvor prisen p\u00e5 v\u00e6relset er mindre end 199 dollars. F\u00e6rdig.<br \/>\n<img decoding=\"async\" alt=\"Bitmap-indekser i Go: s\u00f8gning med vild hastighed\" src=\"\/wp-content\/uploads\/2019\/05\/cc2bb58d7d7a51495c62ec7da81e2d12.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nDu vil blive overrasket, men selv geoforesp\u00f8rgsler er mulige ved hj\u00e6lp af bitmap-indekser. Tricket er at bruge en georepr\u00e6sentation, der omkranser din koordinat med en geometrisk figur. For eksempel S2 fra Google. Figuren skal kunne repr\u00e6senteres med tre eller flere overlappende linjer, som kan nummereres. P\u00e5 den m\u00e5de kan vi oms\u00e6tte vores geoforesp\u00f8rgsel til flere \"interval\" foresp\u00f8rgsler (ved hj\u00e6lp af disse nummererede linjer).<\/p>\n<h2>F\u00e6rdige l\u00f8sninger<\/h2>\n<p>\nJeg h\u00e5ber, jeg har fanget lidt af din interesse, og at du nu har et nyttigt v\u00e6rkt\u00f8j til din samling. Hvis du nogensinde skal lave noget lignende, vil du vide, hvilken retning du skal se i.<\/p>\n<p>Men ikke alle har tid, t\u00e5lmodighed og ressourcer til at oprette bitmap-indekser fra bunden. Is\u00e6r mere avancerede, ved hj\u00e6lp af SIMD, for eksempel.<\/p>\n<p>Heldigvis er der flere f\u00e6rdige l\u00f8sninger, der kan hj\u00e6lpe dig.<br \/>\n<img decoding=\"async\" alt=\"Bitmap-indekser i Go: s\u00f8gning med vild hastighed\" src=\"\/wp-content\/uploads\/2019\/05\/817b47cb189a758756b602ec9cf319e1.jpeg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<h2>Roaring bitmaps<\/h2>\n<p>\nFor det f\u00f8rste er der den n\u00e6vnte roaring bitmaps-bibliotek, som jeg allerede har talt om. Det indeholder alle de n\u00f8dvendige containere og bitoperationer, du har brug for for at lave et fuldt bitmap-indeks.<br \/>\n<img decoding=\"async\" alt=\"Bitmap-indekser i Go: s\u00f8gning med vild hastighed\" src=\"\/wp-content\/uploads\/2019\/05\/57f61a51485c174666b52dc2063fabe8.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nDesv\u00e6rre bruger ingen af Go-implementeringerne i \u00f8jeblikket SIMD, hvilket betyder, at Go-implementeringerne er mindre effektive end implementeringer i C, for eksempel.<\/p>\n<h2>Pilosa<\/h2>\n<p>\nEt andet produkt, der kan hj\u00e6lpe dig, er databasen Pilosa, som i bund og grund kun har bitmap-indekser. Det er en relativt ny l\u00f8sning, men den vinder hurtigt popularitet.<br \/>\n<img decoding=\"async\" alt=\"Bitmap-indekser i Go: s\u00f8gning med vild hastighed\" src=\"\/wp-content\/uploads\/2019\/05\/d3870a979e1e73d093fe5d5e9bb71cd8.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nPilosa bruger roaring bitmaps internt og giver dig mulighed for at anvende dem, hvilket forenkler og forklarer alle de ting, jeg har n\u00e6vnt f\u00f8r: gruppering, range-encoded bitmaps, begrebet felt osv.<\/p>\n<p>Lad os hurtigt tage et kig p\u00e5 et eksempel p\u00e5 brugen af Pilosa til at besvare et sp\u00f8rgsm\u00e5l, som du allerede kender.<br \/>\n<img decoding=\"async\" alt=\"Bitmap-indekser i Go: s\u00f8gning med vild hastighed\" src=\"\/wp-content\/uploads\/2019\/05\/dfee7abfd653cc19d9aa8b64c64f3e4e.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nEksemplet ligner meget det, du har set f\u00f8r. Vi opretter en klient til Pilosa-serveren, opretter et indeks og de n\u00f8dvendige felter, fylder derefter vores felter med tilf\u00e6ldige data med sandsynligheder, og til sidst udf\u00f8rer vi det kendte request.<\/p>\n<p>Derefter bruger vi NOT p\u00e5 feltet \u00abexpensive\u00bb, krydser resultatet (eller AND-er) med feltet \u00abterrace\u00bb og med feltet \u00abreservations\u00bb. Og til sidst f\u00e5r vi det endelige resultat.<br \/>\n<img decoding=\"async\" alt=\"Bitmap-indekser i Go: s\u00f8gning med vild hastighed\" src=\"\/wp-content\/uploads\/2019\/05\/8d66c6d68019c2297b6c15b700f06a3a.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nJeg h\u00e5ber virkelig, at der i den n\u00e6rmeste fremtid vil komme denne nye type indekser - bitmap-indekser - i databaser som MySQL og PostgreSQL.<br \/>\n<img decoding=\"async\" alt=\"Bitmap-indekser i Go: s\u00f8gning med vild hastighed\" src=\"\/wp-content\/uploads\/2019\/05\/7a8e33d576c7fb6376a173ee038b4206.jpeg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<h2>Konklusion<\/h2>\n<p>\n<img decoding=\"async\" alt=\"Bitmap-indekser i Go: s\u00f8gning med vild hastighed\" src=\"\/wp-content\/uploads\/2019\/05\/c62caa9ad6f2d96056c80326f4fa9a0d.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nHvis du ikke er faldet i s\u00f8vn endnu, tak. Jeg var n\u00f8dt til at ber\u00f8re mange emner kort p\u00e5 grund af tidsbegr\u00e6nsninger, men jeg h\u00e5ber, at pr\u00e6sentationen var nyttig og m\u00e5ske endda motiverende.<\/p>\n<p>Det er godt at vide om bitmap-indekser, selvom de ikke er n\u00f8dvendige lige nu. Lad dem v\u00e6re et ekstra v\u00e6rkt\u00f8j i din v\u00e6rkt\u00f8jskasse.<\/p>\n<p>Vi har set p\u00e5 forskellige tricks til at \u00f8ge ydeevnen for Go og de ting, som Go-kompilatoren ikke helt h\u00e5ndterer godt endnu. Dette er bestemt nyttigt at vide for enhver Go-udvikler.<\/p>\n<p>Det var alt, hvad jeg ville sige. Tak!<br \/>\n<br \/>Kilde: <a content=\"nofollow\" rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/badoo\/blog\/451938\/\">habr.com<\/a><\/p>","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"excerpt":{"rendered":"<p>\u0412\u0441\u0442\u0443\u043f\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0435 \u0441\u043b\u043e\u0432\u043e \u042f \u0432\u044b\u0441\u0442\u0443\u043f\u0438\u043b \u0441 \u044d\u0442\u0438\u043c \u0434\u043e\u043a\u043b\u0430\u0434\u043e\u043c \u043d\u0430 \u0430\u043d\u0433\u043b\u0438\u0439\u0441\u043a\u043e\u043c \u044f\u0437\u044b\u043a\u0435 \u043d\u0430 \u043a\u043e\u043d\u0444\u0435\u0440\u0435\u043d\u0446\u0438\u0438 GopherCon Russia 2019 \u0432 \u041c\u043e\u0441\u043a\u0432\u0435 \u0438 \u043d\u0430 \u0440\u0443\u0441\u0441\u043a\u043e\u043c \u2014 \u043d\u0430 \u043c\u0438\u0442\u0430\u043f\u0435 \u0432 \u041d\u0438\u0436\u043d\u0435\u043c \u041d\u043e\u0432\u0433\u043e\u0440\u043e\u0434\u0435. \u0420\u0435\u0447\u044c \u0432 \u043d\u0451\u043c \u0438\u0434\u0451\u0442 \u043e bitmap-\u0438\u043d\u0434\u0435\u043a\u0441\u0435 \u2014 \u043c\u0435\u043d\u0435\u0435 \u0440\u0430\u0441\u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0451\u043d\u043d\u043e\u043c, \u0447\u0435\u043c B-tree, \u043d\u043e \u043d\u0435 \u043c\u0435\u043d\u0435\u0435 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e\u043c. \u0414\u0435\u043b\u044e\u0441\u044c \u0437\u0430\u043f\u0438\u0441\u044c\u044e \u0432\u044b\u0441\u0442\u0443\u043f\u043b\u0435\u043d\u0438\u044f \u043d\u0430 \u043a\u043e\u043d\u0444\u0435\u0440\u0435\u043d\u0446\u0438\u0438 \u043d\u0430 \u0430\u043d\u0433\u043b\u0438\u0439\u0441\u043a\u043e\u043c \u0438 \u0442\u0435\u043a\u0441\u0442\u043e\u0432\u043e\u0439 \u0440\u0430\u0441\u0448\u0438\u0444\u0440\u043e\u0432\u043a\u043e\u0439 \u043d\u0430 \u0440\u0443\u0441\u0441\u043a\u043e\u043c. \u041c\u044b \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c, [&hellip;]<\/p>\n","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"author":1,"featured_media":25469,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[688],"tags":[],"class_list":["post-33793","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-administrirovanie"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.3 - aioseo.com -->\n\t<meta name=\"description\" content=\"\u0412\u0441\u0442\u0443\u043f\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0435 \u0441\u043b\u043e\u0432\u043e \u042f \u0432\u044b\u0441\u0442\u0443\u043f\u0438\u043b \u0441.\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Yuri Gagarin\"\/>\n\t<link rel=\"canonical\" href=\"https:\/\/prohoster.info\/da\/blog\/administrirovanie\/bitmap-indeksy-v-go-poisk-na-dikoj-skorosti\" \/>\n\t\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.3\" \/>\n\t\t<meta property=\"og:locale\" content=\"da_DK\" \/>\n\t\t<meta property=\"og:site_name\" content=\"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"\ud83e\udd47Bitmap-\u0438\u043d\u0434\u0435\u043a\u0441\u044b \u0432 Go: \u043f\u043e\u0438\u0441\u043a \u043d\u0430 \u0434\u0438\u043a\u043e\u0439 \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u0438 | ProHoster\" \/>\n\t\t<meta property=\"og:description\" content=\"\u0412\u0441\u0442\u0443\u043f\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0435 \u0441\u043b\u043e\u0432\u043e \u042f \u0432\u044b\u0441\u0442\u0443\u043f\u0438\u043b \u0441.\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/da\/blog\/administrirovanie\/bitmap-indeksy-v-go-poisk-na-dikoj-skorosti\" \/>\n\t\t<meta property=\"og:image\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:secure_url\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:width\" content=\"350\" \/>\n\t\t<meta property=\"og:image:height\" content=\"350\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2019-10-31T18:54:42+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2019-10-31T18:54:42+00:00\" \/>\n\t\t<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<meta property=\"article:author\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"\ud83e\udd47Bitmap-indekser i Go: s\u00f8gning med vilde hastigheder | ProHoster","description":"Introduktionsord Jeg holdt.","canonical_url":"https:\/\/prohoster.info\/da\/blog\/administrirovanie\/bitmap-indeksy-v-go-poisk-na-dikoj-skorosti","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"da_DK","og:site_name":"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b","og:type":"article","og:title":"\ud83e\udd47Bitmap-\u0438\u043d\u0434\u0435\u043a\u0441\u044b \u0432 Go: \u043f\u043e\u0438\u0441\u043a \u043d\u0430 \u0434\u0438\u043a\u043e\u0439 \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u0438 | ProHoster","og:description":"\u0412\u0441\u0442\u0443\u043f\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0435 \u0441\u043b\u043e\u0432\u043e \u042f \u0432\u044b\u0441\u0442\u0443\u043f\u0438\u043b \u0441.","og:url":"https:\/\/prohoster.info\/da\/blog\/administrirovanie\/bitmap-indeksy-v-go-poisk-na-dikoj-skorosti","og:image":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:secure_url":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:width":350,"og:image:height":350,"article:published_time":"2019-10-31T18:54:42+00:00","article:modified_time":"2019-10-31T18:54:42+00:00","article:publisher":"https:\/\/www.facebook.com\/prohoster","article:author":"https:\/\/www.facebook.com\/prohoster"},"aioseo_meta_data":{"post_id":"33793","title":null,"description":null,"keywords":null,"keyphrases":null,"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":null,"og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"","isEnabled":true},"graphs":[]},"schema_type":null,"schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":null,"robots_max_videopreview":null,"robots_max_imagepreview":"large","priority":null,"frequency":null,"local_seo":null,"seo_analyzer_scan_date":"2026-01-21 16:43:19","breadcrumb_settings":null,"limit_modified_date":false,"reviewed_by":null,"ai":null,"created":"2021-03-01 02:33:25","updated":"2026-01-21 16:43:19","focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/posts\/33793","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/comments?post=33793"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/posts\/33793\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/media\/25469"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/media?parent=33793"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/categories?post=33793"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/tags?post=33793"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}