patterncsharpModerate
Given an index return item from opposite end of list
Viewed 0 times
itemreturnlistendindexfromoppositegiven
Problem
Given the following code:
Where the purpose is:
Given an index (say 0) return the corresponding item from the other end of the array (for our example 22).
This is the part of the code that actually does anything:
The code works but I am interested in knowing if it can be made faster/more efficient. I would also like to know if there is an official term for this kind of algorithm.
namespace AtBash
{
class Program
{
static List letters = new List {
"Aleph", "Beis", "Gimmel", "Daled", "Hei", "Vav", "Zayin", "Chies", "Teis",
"Yud", "Chof", "Lamed", "Mem", "Nun", "Samech", "Ayin", "Pei", "Tzadik",
"Kuf", "Reish", "Shin", "Taf"
};
static string AtBash(int index) => letters[Math.Abs((index + 1) - letters.Count)];
static void Main(string[] args)
{
foreach (var item in letters)
{
Console.WriteLine(letters.IndexOf(item));
Console.WriteLine($"{item} -> {AtBash(letters.IndexOf(item))}");
}
}
}
}Where the purpose is:
Given an index (say 0) return the corresponding item from the other end of the array (for our example 22).
This is the part of the code that actually does anything:
static string AtBash(int index) => letters[Math.Abs((index + 1) - letters.Count)];The code works but I am interested in knowing if it can be made faster/more efficient. I would also like to know if there is an official term for this kind of algorithm.
Solution
If the entire goal is absolute performance of the
If that's the case and we can eat some startup time, and you really want that per-access performance on
Compare the IL in the initial accessor,
To the improved accessor,
To the fastest accessor,
Given your very minimal amount of actual memory in the example (which I don't think is likely to increase), I think this is a perfectly reasonable approach for any high-throughput library.
Feel free to benchmark this. The more accesses per startup, the better the payoff. You should already see payoff pretty quickly because sorting an array of a few dozen items is very fast and that's all the time we're adding to get the long-term benefits here. I wouldn't be surprised if your program even with trivial levels of access already performs better with this approach.
AtBash() request, I would simply reverse the letters at startup. I'm speaking outside the context of this particular program and more towards your library where I am making the assumption that the AtBash() method calls vs. startup ratio is much higher to infinite.If that's the case and we can eat some startup time, and you really want that per-access performance on
AtBash(), simply eat a tiny bit of memory to get there:static List letters = new List {
"Aleph", "Beis", "Gimmel", "Daled", "Hei", "Vav", "Zayin", "Chies", "Teis",
"Yud", "Chof", "Lamed", "Mem", "Nun", "Samech", "Ayin", "Pei", "Tzadik",
"Kuf", "Reish", "Shin", "Taf"
};
static List lettersReversed = letters.AsEnumerable().Reverse().ToList();
static string AtBash(int index) => lettersReversed[index];Compare the IL in the initial accessor,
letters[Math.Abs((index + 1) - letters.Count)]:IL_0000: ldsfld UserQuery+Program.letters
IL_0005: ldarg.0
IL_0006: ldc.i4.1
IL_0007: add
IL_0008: ldsfld UserQuery+Program.letters
IL_000D: callvirt System.Collections.Generic.List.get_Count
IL_0012: sub
IL_0013: call System.Math.Abs
IL_0018: callvirt System.Collections.Generic.List.get_Item
IL_001D: retTo the improved accessor,
letters[letters.Count - (index + 1)]:IL_0000: ldsfld UserQuery+Program.letters
IL_0005: ldsfld UserQuery+Program.letters
IL_000A: callvirt System.Collections.Generic.List.get_Count
IL_000F: ldarg.0
IL_0010: ldc.i4.1
IL_0011: add
IL_0012: sub
IL_0013: callvirt System.Collections.Generic.List.get_Item
IL_0018: retTo the fastest accessor,
lettersReversed[index]:IL_0000: ldsfld UserQuery+Program.lettersReversed
IL_0005: ldarg.0
IL_0006: callvirt System.Collections.Generic.List.get_Item
IL_000B: retGiven your very minimal amount of actual memory in the example (which I don't think is likely to increase), I think this is a perfectly reasonable approach for any high-throughput library.
Feel free to benchmark this. The more accesses per startup, the better the payoff. You should already see payoff pretty quickly because sorting an array of a few dozen items is very fast and that's all the time we're adding to get the long-term benefits here. I wouldn't be surprised if your program even with trivial levels of access already performs better with this approach.
Code Snippets
static List<string> letters = new List<string> {
"Aleph", "Beis", "Gimmel", "Daled", "Hei", "Vav", "Zayin", "Chies", "Teis",
"Yud", "Chof", "Lamed", "Mem", "Nun", "Samech", "Ayin", "Pei", "Tzadik",
"Kuf", "Reish", "Shin", "Taf"
};
static List<string> lettersReversed = letters.AsEnumerable().Reverse().ToList();
static string AtBash(int index) => lettersReversed[index];IL_0000: ldsfld UserQuery+Program.letters
IL_0005: ldarg.0
IL_0006: ldc.i4.1
IL_0007: add
IL_0008: ldsfld UserQuery+Program.letters
IL_000D: callvirt System.Collections.Generic.List<System.String>.get_Count
IL_0012: sub
IL_0013: call System.Math.Abs
IL_0018: callvirt System.Collections.Generic.List<System.String>.get_Item
IL_001D: retIL_0000: ldsfld UserQuery+Program.letters
IL_0005: ldsfld UserQuery+Program.letters
IL_000A: callvirt System.Collections.Generic.List<System.String>.get_Count
IL_000F: ldarg.0
IL_0010: ldc.i4.1
IL_0011: add
IL_0012: sub
IL_0013: callvirt System.Collections.Generic.List<System.String>.get_Item
IL_0018: retIL_0000: ldsfld UserQuery+Program.lettersReversed
IL_0005: ldarg.0
IL_0006: callvirt System.Collections.Generic.List<System.String>.get_Item
IL_000B: retContext
StackExchange Code Review Q#128506, answer score: 11
Revisions (0)
No revisions yet.