7

興味深い結果に出くわしたとき、私は楽しみのためにいくつかの実験的計算を行っていました。

Completed 1024x1024 pixels with 700 points in...
For Loop (Inline): 19636ms
For Loop: 12612ms
Parallel.For Loop: 3835ms

これは私が期待したものではありません。

システム: Windows 7 64、i3 2120 [デュアルコア、4 スレッド]、Visual Studio 2010。

ビルド: 最適化オン、リリース モード [デバッガなし]、32 ビット。

2 番目に興味深いのは、残念な 64 ビットのパフォーマンスです。比率に関しては、私が期待するよりもインラインですが、全体的に遅くすることでこれを実現しています。

Completed 1024x1024 pixels with 700 points in...
For Loop (Inline): 23409ms
For Loop: 24373ms
Parallel.For Loop: 6839ms

計算は簡単です。インデックス x と y について、最も近い Vector3 を見つけて 2D 配列に格納します。

問題は、勇気があるなら、なぜインライン for ループが非常に遅いのかを説明しようとすることです。64ビットバージョンのパフォーマンスの欠如を説明するためのボーナスポイント。

using System;
using System.Diagnostics;
using System.Threading.Tasks;

namespace TextureFromPoints
{
    class Program
    {
        const int numPoints = 700;
        const int textureSize = 1024;

        static Random rnd = new Random();

        static void Main(string[] args)
        {
            while (true)
            {
                Console.WriteLine("Starting");
                Console.WriteLine();

                var pointCloud = new Vector3[numPoints];

                for (int i = 0; i < numPoints; i++)
                    pointCloud[i] = new Vector3(textureSize);

                var result1 = new Vector3[textureSize, textureSize];
                var result2 = new Vector3[textureSize, textureSize];
                var result3 = new Vector3[textureSize, textureSize];

                var sw1 = Stopwatch.StartNew();
                for (int x = 0; x < textureSize; x++)
                    for (int y = 0; y < textureSize; y++)
                    {
                        var targetPos = new Vector3(x, y, 0);
                        var nearestV3 = pointCloud[0];
                        var nearestV3Distance = nearestV3.DistanceToPoint(targetPos);

                        for (int i = 1; i < numPoints; i++)
                        {
                            var currentV3 = pointCloud[i];
                            var currentV3Distance = currentV3.DistanceToPoint(targetPos);
                            if (currentV3Distance < nearestV3Distance)
                            {
                                nearestV3 = currentV3;
                                nearestV3Distance = currentV3Distance;
                            }
                        }
                        result1[x, y] = nearestV3;
                    }
                sw1.Stop();

                var sw2 = Stopwatch.StartNew();
                for (int x = 0; x < textureSize; x++)
                    for (int y = 0; y < textureSize; y++)
                        Computation(pointCloud, result2, x, y);
                sw2.Stop();


                var sw3 = Stopwatch.StartNew();

                Parallel.For(0, textureSize, x =>
                {
                    for (int y = 0; y < textureSize; y++)
                        Computation(pointCloud, result3, x, y);
                });
                sw3.Stop();

                Console.WriteLine("Completed {0}x{0} pixels with {1} points in...", textureSize, numPoints);
                Console.WriteLine("{0}: {1}ms", "For Loop (Inline)", sw1.ElapsedMilliseconds);
                Console.WriteLine("{0}: {1}ms", "For Loop", sw2.ElapsedMilliseconds);
                Console.WriteLine("{0}: {1}ms", "Parallel.For Loop", sw3.ElapsedMilliseconds);
                Console.WriteLine();
                Console.Write("Verifying Data: ");
                Console.WriteLine(CheckResults(result1, result2) && CheckResults(result1, result3) ? "Valid" : "Error");
                Console.WriteLine(); Console.WriteLine();
                Console.ReadLine();
            }
        }

        private static bool CheckResults(Vector3[,] lhs, Vector3[,] rhs)
        {
            for (int x = 0; x < textureSize; x++)
                for (int y = 0; y < textureSize; y++)
                    if (!lhs[x, y].Equals(rhs[x, y]))
                        return false;
            return true;
        }

