using System.Collections.Generic;
public static void Main()
var list = new List<int>{2,3,4,5,6};
var batchSizes = new int[] {3};
Console.WriteLine("OLD ALGORITHM START");
foreach(var batchSize in batchSizes)
Test(list, ids, batchSize, o => false);
Test(list, ids, batchSize, o => o % 2 == 0);
Test(list, ids, batchSize, o => o % 2 != 0);
Console.WriteLine("OLD ALGORITHM END");
Console.WriteLine("NEW ALGORITHM NO BATCH CACHE START");
foreach(var batchSize in batchSizes)
TestBatch(list, ids, batchSize, o => false, true, false);
TestBatch(list, ids, batchSize, o => o % 2 == 0, true, false);
TestBatch(list, ids, batchSize, o => o % 2 != 0, true, false);
Console.WriteLine("NEW ALGORITHM NO BATCH CACHE END");
Console.WriteLine("NEW ALGORITHM BATCH CACHE START");
foreach(var batchSize in batchSizes)
TestBatch(list, ids, batchSize, o => false);
TestBatch(list, ids, batchSize, o => o % 2 == 0);
TestBatch(list, ids, batchSize, o => o % 2 != 0);
TestBatch(list, ids, batchSize, o => false, false);
Console.WriteLine("NEW ALGORITHM BATCH CACHE END");
private static void TestBatch(List<int> set, IEnumerable<int> ids, int batchSize, Func<int, bool> isCachedFn, bool checkCache = true, bool batchableCache = true)
Console.WriteLine("-----START------");
Func<int[], bool[]> areCachedFn = keys => {
Console.WriteLine($"Checking cache keys: {string.Join(",", keys)}");
var result = new bool[keys.Length];
for(var i = 0; i < keys.Length; i++)
result[i] = isCachedFn(keys[i]);
Console.WriteLine($"Cache result: {string.Join(",", result)}");
Func<int, bool> isCachedLogFn = key => {
Console.WriteLine($"Checking cache key: {key}");
var result = isCachedFn(key);
Console.WriteLine($"Cache result: {result}");
Console.WriteLine(string.Format("Id: {0}", id));
Console.WriteLine(string.Format("BatchSize: {0}", batchSize));
var result = GetEntityBatch(id, batchSize, set, checkCache, batchableCache, isCachedLogFn, areCachedFn);
Console.WriteLine("Result: " + string.Join(",", result));
Console.WriteLine("----------------");
Console.WriteLine($"Total cache calls: {cacheCalls}");
Console.WriteLine("------END-------");
private static void Test(List<int> set, IEnumerable<int> ids, int batchSize, Func<int, bool> isCachedFn)
Console.WriteLine("-----START------");
Func<int, bool> isCachedLogFn = key => {
Console.WriteLine($"Checking cache key: {key}");
var result = isCachedFn(key);
Console.WriteLine($"Cache result: {result}");
Console.WriteLine(string.Format("Id: {0}", id));
Console.WriteLine(string.Format("BatchSize: {0}", batchSize));
var result = GetEntityBatch(id, batchSize, set, isCachedLogFn);
Console.WriteLine("Result: " + string.Join(",", result));
Console.WriteLine("----------------");
Console.WriteLine($"Total cache calls: {cacheCalls}");
Console.WriteLine("------END-------");
public static int[] GetEntityBatch(int id, int batchSize, List<int> set, Func<int, bool> isCachedFn)
int[] ids = new int[batchSize];
bool checkForEnd = false;
if (checkForEnd && i == end)
internal static int[] GetEntityBatch(int id, int batchSize, List<int> set, bool checkCache, bool batchableCache, Func<int, bool> isCachedFn, Func<int[], bool[]> areCachedFn)
int[] ids = new int[batchSize];
bool checkForEnd = false;
var entityKeys = new List<KeyValuePair<int, int>>(batchSize);
while (i != batchSize && entityKeys.Count > 0)
if (CheckCacheAndProcessResult())
bool CheckCacheAndProcessResult()
var fromIndex = batchableCache
? entityKeys.Count - Math.Min(batchSize, entityKeys.Count)
var toIndex = entityKeys.Count - 1;
var indexes = GetSortedKeyIndexes(entityKeys, idIndex, fromIndex, toIndex);
for (var j = 0; j < entityKeys.Count; j++)
if (ProcessKey(entityKeys[indexes[j]].Key))
var results = AreCached(entityKeys, indexes, areCachedFn, checkCache);
for (var j = 0; j < results.Length; j++)
if (!results[j] && ProcessKey(entityKeys[indexes[j]].Key, true))
for (var j = toIndex; j >= fromIndex; j--)
bool ProcessKey(int key, bool ignoreCache = false)
if (checkForEnd && (index == set.Count || index >= idIndex.Value + batchSize))
else if (!checkCache || !batchableCache)
if (index < set.Count && (!idIndex.HasValue || index < idIndex.Value))
entityKeys.Add(new KeyValuePair<int, int>(key, index));
if (!checkCache || !isCachedFn(key))
entityKeys.Add(new KeyValuePair<int, int>(key, index));
if (!idIndex.HasValue || index < idIndex.Value + batchSize)
return CheckCacheAndProcessResult();
if (idIndex.HasValue || index == set.Count)
return index == set.Count || index >= idIndex.Value + batchSize;
private static int[] GetSortedKeyIndexes(List<KeyValuePair<int, int>> entityKeys, int? idIndex, int fromIndex, int toIndex)
var result = new int[Math.Abs(toIndex - fromIndex) + 1];
var lowerIndexes = new List<int>();
for (var j = fromIndex; j <= toIndex; j++)
if (!idIndex.HasValue || entityKeys[j].Value < idIndex.Value)
for (var j = lowerIndexes.Count - 1; j >= 0; j--)
result[i++] = lowerIndexes[j];
private static bool[] AreCached(List<KeyValuePair<int, int>> entityKeys, int[] keyIndexes, Func<int[], bool[]> areCachedFn, bool checkCache = true)
var result = new bool[keyIndexes.Length];
var cacheKeys = new int[keyIndexes.Length];
foreach(var index in keyIndexes)
cacheKeys[i++] = entityKeys[index].Key;
var cacheResult = areCachedFn(cacheKeys);
for (var j = 0; j < result.Length; j++)
result[j] = cacheResult[j];