Uvod u Guava Memoizer

1. Pregled

U ovom uputstvu istražit ćemo značajke pamćenja Googleove biblioteke Guava.

Memoizacija je tehnika kojom se izbjegava opetovano izvršavanje računski skupe funkcije predmemoriranjem rezultata prvog izvršavanja funkcije.

1.1. Memoizacija nasuprot predmemoriranju

Memoriranje je slično predmemoriranju s obzirom na memoriju. Obje tehnike pokušavaju povećati učinkovitost smanjenjem broja poziva računski skupom kodu.

Međutim, dok je predmemoriranje općenitiji pojam koji rješava problem na razini instancije klase, dohvata objekta ili dohvata sadržaja, memoizacija rješava problem na razini izvršenja metode / funkcije.

1.2. Guava Memoizer i Guava Cache

Guava podržava i pamćenje i predmemoriranje. Memoizacija se odnosi na funkcije bez argumenta (Dobavljač) i funkcionira s točno jednim argumentom (Funkcija). Dobavljač i Funkcija ovdje se odnose na Guava funkcionalna sučelja koja su izravna potklasa Java 8 Funkcionalnih API sučelja s istim imenima.

Od verzije 23.6, Guava ne podržava memoriranje funkcija s više od jednog argumenta.

API-je za zapamćivanje možemo nazvati na zahtjev i odrediti politiku deložacije koja kontrolira broj unosa koji se čuvaju u memoriji i sprječava nekontrolirani rast memorije koja se koristi izbacivanjem / uklanjanjem unosa iz predmemorije nakon što se podudara s uvjetima politike.

Zabilješka koristi predmemoriju Guava; za detaljnije informacije o predmemoriji Guava, pogledajte naš članak predmemorije Guava.

2. Dobavljač Memoizacija

Postoje dvije metode u Dobavljači klasa koja omogućuje pamćenje: pamtiti, i memoizeWithExpiration.

Kada želimo izvršiti memoriranu metodu, možemo jednostavno nazvati dobiti metoda vraćenih Dobavljač. Ovisno o tome postoji li povratna vrijednost metode u memoriji, dobiti metoda će vratiti vrijednost u memoriji ili će izvršiti memoriranu metodu i proslijediti povratnu vrijednost pozivatelju.

Istražimo svaku metodu Dobavljač‘S memoizacija.

2.1. Dobavljač Memoizacija bez deložacije

Možemo koristiti Dobavljačipamtiti metodu i navedite delegirano Dobavljač kao referenca metode:

Dobavljač memoizedSupplier = Dobavljači.memoize (CostlySupplier :: generirajBigNumber);

Budući da nismo odredili politiku deložacije, jednom dobiti poziva metoda, vraćena vrijednost će se zadržati u memoriji dok Java aplikacija još uvijek radi. Bilo koji poziv na dobiti nakon početnog poziva vratit će zapamćenu vrijednost.

2.2. Dobavljač Memoizacija deložacijom prema vremenu provedenom u životu (TTL)

Pretpostavimo da povratnu vrijednost želimo zadržati samo iz Dobavljač u dopisu na određeno vrijeme.

Možemo koristiti DobavljačimemoizeWithExpiration metodu i navedite vrijeme isteka s pripadajućom vremenskom jedinicom (npr. sekunda, minuta), uz preneseno Dobavljač:

Dobavljač memoizedSupplier = Suppliers.memoizeWithExpiration (CostlySupplier :: generirajBigNumber, 5, TimeUnit.SECONDS);

Nakon što protekne određeno vrijeme (5 sekundi), predmemorija će izbaciti vraćenu vrijednost datoteke Dobavljač iz sjećanja i svaki sljedeći poziv na dobiti metoda će se ponovno izvršiti generiratiBigNumber.

Za detaljnije informacije, molimo pogledajte Javadoc.

2.3. Primjer

Simulirajmo računski skupu metodu imenovanu generiratiBigNumber:

javna klasa CostlySupplier {private static BigInteger generirajBigNumber () {try {TimeUnit.SECONDS.sleep (2); } catch (InterruptedException e) {} return new BigInteger ("12345"); }}

Našoj primjeru metode će trebati 2 sekunde da se izvrši, a zatim vrati a BigInteger proizlaziti. Mogli bismo ga zapamtiti koristeći bilo koji pamtiti ili memoizeWithExpiration Apis.

Radi jednostavnosti izostavit ćemo politiku deložacije:

@Test public void givenMemoizedSupplier_whenGet_thenSubsequentGetsAreFast () {Dobavljač memoizedSupplier; memoizedSupplier = Dobavljači.memoize (CostlySupplier :: generirajBigNumber); BigInteger očekujeValue = novi BigInteger ("12345"); assertSupplierGetExecutionResultAndDuration (memoizedSupplier, očekivana vrijednost, 2000D); assertSupplierGetExecutionResultAndDuration (memoizedSupplier, očekivana vrijednost, 0D); assertSupplierGetExecutionResultAndDuration (memoizedSupplier, očekivana vrijednost, 0D); } private void assertSupplierGetExecutionResultAndDuration (dobavljač dobavljača, T očekujeValue, dvostruko očekujeDurationInMs) {Instant start = Instant.now (); T vrijednost = dobavljač.get (); Long durationInMs = Duration.između (start, Instant.now ()). ToMillis (); dvostruka marginOfErrorInMs = 100D; assertThat (vrijednost, je (jednakTo (očekivanaVrijednost))); assertThat (durationInMs.doubleValue (), is (closeTo (očekuje seDurationInMs, marginOfErrorInMs))); }