        private static void Computation(Vector3[] pointCloud, Vector3[,] result, int x, int y)
        {
            var targetPos = new Vector3(x, y, 0);
            var nearestV3 = pointCloud[0];
            var nearestV3Distance = nearestV3.DistanceToPoint(targetPos);

            for (int i = 1; i < numPoints; i++)
            {
                var currentV3 = pointCloud[i];
                var currentV3Distance = currentV3.DistanceToPoint(targetPos);
                if (currentV3Distance < nearestV3Distance)
                {
                    nearestV3 = currentV3;
                    nearestV3Distance = currentV3Distance;
                }
            }
            result[x, y] = nearestV3;
        }

        struct Vector3
        {
            public float x;
            public float y;
            public float z;

            public Vector3(float x, float y, float z)
            {
                this.x = x;
                this.y = y;
                this.z = z;
            }
            public Vector3(float randomDistance)
            {
                this.x = (float)rnd.NextDouble() * randomDistance;
                this.y = (float)rnd.NextDouble() * randomDistance;
                this.z = (float)rnd.NextDouble() * randomDistance;
            }

            public static Vector3 operator -(Vector3 a, Vector3 b)
            {
                return new Vector3(a.x - b.x, a.y - b.y, a.z - b.z);
            }

            public float sqrMagnitude()
            {
                return x * x + y * y + z * z;
            }

            public float DistanceToPoint(Vector3 point)
            {
                return (this - point).sqrMagnitude();
            }
        }
    }
}

更新: Drew Marshの努力のおかげで、すべての V3 操作をインライン化するこの非常に最適化されたバージョンができました。

using System;
using System.Diagnostics;
using System.Threading.Tasks;

namespace TextureFromPoints
{
    class RevisedProgram
    {
        const int numPoints = 700;
        const int textureSize = 1024;

        static Random rnd = new Random();

        static void Main(string[] args)
        {
            while (true)
            {
                Console.WriteLine("Starting REVISED");
                Console.WriteLine();

                var pointCloud = new Vector3[numPoints];

                for (int i = 0; i < numPoints; i++)
                    pointCloud[i] = new Vector3(textureSize);

                var result1 = new Vector3[textureSize, textureSize];
                var result2 = new Vector3[textureSize, textureSize];
                var result3 = new Vector3[textureSize, textureSize];

                var sw1 = Inline(pointCloud, result1);

                var sw2 = NotInline(pointCloud, result2);

                var sw3 = Parallelized(pointCloud, result3);

                Console.WriteLine("Completed {0}x{0} pixels with {1} points in...", textureSize, numPoints);
                Console.WriteLine("{0}: {1}ms", "For Loop (Inline)", sw1.ElapsedMilliseconds);
                Console.WriteLine("{0}: {1}ms", "For Loop", sw2.ElapsedMilliseconds);
                Console.WriteLine("{0}: {1}ms", "Parallel.For Loop", sw3.ElapsedMilliseconds);
                Console.WriteLine();
                Console.Write("Verifying Data: ");
                Console.WriteLine(CheckResults(result1, result2) && CheckResults(result1, result3) ? "Valid" : "Error");
                Console.WriteLine();
                Console.WriteLine();
                Console.ReadLine();
            }
        }

        private static Stopwatch Parallelized(Vector3[] pointCloud, Vector3[,] result3)
        {
            var sw3 = Stopwatch.StartNew();

            Parallel.For(0, textureSize, x =>
            {
                for (int y = 0; y < textureSize; y++)
                    Computation(pointCloud, result3, x, y);
            });
            sw3.Stop();
            return sw3;
        }

        private static Stopwatch NotInline(Vector3[] pointCloud, Vector3[,] result2)
        {
            var sw2 = Stopwatch.StartNew();
            for (int x = 0; x < textureSize; x++)
                for (int y = 0; y < textureSize; y++)
                    Computation(pointCloud, result2, x, y);
            sw2.Stop();
            return sw2;
        }

