Reputation: 423
Here's a simplified scenario that I'm dealing with. There are multiple methods with the looping structure.
for (int i=0; i<I; i++) {
// Do something
for (int j=0; j<J; j++) {
// Do something
for (int k=0; k<K; k++) {
// Do something
Update(a,b,c);
}
}
}
In one method, Update(a,b,c) is
a[i] += b[j] * c[k]
In another method, it is
b[j] += a[i] * c[k]
And yet in another method, it is
c[k] += a[i] * b[j]
At the moment, my code is duplicated everywhere. Is there a pattern in C# so that I don't duplicate code? I was tempted to use delegate but it seems that delegate would degrade the performance (which is critical in my case).
Is there a way to write a macro or an inline delegate function for such scenario?
Upvotes: 5
Views: 809
Reputation: 4693
Likely you are implementing something like large number multiplication or other linear combination of vectors. The reason you would need the approach you described as inline delegate is quite probably because of the varying place to store the result during the calculation and also because the nested for-loops are hard coded. Thus I suggest to revise your code like following:
public void Update(int destinationIndex, int[][] arrays, int[] indices) {
var product=1;
for(var i=indices.Length; i-->0; )
if(destinationIndex!=i)
product*=arrays[i][indices[i]];
arrays[destinationIndex][indices[destinationIndex]]+=product;
}
public void PerformUpdate(
int destinationIndex, int[] counts, int[][] arrays, Action<int, int>[] actions,
List<int> indices=null, int level=0
) {
if(level==counts.Length)
Update(destinationIndex, arrays, (indices??new List<int>()).ToArray());
else
for(int count=counts[level], i=0; i<count; i++) {
if(null!=actions&&level<actions.Length)
actions[level](i, count); // do something according to nesting level
(indices=indices??new List<int>()).Add(i);
PerformUpdate(destinationIndex, counts, arrays, actions, indices, 1+level);
indices.RemoveAt(indices.Count-1);
}
}
This code is implemented in a recursive manner. The int[][] array
can be replace with a generic array as long as you are going to define the calculation of operator *
and operator +
, rather the methods name in MutiplyScalar
and AddScalar
.
So, we would not use a delegate of Update
to control the destination. Instead, we just use the destinationIndex
to accomplish that. Following is a test case:
int[] a=new[] { 1, 2 }, b=new[] { 3, 4, 5 }, c=new[] { 6 };
Action<int, int> m=(index, count) => Debug.Print("index={0}; count={1}", index, count);
PerformUpdate(2, new[] { a.Length, b.Length, c.Length }, new[] { a, b, c }, new[] { m, m, m });
We still have inline delegates there, which are called Lambda Expressions
in c#. According to the original code you provided, there're Do something
s between the nested for-loops. However, there is not much information we can found which is non-globally known to Update
; the most significant difference we can see is the iterating index and the ending number, which are i, I
, j, J
and k, K
. Thus we just take these as arguments to pass to the Action<int, int>
for doing something, and they are variable for each level of for-loop.
The execution is much depended on the indices
. It stores the iterating index of current for-loop and passed to next level of recursive call. Further if you passed the arrays
with a count smaller than its Length
in indices
, it's treated as an array with the length of that count you passed to. Don't pass a negative count, neither a larger one. It can be lack of Action<int, int>
, that would just mean do nothing instead of do something.
Upvotes: 0
Reputation: 210445
This will probably inline it.
interface IFunc<T>
{
void Invoke(ref T a, ref T b, ref T c);
}
void DoUpdates<TFunc>(TFunc update)
where TFunc : IFunc<int>
{
for (int i = 0; i < I; i++)
for (int j = 0; j < J; j++)
for (int k = 0; k < K; k++)
update.Invoke(ref i, ref j, ref k);
}
Upvotes: 0
Reputation: 1135
If performance is critical you could avoid the method call in the inner loop, like this:
void Update(int[]x, int[]y, int[]z, int I, int J, int K)
{
for (int i = 0; i < I; i++)
{
// Do something
for (int j = 0; j < J; j++)
{
// Do something
for (int k = 0; k < K; k++)
{
// Do something
x[i] += y[j] * z[k];
}
}
}
}
and the calling code:
Update(a, b, c, I, J, K);
Update(b, a, c, J, I, K);
Update(c, a, b, K, I, J);
Upvotes: 2
Reputation: 660032
Something like this?
void DoUpdates(Action<int, int, int> update)
{
for (int i=0; i<I; i++) {
// Do something
for (int j=0; j<J; j++) {
// Do something
for (int k=0; k<K; k++) {
// Do something
update(i, j, k);
}
}
}
}
and then in the caller
DoUpdates((int i, int j, int k) => { a[i] += b[j] * c[k]; });
Is that what you're looking for?
Upvotes: 11
Reputation: 100258
void Update<T>(T[] x, T[] y, T[] z, int i, int j, int k)
{
x[i] += y[j] * z[k];
}
Usage:
Update(a, b, c, i, j, k);
Update(b, a, c, j, i, k);
Update(c, a, b, k, i, j);
I see that a
is always accessed by i
(and so on - b
by j
, c
by k
). You can try to optimize the code using this fact.
Upvotes: 5