Prvi dobiti poziv metode traje dvije sekunde, kao što je simulirano u generiratiBigNumber metoda; međutim, naknadni pozivi na dobiti() izvršit će se znatno brže, budući da generiratiBigNumber rezultat je zapamćen.

3. Funkcija Memoizacija

Za pamćenje metode koja uzima jedan argument mi izgraditi a LoadingCache karta pomoću CacheLoader‘S iz metodu kako bi graditelj dao našu metodu kao Guavu Funkcija.

LoadingCache je istodobna karta s vrijednostima koje automatski učitava CacheLoader.CacheLoader popunjava kartu računanjem Funkcija navedeno u iz metoda, i stavljanje vraćene vrijednosti u LoadingCache. Za detaljnije informacije, molimo pogledajte Javadoc.

LoadingCacheKljuč je Funkcija'S argument / unos, dok je vrijednost karte: FunkcijaVraćena vrijednost:

LoadingCache memo = CacheBuilder.newBuilder () .build (CacheLoader.from (FibonacciSequence :: getFibonacciNumber));

Od LoadingCache je istodobna karta, ne dopušta null ključeve ili vrijednosti. Stoga moramo osigurati da Funkcija ne podržava null kao argument ili vraća null vrijednosti.

3.1. Funkcija Memoizacija politikama deložacije

Možemo primijeniti različite politike iseljavanja iz Guava Cachea kada zapamtimo a Funkcija kako je spomenuto u odjeljku 3 članka Guava Cache.

Na primjer, možemo izbaciti unose koji su bili u mirovanju 2 sekunde:

LoadingCache memo = CacheBuilder.newBuilder () .expireAfterAccess (2, TimeUnit.SECONDS) .build (CacheLoader.from (Fibonacci :: getFibonacciNumber));

Dalje, pogledajmo dva slučaja upotrebe Funkcija memoizacija: Fibonaccijev niz i faktorijel.

3.2. Primjer Fibonaccijeve sekvence

Možemo rekurzivno izračunati Fibonaccijev broj iz određenog broja n:

javni statički BigInteger getFibonacciNumber (int n) {if (n == 0) {return BigInteger.ZERO; } else if (n == 1) {return BigInteger.ONE; } else {return getFibonacciNumber (n - 1) .add (getFibonacciNumber (n - 2)); }}

Bez memoriranja, kada je ulazna vrijednost relativno visoka, izvršavanje funkcije bit će sporo.

Da bismo poboljšali učinkovitost i performanse, možemo pamtiti getFibonacciNumber koristeći CacheLoader i CacheBuilder, navodeći politiku iseljavanja ako je potrebno.

U sljedećem primjeru uklanjamo najstariji unos nakon što veličina bilješke dosegne 100 unosa:

javna klasa FibonacciSequence {private static LoadingCache memo = CacheBuilder.newBuilder () .maximumSize (100) .build (CacheLoader.from (FibonacciSequence :: getFibonacciNumber)); javni statički BigInteger getFibonacciNumber (int n) {if (n == 0) {return BigInteger.ZERO; } else if (n == 1) {return BigInteger.ONE; } else {vratiti memo.getUn Check (n - 1) .add (memo.getUn Check (n - 2)); }}}

Evo, koristimo getUn провеreno metoda koja vraća vrijednost ako postoji bez bacanja provjerene iznimke.

U ovom slučaju ne trebamo izričito rukovati iznimkom prilikom specificiranja getFibonacciNumber referenca metode u CacheLoader‘S iz poziv metode.

Za detaljnije informacije, molimo pogledajte Javadoc.

3.3. Faktorijalni primjer

Dalje, imamo još jednu rekurzivnu metodu koja izračunava faktorijel zadane ulazne vrijednosti, n:

javni statički BigInteger getFactorial (int n) {if (n == 0) {return BigInteger.ONE; } else {return BigInteger.valueOf (n) .multiply (getFactorial (n - 1)); }}

Učinkovitost ove implementacije možemo poboljšati primjenom memoizacije:

javna klasa Factorial {private static LoadingCache memo = CacheBuilder.newBuilder () .build (CacheLoader.from (Factorial :: getFactorial)); javni statički BigInteger getFactorial (int n) {if (n == 0) {return BigInteger.ONE; } else {return BigInteger.valueOf (n) .multiply (memo.getUn провеreno (n - 1)); }}}

4. Zaključak

U ovom smo članku vidjeli kako Guava nudi API-je za izvršavanje pamćenja Dobavljač i Funkcija metode. Također smo pokazali kako odrediti politiku deložacije rezultata pohranjene funkcije u memoriju.

Kao i uvijek, izvorni kod možete pronaći na GitHubu.


$config[zx-auto] not found$config[zx-overlay] not found