        private static Stopwatch Inline(Vector3[] pointCloud, Vector3[,] result1)
        {
            var sw1 = Stopwatch.StartNew();
            for (int x = 0; x < textureSize; x++)
                for (int y = 0; y < textureSize; y++)
                {
                    var targetPos = new Vector3(x, y, 0);
                    var nearestV3 = pointCloud[0];
                    Vector3 temp1 = new Vector3(nearestV3.x - targetPos.x, nearestV3.y - targetPos.y, nearestV3.z - targetPos.z);
                    var nearestV3Distance = temp1.x * temp1.x + temp1.y * temp1.y + temp1.z * temp1.z;

                    for (int i = 1; i < numPoints; i++)
                    {
                        var currentV3 = pointCloud[i];
                        Vector3 temp2 = new Vector3(currentV3.x - targetPos.x, currentV3.y - targetPos.y, currentV3.z - targetPos.z);
                        var currentV3Distance = temp2.x * temp2.x + temp2.y * temp2.y + temp2.z * temp2.z;
                        if (currentV3Distance < nearestV3Distance)
                        {
                            nearestV3 = currentV3;
                            nearestV3Distance = currentV3Distance;
                        }
                    }
                    result1[x, y] = nearestV3;
                }
            sw1.Stop();
            return sw1;
        }

        private static bool CheckResults(Vector3[,] lhs, Vector3[,] rhs)
        {
            for (int x = 0; x < textureSize; x++)
                for (int y = 0; y < textureSize; y++)
                    if (!lhs[x, y].Equals(rhs[x, y]))
                        return false;
            return true;
        }

        private static void Computation(Vector3[] pointCloud, Vector3[,] result, int x, int y)
        {
            var targetPos = new Vector3(x, y, 0);
            var nearestV3 = pointCloud[0];
            Vector3 temp1 = new Vector3(nearestV3.x - targetPos.x, nearestV3.y - targetPos.y, nearestV3.z - targetPos.z);

            var nearestV3Distance = temp1.x * temp1.x + temp1.y * temp1.y + temp1.z * temp1.z;

            for (int i = 1; i < numPoints; i++)
            {
                var currentV3 = pointCloud[i];
                Vector3 temp2 = new Vector3(currentV3.x - targetPos.x, currentV3.y - targetPos.y, currentV3.z - targetPos.z);
                var currentV3Distance = temp2.x * temp2.x + temp2.y * temp2.y + temp2.z * temp2.z;
                if (currentV3Distance < nearestV3Distance)
                {
                    nearestV3 = currentV3;
                    nearestV3Distance = currentV3Distance;
                }
            }
            result[x, y] = nearestV3;
        }


        struct Vector3
        {
            public float x;
            public float y;
            public float z;

            public Vector3(float x, float y, float z)
            {
                this.x = x;
                this.y = y;
                this.z = z;
            }
            public Vector3(float randomDistance)
            {
                this.x = (float)rnd.NextDouble() * randomDistance;
                this.y = (float)rnd.NextDouble() * randomDistance;
                this.z = (float)rnd.NextDouble() * randomDistance;
            }
        }
    }
}

そして、次の結果が得られます。

x86

Completed 1024x1024 pixels with 700 points in...
For Loop (Inline): 3820ms
For Loop: 3962ms
Parallel.For Loop: 1681ms

x64

Completed 1024x1024 pixels with 700 points in...
For Loop (Inline): 10978ms
For Loop: 10924ms
Parallel.For Loop: 3073ms

したがって、良いニュースは、このコードのパフォーマンスを大幅に向上させることができるということです。シングル スレッド バージョンを、その並列版と同じ速度で動作させることができます。

悪いニュースは、これは x64 を完全に捨て、手動ですべての数学をインライン化することを意味することです。

この段階では、コンパイラのパフォーマンスに非常に失望しています。

結論

