Optymalizowanie szybkości wnioskowania za pomocą buforowania prefiksów

Buforowanie prefiksów to funkcja, która skraca czas wnioskowania przez przechowywanie i ponowne wykorzystywanie pośredniego stanu LLM podczas przetwarzania wspólnego i powtarzającego się prefiksu prompta. Aby włączyć buforowanie prefiksów, wystarczy oddzielić statyczny prefiks od dynamicznego sufiksu w żądaniu do interfejsu API.

Pamięć podręczna prefiksów obsługuje obecnie tylko dane wejściowe w postaci tekstu, więc nie należy używać tej funkcji, jeśli w prompcie podajesz obraz.

Pamięć podręczną prefiksów można wdrożyć na 2 sposoby: niejawny i jawny:

Używanie buforowania prefiksów w sposób niejawny

Aby włączyć buforowanie prefiksów, dodaj wspólną część promptu do pola promptPrefix, jak pokazano w tych fragmentach kodu:

Kotlin

val promptPrefix = "Reverse the given sentence: "
val dynamicSuffix = "Hello World"

val result = generativeModel.generateContent(
  generateContentRequest(TextPart(dynamicSuffix)) {
    promptPrefix = PromptPrefix(promptPrefix)
  }
)

Java

String promptPrefix = "Reverse the given sentence: ";
String dynamicSuffix = "Hello World";

GenerateContentResponse response = generativeModelFutures.generateContent(
    new GenerateContentRequest.Builder(new TextPart(dynamicSuffix))
    .setPromptPrefix(new PromptPrefix(promptPrefix))
    .build())
    .get();

W poprzednim fragmencie kodu element dynamicSuffix jest przekazywany jako główna treść, a element promptPrefix jest podawany oddzielnie.

Szacowane zwiększenie wydajności

Bez buforowania prefiksów

Z prefiksem w pamięci podręcznej

(Brak w pamięci podręcznej prefiksu może wystąpić, gdy prefiks jest używany po raz pierwszy).

Pixel 9 ze stałym prefiksem o długości 300 tokenów i dynamicznym sufiksem o długości 50 tokenów prompt

0,82 sekundy

0,45 sekundy

Pixel 9 z promptem zawierającym stały prefiks o długości 1000 tokenów i dynamiczny sufiks o długości 100 tokenów

2,11 sekundy

0,5 sekundy

Miejsce na dane

W przypadku buforowania z niejawnym prefiksem pliki pamięci podręcznej są zapisywane w pamięci prywatnej aplikacji klienckiej, co zwiększa wykorzystanie pamięci przez aplikację. Przechowywane są zaszyfrowane pliki pamięci podręcznej i powiązane z nimi metadane, w tym oryginalny tekst prefiksu. Pamiętaj o tych kwestiach związanych z pamięcią:

  • Liczba pamięci podręcznych jest zarządzana przez mechanizm LRU (Least Recently Used). Najrzadziej używane pamięci podręczne są automatycznie usuwane po przekroczeniu maksymalnej łącznej ilości pamięci podręcznej.
  • Rozmiary pamięci podręcznej promptów zależą od długości prefiksu.
  • Aby wyczyścić wszystkie pamięci podręczne utworzone na podstawie buforowania prefiksów, użyj metody generativeMode.clearImplicitCaches().

Używanie jawnego zarządzania pamięcią podręczną

Interfejs Prompt API zawiera metody jawnego zarządzania pamięcią podręczną, aby zapewnić deweloperom większą kontrolę nad sposobem tworzenia, przeszukiwania, używania i usuwania pamięci podręcznych. Te operacje ręczne są wykonywane niezależnie od automatycznego zarządzania pamięcią podręczną przez system.

Ten przykład pokazuje, jak zainicjować jawne zarządzanie pamięcią podręczną i przeprowadzić wnioskowanie:

Kotlin

val cacheName = "my_cache"
val promptPrefix = "Reverse the given sentence: "
val dynamicSuffix = "Hello World"

// Create a cache
val cacheRequest = createCachedContextRequest(cacheName, PromptPrefix(promptPrefix))
val cache = generativeModel.caches.create(cacheRequest)

// Run inference with the cache
val response = generativeModel.generateContent(
  generateContentRequest(TextPart(dynamicSuffix)) {
    cachedContextName = cache.name
  }
)

Java

String cacheName = "my_cache";
String promptPrefix = "Reverse the given sentence: ";
String dynamicSuffix = "Hello World";

// Create a cache
CachedContext cache = cachesFutures.create(
  new CreateCachedContextRequest.Builder(cacheName, new PromptPrefix(promptPrefix))
  .build())
  .get();

// Run inference with the cache
GenerateContentResponse response = generativeModelFutures.generateContent(
  new GenerateContentRequest.Builder(new TextPart(dynamicSuffix))
  .setCachedContextName(cache.getName())
  .build())
  .get();

Ten przykład pokazuje, jak za pomocą generativeModel.caches wysyłać zapytania dotyczące pamięci podręcznych zarządzanych jawnie, pobierać je i usuwać:

Kotlin

val cacheName = "my_cache"

// Query pre-created caches
for (cache in generativeModel.caches.list()) {
  // Do something with cache
}

// Get specific cache
val cache = generativeModel.caches.get(cacheName)

// Delete a pre-created cache
generativeModel.caches.delete(cacheName)

Java

String cacheName = "my_cache";

// Query pre-created caches
for (PrefixCache cache : cachesFutures.list().get()) {
  // Do something with cache
}

// Get specific cache
PrefixCache cache = cachesFutures.get(cacheName).get();

// Delete a pre-created cache
cachesFutures.delete(cacheName);