Obecnie Twój kod korzysta z synchronicznego API (StringSet
) i jest ładowany przez 10 wątków jednocześnie. Nie będzie to stanowić dużego wyzwania dla SE.Redis - tutaj działa dobrze. podejrzewam że rzeczywiście jest to limit czasu, w którym serwer zabrał więcej czasu, niż chciałbyś przetworzyć niektóre dane, najprawdopodobniej również związane z alokatorem serwera. Jedną z opcji jest zatem po prostu nieznaczne zwiększenie limitu czasu . Niewiele... spróbuj 5 sekund zamiast domyślnej 1 sekundy. Prawdopodobnie większość operacji i tak działa bardzo szybko.
Jeśli chodzi o przyspieszenie:jedną z opcji jest nie czekać - tj. zachować dane dotyczące potoku. Jeśli nie chcesz sprawdzać każdej wiadomości pod kątem stanu błędu, jednym prostym sposobem, aby to zrobić, jest dodanie , flags: CommandFlags.FireAndForget
na końcu twojego StringSet
połączenie. W moich lokalnych testach przyspieszyło to przykład 1M o 25% (i podejrzewam, że większość czasu spędza się na serializacji ciągów).
Największym problemem, jaki miałem z przykładem 10M, był po prostu narzut związany z pracą z przykładem 10M - zwłaszcza, że zajmuje to ogromne ilości pamięci zarówno dla redis-server
oraz aplikacja, która (aby emulować twoją konfigurację) znajduje się na tym samym komputerze. Tworzy to konkurencyjne ciśnienie pamięci, z przerwami GC itp. w kodzie zarządzanym. Ale co może ważniejsze:po prostu potrzeba wieczności, aby zacząć cokolwiek robić . W związku z tym zrefaktorowałem kod, aby używał równoległego yield return
generatory zamiast pojedynczej listy. Na przykład:
static IEnumerable<Person> InventPeople(int seed, int count)
{
for(int i = 0; i < count; i++)
{
int f = 1 + seed + i;
var item = new Person
{
Id = f,
Name = Path.GetRandomFileName().Replace(".", "").Substring(0, appRandom.Value.Next(3, 6)) + " " + Path.GetRandomFileName().Replace(".", "").Substring(0, new Random(Guid.NewGuid().GetHashCode()).Next(3, 6)),
Age = f % 90,
Friends = ParallelEnumerable.Range(0, 100).Select(n => appRandom.Value.Next(1, f)).ToArray()
};
yield return item;
}
}
static IEnumerable<T> Batchify<T>(this IEnumerable<T> source, int count)
{
var list = new List<T>(count);
foreach(var item in source)
{
list.Add(item);
if(list.Count == count)
{
foreach (var x in list) yield return x;
list.Clear();
}
}
foreach (var item in list) yield return item;
}
z:
foreach (var element in InventPeople(PER_THREAD * counter1, PER_THREAD).Batchify(1000))
Tutaj cel Batchify
jest zapewnienie, że nie pomagamy zbytnio serwerowi, poświęcając znaczną ilość czasu między każdą operacją - dane są tworzone w partiach po 1000, a każda partia jest udostępniana bardzo szybko.
Martwiłem się również o wydajność JSON, więc przerzuciłem się na JIL:
public static string ToJSON<T>(this T obj)
{
return Jil.JSON.Serialize<T>(obj);
}
a potem dla zabawy przeniosłem pracę JSON do przetwarzania wsadowego (tak, aby rzeczywiste pętle przetwarzania:
foreach (var element in InventPeople(PER_THREAD * counter1, PER_THREAD)
.Select(x => new { x.Id, Json = x.ToJSON() }).Batchify(1000))
To skróciło czasy nieco bardziej, więc mogę załadować 10M w 3 minuty i 57 sekund, w tempie 42 194 rzutów. Większość tego czasu to w rzeczywistości przetwarzanie lokalne w aplikacji. Jeśli zmienię to tak, że każdy wątek ładuje tak samo item ITEMS / THREADS
razy, potem zmienia się to na 1 minutę 48 sekund – w tempie 92 592 skoków.
Nie jestem pewien, czy naprawdę na cokolwiek odpowiedziałem, ale krótka wersja może brzmieć po prostu „spróbuj dłuższego czasu oczekiwania; rozważ użycie opcji „uruchom i zapomnij”.