これは非常識で悲しいことです...そして、愚かなコンパイラが原因であると推測できる理由はよくわかりません. コンパイラーを x64 から x86 に変更し、手動でインライン化するだけで 24 秒から 3.8 秒になることは、私が期待するものではありません。しかし、私が書いていた概念実証は終了しました。単純な空間ハッシュのおかげで、0.7 秒で 70,000 の「ポイント」を持つ 1024 x 1024 の画像を計算できます。元の x64 シナリオよりも ~340000% 速く、スレッドまたはインラインなし。そのため、私は答えを受け入れました-私はまだ問題を調査していますが、当面の必要性はなくなりました.

コードはここここで利用できます- 副作用として素敵なボロノイ図を生成します:P

4

4 に答える 4

8

8 コア i7、Win7、x64 からのすべてのデータ

確実に5倍になるのは驚きです。あなたが書いたように、このテストの1つの問題は、3つのアプローチすべてを Main メソッドに入れていることですParallel.For.インライン方式のやり方。次のように作業を分割すると、3 つの実装すべてでパフォーマンスが大幅に向上することがわかります...少なくとも x86 の場合:

x86 より前:

For Loop (Inline): 24313ms 
For Loop: 25236ms 
Parallel.For Loop: 3840ms

x86 以降:

For Loop (Inline): 13007ms
For Loop: 13013ms
Parallel.For Loop: 2208ms

それで、私の x86 Parallel を見てください。結果については、約 5.9 倍にスケーリングし、各バージョンを分離するとはるかに高速であることがわかります。

次に興味深いことに、この同じ変更の後、x64 にはまったく効果がありません。実際、3 回のテストのうち 2 回の実行で、一貫してわずかに高い値で終了しました。

x64以前

For Loop (Inline): 24222ms
For Loop: 25197ms
Parallel.For Loop: 3810ms

x64以降

For Loop (Inline): 25302ms
For Loop: 25209ms
Parallel.For Loop: 3821ms

x64 JITの見栄えを悪くするこのようなコードを人々が一貫して思いつくという事実以外に、なぜx64がそれほど悪いのかという直接的な答えはありません。

そうは言っても、そのような実装で検討したいことがもう 1 つあります。それは、キャッシュ ラインの無効化です。ここに素晴らしいMSDNの記事がありますこれが何であるかを説明する@StephenToubによって書かれました。TL;DR; それは、すべてのデータが 1 つの配列と diff に格納されているためです。異なるローカル (L2) キャッシュを持つコアは、重複する他のコアとデータを同期する必要がある配列の一部を変更します。セクションが差分の場合。作業中のコアが互いに近すぎると、これらの同期が多数発生し、並列ゲインが損なわれる可能性があります。この記事では、作業対象のデータを含む実際のセクションを分離するのに十分な余分なスペースを作業配列に実際に割り当てる手法を示しています。これにより、これらのコアがデータで作業するときに、他のコアを無効にする必要がなくなります。コア。それよりも8xに近づくのではなく、forループの。キャッシュラインの無効化に対処するための作業を行っていただければ、さらに 10% 以上を絞り出すことができると思います。並行作業のセットアップと調整には常にいくらかのオーバーヘッドがかかるため、100% 完璧になることはありません。

各アプローチが個別のメソッドに分解された、プログラムの改訂版を次に示します。

using System;
using System.Diagnostics;
using System.Threading.Tasks;

namespace TextureFromPoints
{
    class RevisedProgram
    {
        const int numPoints = 700;
        const int textureSize = 1024;

        static Random rnd = new Random();

