Atualmente, seu código está usando a API síncrona (
StringSet
), e está sendo carregado por 10 threads simultaneamente. Isso não representará nenhum desafio apreciável para o SE.Redis - funciona muito bem aqui. Eu suspeito que realmente é um tempo limite em que o servidor demorou mais do que você gostaria para processar alguns dos dados, provavelmente também relacionados ao alocador do servidor. Uma opção, então, é simplesmente aumentar um pouco o tempo limite . Não muito... tente 5 segundos em vez do 1 segundo padrão. Provavelmente, a maioria das operações está funcionando muito rápido de qualquer maneira. Com relação à aceleração:uma opção aqui é não esperar - ou seja, manter dados de pipeline. Se você está satisfeito em não verificar todas as mensagens para um estado de erro, então uma maneira simples de fazer isso é adicionar
, flags: CommandFlags.FireAndForget
ao final do seu StringSet
ligar. Em meus testes locais, isso acelerou o exemplo de 1 milhão em 25% (e suspeito que muito do resto do tempo é gasto na serialização de strings). O maior problema que tive com o exemplo de 10 milhões foi simplesmente a sobrecarga de trabalhar com o exemplo de 10 milhões - especialmente porque isso consome grandes quantidades de memória tanto para o
redis-server
e o aplicativo, que (para emular sua configuração) estão na mesma máquina. Isso cria pressão de memória concorrente, com pausas de GC etc no código gerenciado. Mas talvez o mais importante:simplesmente leva uma eternidade para começar a fazer qualquer coisa . Consequentemente, refatorei o código para usar o yield return
paralelo geradores em vez de uma única lista. Por exemplo: 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;
}
com:
foreach (var element in InventPeople(PER_THREAD * counter1, PER_THREAD).Batchify(1000))
Aqui, o propósito de
Batchify
é garantir que não estamos ajudando muito o servidor, levando um tempo considerável entre cada operação - os dados são inventados em lotes de 1000 e cada lote é disponibilizado muito rapidamente. Eu também estava preocupado com o desempenho do JSON, então mudei para o JIL:
public static string ToJSON<T>(this T obj)
{
return Jil.JSON.Serialize<T>(obj);
}
e então, apenas por diversão, movi o trabalho JSON para o lote (para que os loops de processamento reais:
foreach (var element in InventPeople(PER_THREAD * counter1, PER_THREAD)
.Select(x => new { x.Id, Json = x.ToJSON() }).Batchify(1000))
Isso diminuiu um pouco mais os tempos, então posso carregar 10M em 3 minutos e 57 segundos, uma taxa de 42.194 rops. A maior parte desse tempo é, na verdade, processamento local dentro do aplicativo. Se eu alterar para que cada thread carregue o mesmo item
ITEMS / THREADS
vezes, isso muda para 1 minuto e 48 segundos - uma taxa de 92.592 rops. Não tenho certeza se respondi alguma coisa realmente, mas a versão curta pode ser simplesmente "experimente um tempo limite mais longo; considere usar acionar e esquecer).