V současné době váš kód používá synchronní API (StringSet
) a je načítáno 10 vlákny současně. Pro SE.Redis to nepředstavuje žádnou výraznou výzvu – zde to funguje dobře. Mám podezření že se skutečně jedná o časový limit, kdy serveru trvalo déle, než byste chtěli zpracovat některá data, s největší pravděpodobností také související s alokátorem serveru. Jednou z možností je tedy jednoduše trochu prodloužit časový limit . Ne moc... zkuste 5 sekund místo výchozí 1 sekundy. Většina operací pravděpodobně stejně funguje velmi rychle.
Pokud jde o urychlení:jednou z možností je nečekat - tj. uchovávat data z potrubí. Pokud jste spokojeni s tím, že nekontrolujete chybový stav každé jednotlivé zprávy, pak jednoduchým způsobem, jak to udělat, je přidat , flags: CommandFlags.FireAndForget
na konec vaší StringSet
volání. Při mém místním testování to urychlilo příklad 1M o 25 % (a mám podezření, že spoustu zbytku času ve skutečnosti stráví serializací řetězců).
Největší problém, který jsem měl s příkladem 10 milionů, byla prostě režie práce s příkladem 10 milionů - zvláště proto, že to zabírá obrovské množství paměti pro oba redis-server
a aplikace, které (pro emulaci vašeho nastavení) jsou na stejném počítači. To vytváří konkurenční tlak na paměť s GC pauzami atd. ve spravovaném kódu. Ale možná ještě důležitější:začít cokoliv dělat trvá prostě věčnost . Následně jsem přefaktoroval kód tak, aby používal paralelní yield return
generátory spíše než jeden seznam. Například:
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;
}
s:
foreach (var element in InventPeople(PER_THREAD * counter1, PER_THREAD).Batchify(1000))
Zde je účel Batchify
je zajistit, abychom serveru příliš nepomáhali tím, že bychom mezi jednotlivými operacemi zabírali značný čas – data jsou vynalezena v dávkách po 1000 a každá dávka je zpřístupněna velmi rychle.
Také jsem měl obavy o výkon JSON, tak jsem přešel na JIL:
public static string ToJSON<T>(this T obj)
{
return Jil.JSON.Serialize<T>(obj);
}
a pak jsem jen pro zábavu přesunul práci JSON do dávkování (takže skutečné zpracování zacyklí:
foreach (var element in InventPeople(PER_THREAD * counter1, PER_THREAD)
.Select(x => new { x.Id, Json = x.ToJSON() }).Batchify(1000))
Tím se časy o něco zkrátily, takže mohu načíst 10M za 3 minuty a 57 sekund, což je rychlost 42 194 rop. Většinu tohoto času je ve skutečnosti lokální zpracování uvnitř aplikace. Pokud to změním tak, aby se každé vlákno načítalo stejně položka ITEMS / THREADS
krát, pak se to změní na 1 minutu 48 sekund – rychlost 92 592 rop.
Nejsem si jistý, jestli jsem na něco skutečně odpověděl, ale zkrácená verze by mohla být jednoduše „zkuste delší časový limit; zvažte použití funkce fire-and-forget).