        static void Main(string[] args)
        {
            while(true)
            {
                Console.WriteLine("Starting REVISED");
                Console.WriteLine();

                var pointCloud = new Vector3[numPoints];

                for(int i = 0; i < numPoints; i++)
                    pointCloud[i] = new Vector3(textureSize);

                var result1 = new Vector3[textureSize, textureSize];
                var result2 = new Vector3[textureSize, textureSize];
                var result3 = new Vector3[textureSize, textureSize];

                var sw1 = Inline(pointCloud, result1);

                var sw2 = NotInline(pointCloud, result2);


                var sw3 = Parallelized(pointCloud, result3);

                Console.WriteLine("Completed {0}x{0} pixels with {1} points in...", textureSize, numPoints);
                Console.WriteLine("{0}: {1}ms", "For Loop (Inline)", sw1.ElapsedMilliseconds);
                Console.WriteLine("{0}: {1}ms", "For Loop", sw2.ElapsedMilliseconds);
                Console.WriteLine("{0}: {1}ms", "Parallel.For Loop", sw3.ElapsedMilliseconds);
                Console.WriteLine();
                Console.Write("Verifying Data: ");
                Console.WriteLine(CheckResults(result1, result2) && CheckResults(result1, result3) ? "Valid" : "Error");
                Console.WriteLine();
                Console.WriteLine();
                Console.ReadLine();
            }
        }

        private static Stopwatch Parallelized(Vector3[] pointCloud, Vector3[,] result3)
        {
            var sw3 = Stopwatch.StartNew();

            Parallel.For(0, textureSize, x =>
            {
                for(int y = 0; y < textureSize; y++)
                    Computation(pointCloud, result3, x, y);
            });
            sw3.Stop();
            return sw3;
        }

        private static Stopwatch NotInline(Vector3[] pointCloud, Vector3[,] result2)
        {
            var sw2 = Stopwatch.StartNew();
            for(int x = 0; x < textureSize; x++)
                for(int y = 0; y < textureSize; y++)
                    Computation(pointCloud, result2, x, y);
            sw2.Stop();
            return sw2;
        }

        private static Stopwatch Inline(Vector3[] pointCloud, Vector3[,] result1)
        {
            var sw1 = Stopwatch.StartNew();
            for(int x = 0; x < textureSize; x++)
                for(int y = 0; y < textureSize; y++)
                {
                    var targetPos = new Vector3(x, y, 0);
                    var nearestV3 = pointCloud[0];
                    var nearestV3Distance = nearestV3.DistanceToPoint(targetPos);

                    for(int i = 1; i < numPoints; i++)
                    {
                        var currentV3 = pointCloud[i];
                        var currentV3Distance = currentV3.DistanceToPoint(targetPos);
                        if(currentV3Distance < nearestV3Distance)
                        {
                            nearestV3 = currentV3;
                            nearestV3Distance = currentV3Distance;
                        }
                    }
                    result1[x, y] = nearestV3;
                }
            sw1.Stop();
            return sw1;
        }

        private static bool CheckResults(Vector3[,] lhs, Vector3[,] rhs)
        {
            for(int x = 0; x < textureSize; x++)
                for(int y = 0; y < textureSize; y++)
                    if(!lhs[x, y].Equals(rhs[x, y]))
                        return false;
            return true;
        }

        private static void Computation(Vector3[] pointCloud, Vector3[,] result, int x, int y)
        {
            var targetPos = new Vector3(x, y, 0);
            var nearestV3 = pointCloud[0];
            var nearestV3Distance = nearestV3.DistanceToPoint(targetPos);

            for(int i = 1; i < numPoints; i++)
            {
                var currentV3 = pointCloud[i];
                var currentV3Distance = currentV3.DistanceToPoint(targetPos);
                if(currentV3Distance < nearestV3Distance)
                {
                    nearestV3 = currentV3;
                    nearestV3Distance = currentV3Distance;
                }
            }
            result[x, y] = nearestV3;
        }

        struct Vector3
        {
            public float x;
            public float y;
            public float z;

            public Vector3(float x, float y, float z)
            {
                this.x = x;
                this.y = y;
                this.z = z;
            }
            public Vector3(float randomDistance)
            {
                this.x = (float)rnd.NextDouble() * randomDistance;
                this.y = (float)rnd.NextDouble() * randomDistance;
                this.z = (float)rnd.NextDouble() * randomDistance;
            }

            public static Vector3 operator -(Vector3 a, Vector3 b)
            {
                return new Vector3(a.x - b.x, a.y - b.y, a.z - b.z);
            }

            public float sqrMagnitude()
            {
                return x * x + y * y + z * z;
            }

