Skip to content

Instantly share code, notes, and snippets.

@kaja47
Last active December 19, 2015 12:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kaja47/5954091 to your computer and use it in GitHub Desktop.
Save kaja47/5954091 to your computer and use it in GitHub Desktop.

Můj problém je jednoduchý: mám pole doublů a potřebuji top-k hodnot a jejich indexů.

Top-k vrátí k největších elementů, které jsou větší než nějaké minimum. Funkce topKEstaimateMin velice rychle odhadne tohle minimum (jednou sekvenčně projde pole a ke cache se chová naprosto ideálně). Odhad nebude skutečné minimum, ale bude o něco menší, tedy vybere víc než k elementů. Ale zároveň garantuje, že nebude větší něž skutečné minimum. Je to tedy dolní mez pro minimum.

Pracuje tak, že rozhazuje elementy do k bucketů (prakticky je to nejbližší mocnina dvou větší než k, protože pak můžu použít maskování bitů, které je mnohem rychlejší než modulo) a počítá jejich maxima. Z těchto maxim pak vezmu minimum, u kterého je garantováno, že je menší nebo rovno než k (nebo více) čísel ze zdrojového pole a v ideálním je poměrně blízko skutečného minima (v mých testech se ukazuje, že pro n=340000 a k=1000, to vrátí minimum pro 6000 čísel, tedy odfiltruje 98% hodnot).

Dá se to dělat taky obráceně, jak maximum minim, ale to se mi zdá, že nebude tak účinné.

Když mám tohle odhadnuté minimum ještě jednou projdu zdrojovým polem a normálně počítám Top-K pomocí hald nebo binárních vyhledávacích stromů, ale věnuji se jenom elementům, které jsou větší než moje odhadnuté minimum + udržuji průběžné minimum.

Řešení s haldami nebo binárními stromy má dobrou asymptotickou složitost, ale jeho konstantní faktory jsou příliš veliké. Do značné míry jde o limitaci Javy a autoboxingu.

Obě řešení (odhadnout minimum + sort + top-k a haldy) jsou přibližně stejně rychlé, ale když se zkombinují dohromady, tak je jejich rychlost naprosto neuvěřitelná.


A potom, co jsem to naprogramoval, tak jsem si matně vzpomněl na jeden paper, který se mi kdysi dostal do ruky, kde se zmínili o podobné myšlence. Ale nepoužívali to pro konstrukci algoritmu pro odhad minima, ale pro důkaz ohledně časových složitostí. Kdybych si jenom vzpomněl, co to bylo za paper.

@jsuchal
Copy link

jsuchal commented Jul 15, 2013

Aha jasne. Super finta. Jedine co ma trochu strasi je potreba 2x zbehnut cez cele pole "kandidatov".

@kaja47
Copy link
Author

kaja47 commented Jul 17, 2013

Toho bych se vůbec nebál. DDR3 1333 umí číst nějakých 10.5GB/s takže pole jednoho milionu kandidátů (které má 8MB) to načte za 0.76ms, ale tenhle čas se na výsledku skoro vůbec neprojeví, protože načítání běží na pozadí maskované výpočtem. Když pole procházím sekvenčně, CPU to pozná a začne data z paměti načítat dopředu, takže vždycky budou k dispozici bez čekání a bez latence a bez cache-miss (které můžou stát cca 300 cyklů procesoru, pravda, nějaké budu na začátku a na rozhraní stránek paměti). Smyčka, která odhaduje minimum obsahuje jenom pár rychlých (and místo modula) instrukcí a některé můžou být vykonávány paralelně v CPU (Haswell má 8 portů, takže může dělat najednou 8 instrukcí per jádro).

Čísla mluví za vše:

  • binární vyhledávací strom + min filter: 12 - 15ms
  • binární vyhledávací strom + min filter nastavený na odhadnuté maximum: 2 - 3 ms včetně odhadu

@jsuchal
Copy link

jsuchal commented Jul 17, 2013

@kaja47: To sme sa asi nepochopili, o rychlosti nepochybujem, skor som mal na mysli to, ze to pole kandidatov si musis niekde v pamati drzat, zatial co pri single pass to pole kandidatov moze byt "lazy" a mozes ho rovno pocas hladania top-k zahadzovat. V kazdom pripade zaujimava finta.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment