{"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\/it\/blog\/administrirovanie\/bitmap-indeksy-v-go-poisk-na-dikoj-skorosti","title":{"rendered":"Bitmap index in Go: searching at wild speed","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p><img decoding=\"async\" alt=\"Bitmap index in Go: searching at wild speed\" src=\"\/wp-content\/uploads\/2019\/05\/86ef928e6741022b2c0e5885a031408a.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<\/p>\n<h2>Introduction<\/h2>\n<p>\nI presented this report in English at the GopherCon Russia 2019 conference in Moscow and in Russian at the meetup in Nizhny Novgorod. It's about the bitmap index, which is less common than the B-tree but equally interesting. I'm sharing <noindex><a rel=\"nofollow\" href=\"https:\/\/youtu.be\/WvlUH6MjUuI?list=PL3xVZC4USRNSO_kb2lh_J_no6C-KJ7Phg\">the recording<\/a><\/noindex> of the conference presentation in English and a textual transcript in Russian.<\/p>\n<p>We will explore how the bitmap index is structured, when it is superior and when it is inferior to other indexes, and in what cases it is significantly faster; we will see which popular DBMS already have bitmap indexes; and we'll attempt to write our own in Go. As a \u2018dessert\u2019, we\u2019ll use existing libraries to create our super-fast specialized database.<\/p>\n<p>I truly hope that my efforts will be useful and interesting for you. Let\u2019s go!<br \/>\n<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h2>Introduzione<\/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=\"Riproduci 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>Hello everyone! It's six in the evening, and we are all super tired. A wonderful time to talk about the boring theory of database indexes, right? Don\u2019t worry, I\u2019ll sprinkle in a few lines of code here and there. \ud83d\ude42<\/p>\n<p>Jokes aside, the presentation is packed with information, and we don\u2019t have much time. So let\u2019s get started.<br \/>\n<img decoding=\"async\" alt=\"Bitmap index in Go: searching at wild speed\" src=\"\/wp-content\/uploads\/2019\/05\/e778e13727700f0335a4b5558a0d8db3.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nOggi parler\u00f2 dei seguenti argomenti:<\/p>\n<ul>\n<li>cosa sono gli indici;\n<\/li>\n<li>cosa \u00e8 un bitmap index;\n<\/li>\n<li>dove viene utilizzato e dove NON viene utilizzato e perch\u00e9;\n<\/li>\n<li>una semplice implementazione in Go e un po' di lotta con il compilatore;\n<\/li>\n<li>un'implementazione un po' meno semplice, ma molto pi\u00f9 performante in Go-assembly;\n<\/li>\n<li>i \"problemi\" dei bitmap index;\n<\/li>\n<li>implementazioni esistenti.\n<\/li>\n<\/ul>\n<h2>Allora, cosa sono gli indici?<\/h2>\n<p>\n<img decoding=\"async\" alt=\"Bitmap index in Go: searching at wild speed\" src=\"\/wp-content\/uploads\/2019\/05\/b80e5b990c44814afe9150a9a82351fd.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nUn indice \u00e8 una struttura dati separata che manteniamo e aggiorniamo in aggiunta ai dati principali. Viene utilizzata per accelerare le ricerche. Senza indici, la ricerca richiederebbe un passaggio completo sui dati (un processo chiamato full scan) e questo processo ha una complessit\u00e0 algoritmica lineare. Ma i database contengono generalmente un'enorme quantit\u00e0 di dati e la complessit\u00e0 lineare \u00e8 troppo lenta. Idealmente, vorremmo avere una complessit\u00e0 logaritmica o costante.<\/p>\n<p>\u00c8 un argomento enorme e complesso, pieno di sfumature e compromessi, ma guardando a decenni di sviluppo e ricerca su vari database, sono pronto a sostenere che esistono solo pochi approcci ampiamente utilizzati per la creazione di indici nei database.<\/p>\n<p><img decoding=\"async\" alt=\"Bitmap index in Go: searching at wild speed\" src=\"\/wp-content\/uploads\/2019\/05\/08a74dbb365035cd99bc94d72644a1b7.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nIl primo approccio consiste nella riduzione gerarchica dell'area di ricerca, suddividendo l'area in parti pi\u00f9 piccole.<\/p>\n<p>Di solito lo facciamo utilizzando vari tipi di alberi. Un esempio potrebbe essere una grande scatola con materiali nel tuo armadio, che contiene scatole pi\u00f9 piccole con materiali suddivisi per diversi temi. Se hai bisogno di materiali, \u00e8 probabile che cercherai nella scatola con l'etichetta \u00abMateriali\u00bb, e non in quella con scritto \u00abBiscotti\u00bb, giusto?<\/p>\n<p><img decoding=\"async\" alt=\"Bitmap index in Go: searching at wild speed\" src=\"\/wp-content\/uploads\/2019\/05\/423fac47c748980b18af434644af4dae.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nIl secondo approccio consiste nel selezionare immediatamente l'elemento o il gruppo di elementi desiderati. Lo facciamo usando hash maps o indici inversi. L'uso delle hash maps \u00e8 molto simile all'esempio precedente, solo che invece di una scatola con scatole hai nel tuo armadio un mucchio di piccole scatole con oggetti finali.<\/p>\n<p><img decoding=\"async\" alt=\"Bitmap index in Go: searching at wild speed\" src=\"\/wp-content\/uploads\/2019\/05\/088fed0a2a0feea4a23edbf0ca654805.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nIl terzo approccio \u00e8 quello di eliminare la necessit\u00e0 di ricerca. Questo lo facciamo tramite Bloom filter o cuckoo filter. I primi forniscono una risposta immediata, liberandoti dalla necessit\u00e0 di effettuare ricerche.<\/p>\n<p><img decoding=\"async\" alt=\"Bitmap index in Go: searching at wild speed\" src=\"\/wp-content\/uploads\/2019\/05\/493bc4f20baacc0a5dc0faf15cc46285.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nL'approccio finale consiste nell'utilizzo completo di tutte le potenzialit\u00e0 offerte dall'hardware moderno. \u00c8 esattamente quello che facciamo con gli indici bitmap. S\u00ec, talvolta \u00e8 necessario scorrere tutto l'indice, ma lo facciamo in modo super efficiente.<\/p>\n<p>Come ho gi\u00e0 detto, il tema degli indici del database \u00e8 vasto e pieno di compromessi. Questo significa che a volte possiamo utilizzare pi\u00f9 approcci contemporaneamente: se abbiamo bisogno di accelerare ulteriormente la ricerca o se \u00e8 necessario coprire tutti i possibili tipi di ricerca.<\/p>\n<p>Oggi parler\u00f2 dell'approccio meno conosciuto tra quelli menzionati: gli indici bitmap.<\/p>\n<h2>Chi sono io per parlare di questo argomento?<\/h2>\n<p>\n<img decoding=\"async\" alt=\"Bitmap index in Go: searching at wild speed\" src=\"\/wp-content\/uploads\/2019\/05\/42a9d17a507c392bc202254a92dfaf41.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nLavoro come team leader in Badoo (forse conosci meglio un altro nostro prodotto: Bumble). Abbiamo gi\u00e0 oltre 400 milioni di utenti in tutto il mondo e molte funzionalit\u00e0 che si occupano di trovare per loro la migliore corrispondenza. Lo facciamo tramite servizi personalizzati che utilizzano anche gli indici bitmap.<\/p>\n<h2>Allora, cosa sono gli indici bitmap?<\/h2>\n<p>\n<img decoding=\"async\" alt=\"Bitmap index in Go: searching at wild speed\" src=\"\/wp-content\/uploads\/2019\/05\/14f20f3697a20c02f6b3510dc7f0ae4d.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nGli indici bitmap, come suggerisce il nome, utilizzano bitmap o bitset per implementare un indice di ricerca. Da un punto di vista panoramico, questo indice \u00e8 composto da uno o pi\u00f9 di questi bitmap, rappresentanti entit\u00e0 (come persone) e le loro propriet\u00e0 o parametri (et\u00e0, colore degli occhi, ecc.), e da un algoritmo che utilizza operazioni bitwise (AND, OR, NOT) per rispondere a una query di ricerca.<br \/>\n<img decoding=\"async\" alt=\"Bitmap index in Go: searching at wild speed\" src=\"\/wp-content\/uploads\/2019\/05\/f815720330a51b1f0798e45d23160d3b.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nSi dice che gli indici bitmap siano i pi\u00f9 adatti e molto performanti nei casi in cui ci sia ricerca che combina query su molte colonne con bassa cardinalit\u00e0 (immaginate \u00abcolore degli occhi\u00bb o \u00abstato civile\u00bb rispetto a qualcosa come \u00abdistanza dal centro citt\u00e0\u00bb). Ma pi\u00f9 avanti mostrer\u00f2 che funzionano egregiamente anche per colonne con alta cardinalit\u00e0.<\/p>\n<p>Consideriamo un esempio semplice di indice bitmap.<br \/>\n<img decoding=\"async\" alt=\"Bitmap index in Go: searching at wild speed\" src=\"\/wp-content\/uploads\/2019\/05\/33fe23476e0931c10345d7175b83d68b.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nImmaginate di avere un elenco di ristoranti di Mosca con propriet\u00e0 binarie come queste:<\/p>\n<ul>\n<li>vicino alla metropolitana (near metro);\n<\/li>\n<li>con parcheggio privato (has private parking);\n<\/li>\n<li>con terrazza (has terrace);\n<\/li>\n<li>che accetta prenotazioni (accepts reservations);\n<\/li>\n<li>adatto ai vegetariani (vegan friendly);\n<\/li>\n<li>costoso (expensive).\n<\/li>\n<\/ul>\n<p>\n<img decoding=\"async\" alt=\"Bitmap index in Go: searching at wild speed\" src=\"\/wp-content\/uploads\/2019\/05\/fcbad539ea12f79a9d06966ce308637e.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nAssegniamo un numero a ogni ristorante a partire da 0 e riserviamo memoria per 6 bitmap (uno per ciascuna caratteristica). Poi popoleremo questi bitmap a seconda che il ristorante abbia o meno tale caratteristica. Se il ristorante 4 ha una veranda, il bit n\u00b04 nel bitmap \"ha veranda\" sar\u00e0 impostato su 1 (se non ha veranda, sar\u00e0 0).<br \/>\n<img decoding=\"async\" alt=\"Bitmap index in Go: searching at wild speed\" src=\"\/wp-content\/uploads\/2019\/05\/3709c426617d4364f392ee6b68f92b00.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nOra abbiamo l'indice bitmap pi\u00f9 semplice possibile e possiamo utilizzarlo per rispondere a richieste come:<\/p>\n<ul>\n<li>\"Mostrami i ristoranti adatti ai vegetariani\";\n<\/li>\n<li>\"Mostrami ristoranti economici con veranda dove si pu\u00f2 prenotare un tavolo.\"\n<\/li>\n<\/ul>\n<p><img decoding=\"async\" alt=\"Bitmap index in Go: searching at wild speed\" src=\"\/wp-content\/uploads\/2019\/05\/90acb0f890686bd52c3db1fc667f0b0b.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<img decoding=\"async\" alt=\"Bitmap index in Go: searching at wild speed\" src=\"\/wp-content\/uploads\/2019\/05\/0fd0b7fe9f7b7039022ad5c79fe873bc.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nCome? Vediamo. La prima richiesta \u00e8 molto semplice. Tutto ci\u00f2 che dobbiamo fare \u00e8 prendere il bitmap \"adatto ai vegetariani\" e trasformarlo in un elenco di ristoranti i cui bit sono attivi.<br \/>\n<img decoding=\"async\" alt=\"Bitmap index in Go: searching at wild speed\" src=\"\/wp-content\/uploads\/2019\/05\/ef4a5cfd658ff4ef8bc0c638c4522d11.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<img decoding=\"async\" alt=\"Bitmap index in Go: searching at wild speed\" src=\"\/wp-content\/uploads\/2019\/05\/9cc175bae55c16018fdf5ff95517a61a.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nLa seconda richiesta \u00e8 un po' pi\u00f9 complessa. Dobbiamo utilizzare l'operazione bitwise NOT sulla bitmap 'costoso' per ottenere un elenco di ristoranti economici, poi eseguire un AND con la bitmap 'prenotazione disponibile' e un altro AND con la bitmap 'con veranda'. La bitmap risultante conterr\u00e0 un elenco di attivit\u00e0 che soddisfano tutti i nostri criteri. In questo esempio, \u00e8 solo il ristorante 'Giovent\u00f9'.<br \/>\n<img decoding=\"async\" alt=\"Bitmap index in Go: searching at wild speed\" src=\"\/wp-content\/uploads\/2019\/05\/f1cdda0cbf7f15278553899cf876c17e.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<img decoding=\"async\" alt=\"Bitmap index in Go: searching at wild speed\" src=\"\/wp-content\/uploads\/2019\/05\/126b50e0f622b6e36c461cd74e708c38.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nQui c'\u00e8 molta teoria, ma non preoccupatevi, vedremo il codice molto presto.<\/p>\n<h2>Dove vengono utilizzati gli indici bitmap?<\/h2>\n<p>\n<img decoding=\"async\" alt=\"Bitmap index in Go: searching at wild speed\" src=\"\/wp-content\/uploads\/2019\/05\/406132236c71a4f66ae79957b6e633b3.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nSe fate una ricerca su Google per indici bitmap, il 90% delle risposte sar\u00e0 in qualche modo collegato a Oracle DB. Ma le altre DBMS probabilmente supportano anche questa funzionalit\u00e0, giusto? Non esattamente. <\/p>\n<p>Diamo un'occhiata alla lista dei principali sospetti.<br \/>\n<img decoding=\"async\" alt=\"Bitmap index in Go: searching at wild speed\" src=\"\/wp-content\/uploads\/2019\/05\/d18d66451a8a26b0ddf121f8ec0204cd.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nMySQL non supporta ancora gli indici bitmap, ma esiste una proposta per aggiungere questa opzione (<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 non supporta gli indici bitmap, ma utilizza semplici bitmap e operazioni bitwise per unire i risultati della ricerca su altri indici.<\/p>\n<p>Tarantool ha indici bitset e supporta la ricerca semplice su di essi.<\/p>\n<p>Redis ha semplici campi bitari<noindex><a rel=\"nofollow\" href=\"https:\/\/redis.io\/commands\/bitfield\"> (https:\/\/redis.io\/commands\/bitfield<\/a><\/noindex>) senza possibilit\u00e0 di ricerca su di essi.<\/p>\n<p>MongoDB non supporta ancora gli indici bitmap, ma esiste anche una proposta per aggiungere questa opzione. <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 utilizza bitmap all'interno.<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 index in Go: searching at wild speed\" src=\"\/wp-content\/uploads\/2019\/05\/232d335963603d6e6dec98839fc9f486.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<\/p>\n<ul>\n<li>Ma nella nostra casa \u00e8 arrivato un nuovo vicino: Pilosa. \u00c8 un nuovo database non relazionale, scritto in Go. Contiene solo indici bitmap e basa tutto su di essi. Ne parleremo pi\u00f9 avanti.\n<\/li>\n<\/ul>\n<h2>Implementazione in Go<\/h2>\n<p>\nMa perch\u00e9 gli indici bitmap sono cos\u00ec raramente utilizzati? Prima di rispondere a questa domanda, vorrei mostrarvi l'implementazione di un semplice indice bitmap in Go.<br \/>\n<img decoding=\"async\" alt=\"Bitmap index in Go: searching at wild speed\" src=\"\/wp-content\/uploads\/2019\/05\/ae7f4c1a4a740fe709b05dfaee27ac9b.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nI bitmap sono, in sostanza, semplicemente pezzi di dati. In Go, utilizzeremo per questo degli slice di byte.<\/p>\n<p>Abbiamo un bitmap per ogni caratteristica del ristorante, e ogni bit nel bitmap indica se un determinato ristorante ha quella propriet\u00e0 o meno.<br \/>\n<img decoding=\"async\" alt=\"Bitmap index in Go: searching at wild speed\" src=\"\/wp-content\/uploads\/2019\/05\/1ab628ee15887d6b3c6c99855aa610c8.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nAvremo bisogno di due funzioni ausiliarie. Una sar\u00e0 utilizzata per popolare i nostri bitmap con dati casuali. Casuali, ma con una certa probabilit\u00e0 che il ristorante possieda ciascuna caratteristica. Ad esempio, considero che a Mosca ci siano molto pochi ristoranti in cui non sia possibile prenotare un tavolo, e mi sembra che circa il 20% degli esercizi si adatti ai vegetariani.<\/p>\n<p>La seconda funzione convertir\u00e0 il bitmap in un elenco di ristoranti.<br \/>\n<img decoding=\"async\" alt=\"Bitmap index in Go: searching at wild speed\" src=\"\/wp-content\/uploads\/2019\/05\/ab9c87a0ba63750116e2e7968f842a9a.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<img decoding=\"async\" alt=\"Bitmap index in Go: searching at wild speed\" src=\"\/wp-content\/uploads\/2019\/05\/498cf7a33611d99b90197d4ee82e2834.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nPer rispondere alla richiesta 'Mostrami ristoranti economici che hanno una veranda e dove \u00e8 possibile prenotare un tavolo', avremo bisogno di due operazioni bitwise: NOT e AND.<\/p>\n<p>Possiamo semplificare un po' il nostro codice utilizzando un'operazione pi\u00f9 complessa AND NOT.<\/p>\n<p>Abbiamo funzioni per ciascuna di queste operazioni. Entrambe attraversano gli slice, prendono gli elementi corrispondenti da ognuno, li uniscono con un'operazione bitwise e mettono il risultato nello slice finale.<br \/>\n<img decoding=\"async\" alt=\"Bitmap index in Go: searching at wild speed\" src=\"\/wp-content\/uploads\/2019\/05\/ec5652a34f0370dfb03df199f341f153.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nE ora possiamo utilizzare i nostri bitmap e funzioni per rispondere alla richiesta di ricerca.<br \/>\n<img decoding=\"async\" alt=\"Bitmap index in Go: searching at wild speed\" src=\"\/wp-content\/uploads\/2019\/05\/cf9500ccfe75995a6008191c16729689.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nLe prestazioni non sono cos\u00ec elevate, nonostante le funzioni siano molto semplici e abbiamo risparmiato notevolmente non restituendo un nuovo slice risultante ad ogni chiamata della funzione.<\/p>\n<p>Dopo aver fatto un po' di profiling con pprof, ho notato che il compilatore Go ha trascurato un'ottimizzazione molto semplice, ma fondamentale: l'inlining delle funzioni.<br \/>\n<img decoding=\"async\" alt=\"Bitmap index in Go: searching at wild speed\" src=\"\/wp-content\/uploads\/2019\/05\/9092f5ba3940d0a4f3fbfa90b364d716.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nIl fatto \u00e8 che il compilatore Go ha una pessima paura dei cicli che attraversano gli slice e si rifiuta categoricamente di inlineare funzioni che contengono tali cicli.<br \/>\n<img decoding=\"async\" alt=\"Bitmap index in Go: searching at wild speed\" src=\"\/wp-content\/uploads\/2019\/05\/d1bddd62b61b367dd1f680f223fa0ce7.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nMa io non ho paura e posso ingannare il compilatore utilizzando goto invece di un ciclo, proprio come ai vecchi tempi.<\/p>\n<p><img decoding=\"async\" alt=\"Bitmap index in Go: searching at wild speed\" src=\"\/wp-content\/uploads\/2019\/05\/5b1c3cb26b923972686047910ecd1c31.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<img decoding=\"async\" alt=\"Bitmap index in Go: searching at wild speed\" src=\"\/wp-content\/uploads\/2019\/05\/1ca31d12dc90931b674c6a86a4ea23bd.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nE, come potete vedere, ora il compilatore \u00e8 felice di inlineare la nostra funzione! Alla fine, riusciamo a risparmiare circa 2 microsecondi. Non male!<\/p>\n<p><img decoding=\"async\" alt=\"Bitmap index in Go: searching at wild speed\" src=\"\/wp-content\/uploads\/2019\/05\/bdd6735d16082573e600bffe2cdd5662.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nIl secondo collo di bottiglia \u00e8 facilmente visibile se si esamina attentamente l'output dell'assemblatore. Il compilatore ha aggiunto un controllo sui limiti dello slice direttamente all'interno del nostro ciclo pi\u00f9 critico. Il punto \u00e8 che Go \u00e8 un linguaggio sicuro e il compilatore teme che i miei tre argomenti (tre slice) abbiano dimensioni diverse. Questo comporterebbe la possibilit\u00e0 teorica di un cosiddetto overflow del buffer.<\/p>\n<p>Calmiamo quindi il compilatore, mostrandogli che tutti gli slice hanno la stessa dimensione. Possiamo farlo aggiungendo un semplice controllo all'inizio della nostra funzione.<br \/>\n<img decoding=\"async\" alt=\"Bitmap index in Go: searching at wild speed\" src=\"\/wp-content\/uploads\/2019\/05\/59b4ce687a11dd653e9f12fc3130d89a.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nVedendo questo, il compilatore passa volentieri il controllo e risparmiamo cos\u00ec altri 500 nanosecondi.<\/p>\n<h2>Batch di grandi dimensioni<\/h2>\n<p>\nOk, siamo riusciti a ottenere qualche prestazione dalla nostra semplice implementazione, ma questo risultato \u00e8, in realt\u00e0, molto peggiore di quanto potrebbe essere con l'hardware attuale.<\/p>\n<p>Tutto ci\u00f2 che facciamo sono operazioni bit a bit di base, e i nostri processori le eseguono in modo molto efficiente. Purtroppo, per\u00f2, 'nutriamo' il nostro processore con pezzi di lavoro molto piccoli. Le nostre funzioni eseguono operazioni byte per byte. Possiamo facilmente ottimizzare il nostro codice affinch\u00e9 lavori con pezzi da 8 byte, utilizzando slice di UInt64.<\/p>\n<p><img decoding=\"async\" alt=\"Bitmap index in Go: searching at wild speed\" src=\"\/wp-content\/uploads\/2019\/05\/8d1b60ba4b7046c836601631cadd0b6a.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nCome potete vedere, questa piccola modifica ha accelerato il nostro programma di otto volte grazie all'aumento della dimensione del batch di otto volte. I guadagni sono, si potrebbe dire, lineari.<\/p>\n<p><img decoding=\"async\" alt=\"Bitmap index in Go: searching at wild speed\" src=\"\/wp-content\/uploads\/2019\/05\/0fc663412d04dd38b927de5c8776f69d.jpeg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<h2>Implementazione in assembler<\/h2>\n<p>\n<img decoding=\"async\" alt=\"Bitmap index in Go: searching at wild speed\" src=\"\/wp-content\/uploads\/2019\/05\/b3ef133167b89da983356b1c7389aecd.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nMa non \u00e8 finita qui. I nostri processori possono lavorare con pezzi di 16, 32 e persino 64 byte. Queste 'operazioni larghe' vengono chiamate single instruction multiple data (SIMD; una istruzione, molti dati), e il processo di trasformazione del codice in modo che utilizzi queste operazioni \u00e8 chiamato vettorializzazione.<\/p>\n<p>Sfortunatamente, il compilatore Go non \u00e8 affatto un campione nella vettorializzazione. Attualmente, l'unico modo per vettorizzare il codice in Go \u00e8 prendere e imballare i dati delle operazioni manualmente utilizzando l'assembler Go.<\/p>\n<p><img decoding=\"async\" alt=\"Bitmap index in Go: searching at wild speed\" src=\"\/wp-content\/uploads\/2019\/05\/91278be45df67e1f9572d68fab7ebad1.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nL'assemblatore Go \u00e8 una bestia strana. Sai sicuramente che un assemblatore \u00e8 qualcosa di fortemente legato all'architettura del computer per cui scrivi, ma in Go non \u00e8 cos\u00ec. L'assemblatore Go \u00e8 pi\u00f9 simile a un IRL (linguaggio di rappresentazione intermedia): \u00e8 praticamente indipendente dalla piattaforma. Rob Pike ha fatto un'ottima <noindex><a rel=\"nofollow\" href=\"https:\/\/www.youtube.com\/watch?v=KINIAgRpkDA\">relazione<\/a><\/noindex> presentazione su questo argomento alcuni anni fa al GopherCon di Denver.<\/p>\n<p>In aggiunta, Go utilizza un formato insolito chiamato Plan 9, diverso dai formati comunemente riconosciuti AT&amp;T e Intel.<br \/>\n<img decoding=\"async\" alt=\"Bitmap index in Go: searching at wild speed\" src=\"\/wp-content\/uploads\/2019\/05\/526f75eb18edfc2f851f2725e9d6f69e.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nSi pu\u00f2 dire con certezza che scrivere a mano l'assemblatore Go non \u00e8 l'attivit\u00e0 pi\u00f9 divertente.<\/p>\n<p>Ma, per fortuna, ci sono gi\u00e0 due strumenti di alto livello che ci aiutano a scrivere assemblatore Go: PeachPy e avo. Entruti utilit\u00e0 generano assemblatore Go da codice di livello superiore, scritto rispettivamente in Python e Go.<br \/>\n<img decoding=\"async\" alt=\"Bitmap index in Go: searching at wild speed\" src=\"\/wp-content\/uploads\/2019\/05\/b8a7aa2c585b805e9b1f2ada186b84e8.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nQuesti strumenti semplificano cose come l'allocazione dei registri (scelta dei registri della CPU), la scrittura di cicli e, in generale, rendono pi\u00f9 facile entrare nel mondo della programmazione assemblativa in Go.<\/p>\n<p>Useremo avo, quindi i nostri programmi saranno quasi programmi normali in Go.<br \/>\n<img decoding=\"async\" alt=\"Bitmap index in Go: searching at wild speed\" src=\"\/wp-content\/uploads\/2019\/05\/128e4adef14e5f2cf00fb5b6302ef58e.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nEcco un esempio molto semplice di un programma Avo. Abbiamo una funzione main() che definisce al suo interno la funzione Add(), la cui funzione \u00e8 quella di sommare due numeri. Qui ci sono funzioni ausiliarie per ottenere i parametri per nome e per ottenere uno dei registri di processore liberi e adatti. Ogni operazione del processore ha una corrispondente funzione in Avo, come si pu\u00f2 vedere in ADDQ. Infine, vediamo una funzione ausiliaria per salvare il valore risultante.<br \/>\n<img decoding=\"async\" alt=\"Bitmap index in Go: searching at wild speed\" src=\"\/wp-content\/uploads\/2019\/05\/01ccaa6aa6d394ef598ea2dbc9257d87.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nChiamando go generate, eseguiremo il programma in Avo e alla fine verranno generati due file:<\/p>\n<ul>\n<li>add.s con il codice risultante in Go Assembly;\n<\/li>\n<li>stub.go con le intestazioni delle funzioni per collegare i due mondi: Go e Assembly.\n<\/li>\n<\/ul>\n<p>\n<img decoding=\"async\" alt=\"Bitmap index in Go: searching at wild speed\" src=\"\/wp-content\/uploads\/2019\/05\/72a9443776ecf45eef6fb97a4e08acba.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nOra che abbiamo visto cosa fa e come funziona Avo, guardiamo le nostre funzioni. Ho implementato sia le versioni scalari che quelle vettoriali (SIMD) delle funzioni.<\/p>\n<p>Iniziamo con le versioni scalari.<br \/>\n<img decoding=\"async\" alt=\"Bitmap index in Go: searching at wild speed\" src=\"\/wp-content\/uploads\/2019\/05\/ec31dbb8b97b9d7c1012a120fa18cdaf.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nCome nel precedente esempio, chiediamo di fornire un registro generale libero e corretto, non abbiamo bisogno di calcolare offset e dimensioni per gli argomenti. Tutto questo Avo lo fa per noi.<br \/>\n<img decoding=\"async\" alt=\"Bitmap index in Go: searching at wild speed\" src=\"\/wp-content\/uploads\/2019\/05\/88085e927dd943ea0f808a28fb3ccf9b.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nIn passato utilizzavamo etichette e salti (o goto) per migliorare le prestazioni e ingannare il compilatore Go, ma ora lo facciamo fin dall'inizio. Il fatto \u00e8 che i cicli sono un concetto di livello pi\u00f9 alto. Nell'assembly abbiamo solo etichette e salti.<br \/>\n<img decoding=\"async\" alt=\"Bitmap index in Go: searching at wild speed\" src=\"\/wp-content\/uploads\/2019\/05\/9a4181248eb279d89c1445a820b06649.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nIl resto del codice dovrebbe gi\u00e0 esserti familiare e comprensibile. Emuliamo un ciclo usando etichette e salti, prendiamo una piccola parte dei dati da due dei nostri slice, uniamo i dati con un'operazione bitwise (AND NOT in questo caso) e poi mettiamo il risultato nel nostro slice di output. Tutto qui.<br \/>\n<img decoding=\"async\" alt=\"Bitmap index in Go: searching at wild speed\" src=\"\/wp-content\/uploads\/2019\/05\/19275ead27f63092fc6596bed38a8d03.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nEcco come appare il codice finale in assembly. Non \u00e8 stato necessario calcolare offset e dimensioni (evidenziato in verde) n\u00e9 controllare i registri utilizzati (evidenziato in rosso).<br \/>\n<img decoding=\"async\" alt=\"Bitmap index in Go: searching at wild speed\" src=\"\/wp-content\/uploads\/2019\/05\/ed68528a6f852634a5b536670c6315f0.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nSe confrontiamo le prestazioni dell'implementazione in assembly con quelle della migliore implementazione in Go, vedremo che sono identiche. E questo \u00e8 prevedibile. Infatti, non abbiamo fatto nulla di speciale: abbiamo semplicemente riprodotto ci\u00f2 che avrebbe fatto il compilatore Go.<\/p>\n<p>Sfortunatamente, non possiamo costringere il compilatore a inlinare le nostre funzioni scritte in assembly. Al momento, il compilatore Go non ha questa capacit\u00e0, anche se la richiesta di aggiungerla \u00e8 in circolazione da tempo.<\/p>\n<p>Ecco perch\u00e9 non \u00e8 possibile ottenere vantaggi da piccole funzioni in assembly. Dobbiamo scrivere funzioni pi\u00f9 grandi, utilizzare il nuovo pacchetto math\/bits, oppure evitare l'assembly del tutto.<\/p>\n<p>Ora diamo un'occhiata alle versioni vettoriali delle nostre funzioni.<br \/>\n<img decoding=\"async\" alt=\"Bitmap index in Go: searching at wild speed\" src=\"\/wp-content\/uploads\/2019\/05\/7f67c3cc855908fb47c7900d6e5d7f54.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nPer questo esempio ho deciso di applicare AVX2, quindi utilizzeremo operazioni che lavorano con pezzi da 32 byte. La struttura del codice \u00e8 molto simile a quella della versione scalare: caricamento dei parametri, richiesta di un registro generale libero, e cos\u00ec via.<br \/>\n<img decoding=\"async\" alt=\"Bitmap index in Go: searching at wild speed\" src=\"\/wp-content\/uploads\/2019\/05\/021e0424a0d733ec7db9edeb98ce1f65.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nUna delle novit\u00e0 riguarda il fatto che le operazioni vettoriali pi\u00f9 ampie utilizzano registri speciali pi\u00f9 larghi. Nel caso di pezzi da 32 byte, si tratta dei registri con il prefisso Y. Ecco perch\u00e9 vedi la funzione YMM() nel codice. Se avessi utilizzato AVX-512 con pezzi da 64 bit, il prefisso sarebbe stato Z.<\/p>\n<p>La seconda novit\u00e0 riguarda la mia decisione di utilizzare un'ottimizzazione chiamata loop unrolling, ovvero eseguire manualmente otto operazioni di ciclo prima di tornare all'inizio del ciclo. Questa ottimizzazione riduce il numero di branch nel codice ed \u00e8 limitata al numero di registri liberi disponibili.<br \/>\n<img decoding=\"async\" alt=\"Bitmap index in Go: searching at wild speed\" src=\"\/wp-content\/uploads\/2019\/05\/f54631d9e8c69f6f70ecace3133ae1e8.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nE per quanto riguarda le prestazioni? Sono fantastiche! Abbiamo ottenuto un'accelerazione di circa sette volte rispetto alla migliore soluzione in Go. Impressive, vero?<br \/>\n<img decoding=\"async\" alt=\"Bitmap index in Go: searching at wild speed\" src=\"\/wp-content\/uploads\/2019\/05\/d7e85c933eecb243cfb49225b4d92c6c.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nTuttavia, anche questa implementazione potrebbe essere ulteriormente velocizzata utilizzando AVX-512, prefetching o JIT (just-in-time compiler) per il pianificatore delle richieste. Ma questa \u00e8 senza dubbio una questione per un'altra presentazione.<\/p>\n<h2>Problemi degli indici bitmap<\/h2>\n<p>\nOra che abbiamo esaminato la semplice implementazione dell'indice bitmap in Go e quella molto pi\u00f9 performante in assembly, parliamo finalmente del perch\u00e9 gli indici bitmap siano cos\u00ec poco utilizzati.<br \/>\n<img decoding=\"async\" alt=\"Bitmap index in Go: searching at wild speed\" src=\"\/wp-content\/uploads\/2019\/05\/83d36a5c92ba90fd680fe8afb6cfc11f.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nNei lavori scientifici pi\u00f9 vecchi vengono citati tre problemi degli indici bitmap, ma i lavori scientifici pi\u00f9 recenti e io affermiamo che non sono pi\u00f9 rilevanti. Non scenderemo troppo nel dettaglio di ciascuno di questi problemi, ma li esamineremo superficialmente.<\/p>\n<h2>Il problema dell'alta cardinalit\u00e0<\/h2>\n<p>\nQuindi, ci viene detto che gli indici bitmap sono adatti solo per campi con bassa cardinalit\u00e0, cio\u00e8 quelli con pochi valori (ad esempio, sesso o colore degli occhi), e la ragione \u00e8 che la rappresentazione normale di tali campi (un bit per valore) occuperebbe troppo spazio in caso di alta cardinalit\u00e0 e, inoltre, questi indici bitmap sarebbero scarsamente (raramente) popolati.<br \/>\n<img decoding=\"async\" alt=\"Bitmap index in Go: searching at wild speed\" src=\"\/wp-content\/uploads\/2019\/05\/0787efc4d2cb4ea4d404ca7888b33697.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<img decoding=\"async\" alt=\"Bitmap index in Go: searching at wild speed\" src=\"\/wp-content\/uploads\/2019\/05\/44ab4bc7c25d14fe2f53caf5d9da399d.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nA volte possiamo utilizzare un'altra rappresentazione, ad esempio quella standard che usiamo per rappresentare i numeri. Ma \u00e8 stata l'emergere di algoritmi di compressione a cambiare tutto. Negli ultimi decenni, scienziati e ricercatori hanno ideato un gran numero di algoritmi di compressione per i bitmap. Il loro principale vantaggio \u00e8 che non \u00e8 necessario decomprimere i bitmap per eseguire operazioni bit a bit: possiamo effettuare operazioni bit a bit direttamente sui bitmap compressi.<br \/>\n<img decoding=\"async\" alt=\"Bitmap index in Go: searching at wild speed\" src=\"\/wp-content\/uploads\/2019\/05\/ecb54fbf15aa11271bbbab01ecbda880.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nRecentemente, sono emersi approcci ibridi, come ad esempio i roaring bitmaps. Questi utilizzano contemporaneamente tre rappresentazioni diverse per i bitmaps: i veri e propri bitmaps, gli array e i cosiddetti bit runs, bilanciando tra di loro per massimizzare le prestazioni e minimizzare il consumo di memoria.<\/p>\n<p>Puoi incontrare i roaring bitmaps nelle applicazioni pi\u00f9 popolari. Esistono gi\u00e0 un numero enorme di implementazioni per tanti linguaggi di programmazione, comprese pi\u00f9 di tre implementazioni per Go.<br \/>\n<img decoding=\"async\" alt=\"Bitmap index in Go: searching at wild speed\" src=\"\/wp-content\/uploads\/2019\/05\/de2adfebc431ff48c996247b453f02ae.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nUn altro approccio che pu\u00f2 aiutarci a gestire l'alta cardinalit\u00e0 \u00e8 la raggruppamento (binning). Immagina di avere un campo che rappresenta l'altezza di una persona. L'altezza \u00e8 un numero con la virgola mobile, ma noi, esseri umani, non la vediamo in questo modo. Per noi non c'\u00e8 differenza tra un'altezza di 185,2 cm e una di 185,3 cm.<\/p>\n<p>Di conseguenza, possiamo raggruppare i valori simili in intervalli di 1 cm.<\/p>\n<p>E se sappiamo che molto poche persone hanno un'altezza inferiore a 50 cm e superiore a 250 cm, possiamo, di fatto, trasformare un campo con cardinalit\u00e0 infinita in un campo con circa 200 valori.<\/p>\n<p>Certo, se necessario, possiamo effettuare ulteriori filtri anche in un secondo momento.<\/p>\n<h2>Problema di ampia larghezza di banda<\/h2>\n<p>\nIl problema successivo degli indici bitmap \u00e8 che il loro aggiornamento pu\u00f2 essere molto costoso.<\/p>\n<p>I database devono consentire l'aggiornamento dei dati nel momento in cui potenzialmente centinaia di altre richieste stanno cercando quei dati. Abbiamo bisogno di lock per evitare problemi di accesso concorrente o altri problemi di condivisione. E dove c'\u00e8 un grande lock, c'\u00e8 un problema \u2014 il lock contention, quando quel lock diventa un collo di bottiglia.<br \/>\n<img decoding=\"async\" alt=\"Bitmap index in Go: searching at wild speed\" src=\"\/wp-content\/uploads\/2019\/05\/0ae1bf925542d286f8b7b245c160a35e.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nQuesto problema pu\u00f2 essere risolto o aggirato utilizzando lo sharding o indici versionati.<\/p>\n<p>Lo sharding \u00e8 un concetto semplice e ben noto. Puoi shardingare un indice bitmap proprio come faresti con qualsiasi altro dato. Invece di un grande lock, avrai un mucchio di piccoli lock e in questo modo eliminerai il lock contention.<\/p>\n<p>Un secondo modo per risolvere il problema \u00e8 utilizzare indici versionati. Puoi avere una copia dell'indice che usi per cercare o leggere, e una per scrivere o aggiornare. E a intervalli di tempo prestabiliti (ad esempio, ogni 100 ms o 500 ms) li duplichi e li scambi. Naturalmente, questo approccio \u00e8 applicabile solo quando la tua applicazione pu\u00f2 lavorare con un indice di ricerca leggermente obsoleto.<\/p>\n<p>Questi due approcci possono essere utilizzati contemporaneamente: puoi avere un indice versionato sharded.<\/p>\n<h2>Richieste pi\u00f9 complesse<\/h2>\n<p>L'ultimo problema degli indici bitmap \u00e8 che, come ci dicono, non sono adatti per tipologie di richieste pi\u00f9 complesse, come le richieste 'per intervallo'.<\/p>\n<p>E in effetti, se ci pensi, le operazioni bit a bit come AND, OR ecc. non sono molto adatte per richieste del tipo 'Mostrami gli hotel con tariffe comprese tra 200 e 300 dollari a notte'.<br \/>\n<img decoding=\"async\" alt=\"Bitmap index in Go: searching at wild speed\" src=\"\/wp-content\/uploads\/2019\/05\/7bc2e129cad46fb5875c2b3018c39ff7.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nUna soluzione semplice e molto poco saggia sarebbe quella di prendere i risultati per ogni valore in dollari e combinarli con l'operazione bit a bit OR.<br \/>\n<img decoding=\"async\" alt=\"Bitmap index in Go: searching at wild speed\" src=\"\/wp-content\/uploads\/2019\/05\/b9f8fc7945caa1866f8e0cfa8a04bd98.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nUna soluzione leggermente pi\u00f9 corretta sarebbe stata quella di utilizzare il raggruppamento. Ad esempio, in gruppi da 50 dollari. Questo accelererebbe il nostro processo di 50 volte.<\/p>\n<p>Ma il problema pu\u00f2 essere facilmente risolto utilizzando una rappresentazione creata appositamente per questo tipo di query. Nelle pubblicazioni scientifiche viene chiamata bitmap a intervallo codificato.<br \/>\n<img decoding=\"async\" alt=\"Bitmap index in Go: searching at wild speed\" src=\"\/wp-content\/uploads\/2019\/05\/644a420628b21f220a7af1ff15c4031f.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nIn questa rappresentazione, non assegniamo semplicemente un bit a un valore specifico (ad esempio, 200), ma segnaliamo quel valore e tutto ci\u00f2 che viene dopo. 200 e oltre. Lo stesso per 300: 300 e oltre. E cos\u00ec via.<\/p>\n<p>Utilizzando questa rappresentazione, possiamo rispondere a questo tipo di query di ricerca passando attraverso l'indice solo due volte. Prima otteniamo un elenco di hotel dove il prezzo \u00e8 inferiore a 300 dollari, e poi eliminiamo quelli dove il prezzo \u00e8 inferiore a 199 dollari. Fatto.<br \/>\n<img decoding=\"async\" alt=\"Bitmap index in Go: searching at wild speed\" src=\"\/wp-content\/uploads\/2019\/05\/cc2bb58d7d7a51495c62ec7da81e2d12.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nPotreste rimanere sorpresi, ma anche le query geografiche sono possibili utilizzando gli indici bitmap. Il trucco consiste nell'utilizzare una rappresentazione geospaziale che circonda le vostre coordinate con una figura geometrica. Ad esempio, S2 di Google. La figura dovrebbe essere rappresentabile tramite tre o pi\u00f9 linee incrociate, che possono essere numerate. In questo modo, possiamo trasformare la nostra query geospaziale in pi\u00f9 richieste \"per intervallo\" (su queste linee numerate).<\/p>\n<h2>Soluzioni pronte<\/h2>\n<p>\nSpero di aver suscitato un po' il vostro interesse e che ora abbiate uno strumento utile nel vostro arsenale. Se mai un giorno vi servir\u00e0 fare qualcosa di simile, saprete dove guardare.<\/p>\n<p>Tuttavia, non tutti hanno il tempo, la pazienza e le risorse per creare indici bitmap da zero. Soprattutto quelli pi\u00f9 avanzati, utilizzando SIMD, per esempio.<\/p>\n<p>Fortunatamente, ci sono alcune soluzioni pronte che possono aiutarvi.<br \/>\n<img decoding=\"async\" alt=\"Bitmap index in Go: searching at wild speed\" src=\"\/wp-content\/uploads\/2019\/05\/817b47cb189a758756b602ec9cf319e1.jpeg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<h2>Bitmap Roaring<\/h2>\n<p>\nInnanzitutto, c'\u00e8 quella famosissima libreria roaring bitmaps di cui ho gi\u00e0 parlato. Essa include tutti i contenitori e le operazioni sui bit necessari per creare un indice bitmap completo.<br \/>\n<img decoding=\"async\" alt=\"Bitmap index in Go: searching at wild speed\" src=\"\/wp-content\/uploads\/2019\/05\/57f61a51485c174666b52dc2063fabe8.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nSfortunatamente, al momento nessuna delle implementazioni in Go utilizza SIMD, il che significa che le implementazioni in Go sono meno performanti rispetto a quelle in C, ad esempio.<\/p>\n<h2>Pilosa<\/h2>\n<p>\nUn altro prodotto che pu\u00f2 esserti utile \u00e8 il database Pilosa, che sostanzialmente si basa esclusivamente su indici bitmap. \u00c8 una soluzione relativamente nuova, ma sta rapidamente conquistando il mercato.<br \/>\n<img decoding=\"async\" alt=\"Bitmap index in Go: searching at wild speed\" src=\"\/wp-content\/uploads\/2019\/05\/d3870a979e1e73d093fe5d5e9bb71cd8.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nPilosa utilizza bitmap roaring al suo interno e ti permette di utilizzarli, semplificando e chiarendo tutte quelle cose di cui ho parlato sopra: raggruppamento, bitmap codificati in intervalli, concetto di campo, e cos\u00ec via.<\/p>\n<p>Diamo rapidamente un'occhiata a un esempio di utilizzo di Pilosa per rispondere a una domanda che gi\u00e0 conosci.<br \/>\n<img decoding=\"async\" alt=\"Bitmap index in Go: searching at wild speed\" src=\"\/wp-content\/uploads\/2019\/05\/dfee7abfd653cc19d9aa8b64c64f3e4e.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nL'esempio \u00e8 molto simile a quello che hai visto in precedenza. Creiamo un client per il server Pilosa, creiamo un indice e i campi necessari, quindi popolare i nostri campi con dati casuali secondo probabilit\u00e0 e, infine, eseguiamo una richiesta familiare.<\/p>\n<p>Dopo di ci\u00f2, utilizziamo NOT sul campo \"expensive\", quindi incrociamo il risultato (o facciamo un AND) con il campo \"terrace\" e con il campo \"reservations\". Infine, otteniamo il risultato finale.<br \/>\n<img decoding=\"async\" alt=\"Bitmap index in Go: searching at wild speed\" src=\"\/wp-content\/uploads\/2019\/05\/8d66c6d68019c2297b6c15b700f06a3a.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nSpero davvero che in un futuro prossimo anche nei database come MySQL e PostgreSQL arrivi questo nuovo tipo di indici: gli indici bitmap.<br \/>\n<img decoding=\"async\" alt=\"Bitmap index in Go: searching at wild speed\" src=\"\/wp-content\/uploads\/2019\/05\/7a8e33d576c7fb6376a173ee038b4206.jpeg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<h2>Conclusione<\/h2>\n<p>\n<img decoding=\"async\" alt=\"Bitmap index in Go: searching at wild speed\" src=\"\/wp-content\/uploads\/2019\/05\/c62caa9ad6f2d96056c80326f4fa9a0d.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nSe non vi siete ancora addormentati, grazie. Ho dovuto toccare molti argomenti per motivi di tempo limitato, ma spero che la presentazione sia stata utile e forse anche motivante.<\/p>\n<p>\u00c8 bene conoscere gli indici bitmap, anche se al momento non vi servono. Sono un ulteriore strumento nella vostra cassetta degli attrezzi.<\/p>\n<p>Abbiamo esaminato vari trucchi per migliorare le prestazioni in Go e le cose con cui il compilatore Go non riesce ancora a gestire bene. Questa \u00e8 sicuramente una conoscenza utile per ogni programmatore Go.<\/p>\n<p>Questo \u00e8 tutto ci\u00f2 che volevo condividere. Grazie!<br \/>\n<br \/>Fonte: <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 4.9.10 - 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 \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,\" \/>\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\/it\/blog\/administrirovanie\/bitmap-indeksy-v-go-poisk-na-dikoj-skorosti\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 4.9.10\" \/>\n\t\t<meta property=\"og:locale\" content=\"it_IT\" \/>\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 \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,\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/it\/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\udd47Indici Bitmap in Go: ricerca ad alta velocit\u00e0 | ProHoster","description":"Introduzione Ho presentato questa relazione in inglese alla conferenza GopherCon Russia 2019 a Mosca e in russo durante il meetup a Nizhny Novgorod. Si tratta di un indice bitmap, meno comune rispetto al B-tree, ma altrettanto interessante. Condivido la registrazione della mia presentazione alla conferenza in inglese e la trascrizione testuale in russo. Esamineremo","canonical_url":"https:\/\/prohoster.info\/it\/blog\/administrirovanie\/bitmap-indeksy-v-go-poisk-na-dikoj-skorosti","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"it_IT","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 \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,","og:url":"https:\/\/prohoster.info\/it\/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"},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/posts\/33793","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/comments?post=33793"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/posts\/33793\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/media\/25469"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/media?parent=33793"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/categories?post=33793"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/tags?post=33793"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}