            public float DistanceToPoint(Vector3 point)
            {
                return (this - point).sqrMagnitude();
            }
        }
    }
}

アップデート:

メソッドが x64 JIT によってインライン化されていないことについて Feng Yuan が指摘したことに基づいて、代わりにプログラムを変更して計算をインラインで実行し、x86 バージョンよりも x64 バージョンのパフォーマンスを向上させることができます。これは明らかにひどいものですが、これは x64 JIT が以前に破壊するのを見たようなものです。新しい数字は次のとおりです。

x64 をインライン化した後:

For Loop (Inline): 19032ms
For Loop: 19209ms
Parallel.For Loop: 3015ms

コードのインライン バージョン:

using System;
using System.Diagnostics;
using System.Threading.Tasks;

namespace TextureFromPoints
{
    class RevisedProgram
    {
        const int numPoints = 700;
        const int textureSize = 1024;

        static Random rnd = new Random();

        static void Main(string[] args)
        {
            while(true)
            {
                Console.WriteLine("Starting REVISED");
                Console.WriteLine();

                var pointCloud = new Vector3[numPoints];

                for(int i = 0; i < numPoints; i++)
                    pointCloud[i] = new Vector3(textureSize);

                var result1 = new Vector3[textureSize, textureSize];
                var result2 = new Vector3[textureSize, textureSize];
                var result3 = new Vector3[textureSize, textureSize];

                var sw1 = Inline(pointCloud, result1);

                var sw2 = NotInline(pointCloud, result2);


                var sw3 = Parallelized(pointCloud, result3);

                Console.WriteLine("Completed {0}x{0} pixels with {1} points in...", textureSize, numPoints);
                Console.WriteLine("{0}: {1}ms", "For Loop (Inline)", sw1.ElapsedMilliseconds);
                Console.WriteLine("{0}: {1}ms", "For Loop", sw2.ElapsedMilliseconds);
                Console.WriteLine("{0}: {1}ms", "Parallel.For Loop", sw3.ElapsedMilliseconds);
                Console.WriteLine();
                Console.Write("Verifying Data: ");
                Console.WriteLine(CheckResults(result1, result2) && CheckResults(result1, result3) ? "Valid" : "Error");
                Console.WriteLine();
                Console.WriteLine();
                Console.ReadLine();
            }
        }

        private static Stopwatch Parallelized(Vector3[] pointCloud, Vector3[,] result3)
        {
            var sw3 = Stopwatch.StartNew();

            Parallel.For(0, textureSize, x =>
            {
                for(int y = 0; y < textureSize; y++)
                    Computation(pointCloud, result3, x, y);
            });
            sw3.Stop();
            return sw3;
        }

        private static Stopwatch NotInline(Vector3[] pointCloud, Vector3[,] result2)
        {
            var sw2 = Stopwatch.StartNew();
            for(int x = 0; x < textureSize; x++)
                for(int y = 0; y < textureSize; y++)
                    Computation(pointCloud, result2, x, y);
            sw2.Stop();
            return sw2;
        }

        private static Stopwatch Inline(Vector3[] pointCloud, Vector3[,] result1)
        {
            var sw1 = Stopwatch.StartNew();
            for(int x = 0; x < textureSize; x++)
                for(int y = 0; y < textureSize; y++)
                {
                    var targetPos = new Vector3(x, y, 0);
                    var nearestV3 = pointCloud[0];
                    Vector3 temp1 = nearestV3 - targetPos;
                    var nearestV3Distance = temp1.x * temp1.x + temp1.y * temp1.y + temp1.z * temp1.z;

                    for(int i = 1; i < numPoints; i++)
                    {
                        var currentV3 = pointCloud[i];
                        Vector3 temp2 = currentV3 - targetPos;
                        var currentV3Distance = temp2.x * temp2.x + temp2.y * temp2.y + temp2.z * temp2.z;
                        if(currentV3Distance < nearestV3Distance)
                        {
                            nearestV3 = currentV3;
                            nearestV3Distance = currentV3Distance;
                        }
                    }
                    result1[x, y] = nearestV3;
                }
            sw1.Stop();
            return sw1;
        }

