3

これは、私が扱っている単純化されたシナリオです。ループ構造を持つ複数のメソッドがあります。

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);
        }
    }
}

1 つの方法では、Update(a,b,c) は

a[i] += b[j] * c[k]

別の方法では、

b[j] += a[i] * c[k]

さらに別の方法では、

c[k] += a[i] * b[j]

現時点では、私のコードはいたるところに複製されています。コードを複製しないように C# にパターンはありますか? デリゲートを使用したくなりましたが、デリゲートはパフォーマンスを低下させるようです (私の場合は重要です)。

そのようなシナリオでマクロまたはインライン デリゲート関数を作成する方法はありますか?

4

5 に答える 5

11

このようなもの?

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);
      }
    }
  }
}

そして、呼び出し元で

DoUpdates((int i, int j, int k) => { a[i] += b[j] * c[k]; });

それはあなたが探しているものですか?

于 2013-02-26T01:14:53.390 に答える
4
void Update<T>(T[] x, T[] y, T[] z, int i, int j, int k)
{
    x[i] += y[j] * z[k];
}

使用法:

Update(a, b, c, i, j, k);
Update(b, a, c, j, i, k);
Update(c, a, b, k, i, j);

私はそれaが常にによってアクセスされているのを見るi(など-bによってjcによってk)。この事実を使用して、コードの最適化を試みることができます。

于 2013-02-26T01:05:27.477 に答える
2

パフォーマンスが重要な場合は、次のように内側のループでメソッド呼び出しを回避できます。

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];
            }
        }
    }
}

および呼び出しコード:

Update(a, b, c, I, J, K);
Update(b, a, c, J, I, K);
Update(c, a, b, K, I, J);
于 2013-02-26T01:49:23.907 に答える
0

おそらく、多数の乗算や他のベクトルの線形結合のようなものを実装しています。インラインデリゲートとして説明したアプローチが必要になる理由は、おそらく計算中に結果を格納する場所が異なるためであり、ネストされたforループがハードコーディングされているためです。したがって、次のようにコードを修正することをお勧めします。

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);
        }
}

このコードは再帰的に実装されます。とのメソッド名ではなく、とint[][] arrayの計算を定義する限り、はジェネリック配列に置き換えることができます。operator *operator +MutiplyScalarAddScalar

Updateしたがって、宛先を制御するためにのデリゲートを使用しません。代わりに、を使用してそれdestinationIndexを実現します。以下はテストケースです。

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 });

そこにはまだインラインデリゲートがあり、Lambda Expressionsc#で呼び出されます。提供した元のコードによるとDo something、ネストされたforループの間にsがあります。ただし、グローバルに知られていない情報はあまりありませんUpdate。私たちが見ることができる最も重要な違いは、反復インデックスと終了番号、であるi, Ij, Jk, Kです。したがって、これらを引数として取り、Action<int, int>何かを行うためにforに渡すだけで、forループのレベルごとに可変になります。

実行はに大きく依存しindicesます。現在のforループの反復インデックスを格納し、次のレベルの再帰呼び出しに渡されます。さらに、 inarraysよりも小さいカウントでを渡した場合、渡したカウントの長さの配列として扱われます。負のカウントを渡さないでください。大きいカウントも渡さないでください。不足している可能性があります。つまり、何かをする代わりに何もしないということです。LengthindicesAction<int, int>

于 2013-02-26T05:38:11.060 に答える
0

これはおそらくインライン化されます。

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);
}
于 2013-02-26T05:47:52.993 に答える