        private static bool CheckResults(Vector3[,] lhs, Vector3[,] rhs)
        {
            for(int x = 0; x < textureSize; x++)
                for(int y = 0; y < textureSize; y++)
                    if(!lhs[x, y].Equals(rhs[x, y]))
                        return false;
            return true;
        }

        private static void Computation(Vector3[] pointCloud, Vector3[,] result, int x, int y)
        {
            var targetPos = new Vector3(x, y, 0);
            var nearestV3 = pointCloud[0];
            Vector3 temp1 = nearestV3 - targetPos;
            var nearestV3Distance = temp1.x * temp1.x + temp1.y * temp1.y + temp1.z * temp1.z;

            for(int i = 1; i < numPoints; i++)
            {
                var currentV3 = pointCloud[i];
                Vector3 temp2 = currentV3 - targetPos;
                var currentV3Distance = temp2.x * temp2.x + temp2.y * temp2.y + temp2.z * temp2.z;
                if(currentV3Distance < nearestV3Distance)
                {
                    nearestV3 = currentV3;
                    nearestV3Distance = currentV3Distance;
                }
            }
            result[x, y] = nearestV3;
        }

        private static float DistanceToPoint(Vector3 vector, Vector3 point)
        {
            Vector3 final = vector - point;

            return final.x * final.x + final.y * final.y + final.z * final.z;
        }

        struct Vector3
        {
            public float x;
            public float y;
            public float z;

            public Vector3(float x, float y, float z)
            {
                this.x = x;
                this.y = y;
                this.z = z;
            }
            public Vector3(float randomDistance)
            {
                this.x = (float)rnd.NextDouble() * randomDistance;
                this.y = (float)rnd.NextDouble() * randomDistance;
                this.z = (float)rnd.NextDouble() * randomDistance;
            }

            public static Vector3 operator -(Vector3 a, Vector3 b)
            {
                return new Vector3(a.x - b.x, a.y - b.y, a.z - b.z);
            }
        }
    }
}
于 2012-07-20T05:27:40.863 に答える
3

構造体は、64 ビット システムでも 12 バイトです。

DistanceToPoint のインライン化がないため、64 ビットは低速です

 2     0 [  0] TextureFromPoints.Program+Vector3.DistanceToPoint(Vector3)
23     0 [  0] Texture!TextureFromPoints.Program+Vector3.DistanceToPoint(Vector3)
22     0 [  1]   Texture!TextureFromPoints.Program+Vector3.op_Subtraction(Vector3, Vector3)
30    22 [  0] Texture!TextureFromPoints.Program+Vector3.DistanceToPoint(Vector3)
10     0 [  1]   Texture!TextureFromPoints.Program+Vector3.sqrMagnitude()
33    32 [  0] Texture!TextureFromPoints.Program+Vector3.DistanceToPoint(Vector3)

32 ビット システムでは、sqrtMagnitude のみが関数呼び出しで、DistanceToPoint と op_Subtraction はインライン化されます。

于 2012-07-20T05:31:27.967 に答える
1

64 ビットのパフォーマンスはアライメントに関係していると思われます。Vector3 は 12 バイトの構造です。これらは 32 ビット環境では 12 バイトを占めますが、64 ビット環境では 16 バイトにパディングされます。つまり、配列が 33% 大きくなると、キャッシュ ミスが 33% 増えると予想できます。

私の疑いは完全に間違っていました。その上で寝た後、私は次のことを試しました:

class Program
{
    private struct V3
    {
        public float x;
        public float y;
        public float z;
    }

    private static unsafe long GetDistance()
    {
        var array = new V3[2];
        fixed (V3* pointerOne = &array[0])
        fixed (V3* pointerTwo = &array[1])
            return ((byte*)pointerTwo - (byte*)pointerOne);
    }

    unsafe static void Main()
    {
        Console.WriteLine(GetDistance());
        Console.WriteLine(sizeof(IntPtr));
    }
}

出力、32 ビット システム:

12
4

出力、64 ビット システム:

12
8
于 2012-07-20T04:34:14.917 に答える
0

私は何をすべきか知っています!F#で書いてください!

Completed 1024x1024 pixels with 700 points in...
Sync: 4393ms
Parallel: 2409ms

それはより速く、そしてより小さく...言語の予備知識がほとんどまたはまったくない状態で私が数時間で一掃したものにとって悪くはありません。

module Program

open System
open System.IO
open System.Linq
open System.Threading.Tasks


let main() = 
    let numPoints = 700
    let textureSize = 1024
    let rnd = new Random()

    let randomPos() = (single (rnd.NextDouble()*(double textureSize)))
    let pointCloud = Array.init numPoints (fun _ -> (randomPos(), randomPos()))

    let distanceToPoint(sourceX :int ,sourceY : int, point ) = 
        let x = (single sourceX) - fst point
        let y = (single sourceY) - snd point
        x*x + y*y

    let syncCalc() =
        let resultData = Array2D.zeroCreate<single*single>  textureSize textureSize
        for x in 0..(textureSize-1) do
          for y in 0..(textureSize-1) do
             let mutable closestPoint = pointCloud.[0]
             let mutable closestDistance = distanceToPoint(x,y, closestPoint)
             for p in 1..(numPoints-1) do
                 let point = pointCloud.[p]
                 let distance = distanceToPoint(x,y, closestPoint)
                 if (distance < closestDistance) then
                    closestPoint <- point
                    closestDistance <- distance
             resultData.[x,y] <- closestPoint

    (*let asyncCalc() =
        let resultData = Array2D.zeroCreate<single*single>  textureSize textureSize
        let z = 
            Async.Parallel [ 
                for x in 0..(textureSize-1) -> async { 
                     for y in 0..(textureSize-1) do
                         let closestPoint = ref pointCloud.[0]
                         let closestDistance = ref (distanceToPoint(x,y, !closestPoint))
                         for p in 1..(numPoints-1) do
                             let point = pointCloud.[p]
                             let distance = distanceToPoint(x,y, !closestPoint)
                             if (distance < !closestDistance) then
                                closestPoint := point
                                closestDistance := distance
                         resultData.[x,y] <- !closestPoint
            } ]   |>Async.RunSynchronously 
        resultData*)

    let parallelCalc() =
        let resultData = Array2D.zeroCreate<single*single>  textureSize textureSize
        let z = 
            Parallel.For (0, textureSize,  fun x ->
                     for y in 0..(textureSize-1) do
                         let closestPoint = ref pointCloud.[0]
                         let closestDistance = ref (distanceToPoint(x,y, !closestPoint))
                         for p in 1..(numPoints-1) do
                             let point = pointCloud.[p]
                             let distance = distanceToPoint(x,y, !closestPoint)
                             if (distance < !closestDistance) then
                                closestPoint := point
                                closestDistance := distance
                         resultData.[x,y] <- !closestPoint)
        resultData

    //4.2s
    let sw1 = System.Diagnostics.Stopwatch.StartNew();
    let r1 = syncCalc() 
    sw1.Stop()

    //Slow!
    //let sw2 = System.Diagnostics.Stopwatch.StartNew();
    //let r2 = asyncCalc()      
    //sw2.Stop()

    //2.25s
    let sw3 = System.Diagnostics.Stopwatch.StartNew();
    let r3 = parallelCalc() 
    sw3.Stop()

    Console.WriteLine("Completed {0}x{0} pixels with {1} points in...", textureSize, numPoints)
    Console.WriteLine("Sync: {0}ms", sw1.ElapsedMilliseconds)
    //Console.WriteLine("ASync: {0}ms", sw2.ElapsedMilliseconds)
    Console.WriteLine("Parallel: {0}ms", sw3.ElapsedMilliseconds)
    Console.ReadLine() |> ignore

while true do main()
于 2012-07-22T10:41:57.067 に答える