Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/arrays/12.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 数组中的分段聚合_C#_Arrays_Parallel Processing - Fatal编程技术网

C# 数组中的分段聚合

C# 数组中的分段聚合,c#,arrays,parallel-processing,C#,Arrays,Parallel Processing,我有大量的基本值类型。该数组实际上是一维的,但在逻辑上表示二维字段。从左向右读取时,值需要变成(当前单元格的原始值)+(在左侧单元格中计算的结果)。显然,除了每行的第一个元素,它只是原始值 我已经有了一个实现来实现这一点,但是在整个阵列上完全是迭代的,对于大型(1M+元素)阵列来说速度非常慢 给定以下示例数组 0 0 1 0 0 2 0 0 0 3 0 4 1 1 0 0 1 0 4 1 变成 0 0 1 1 1 2 2 2 2 5 0 4 5 6 6 0 1 1 5 6 以此类推,直到出现

我有大量的基本值类型。该数组实际上是一维的,但在逻辑上表示二维字段。从左向右读取时,值需要变成(当前单元格的原始值)+(在左侧单元格中计算的结果)。显然,除了每行的第一个元素,它只是原始值

我已经有了一个实现来实现这一点,但是在整个阵列上完全是迭代的,对于大型(1M+元素)阵列来说速度非常慢

给定以下示例数组

0 0 1 0 0
2 0 0 0 3
0 4 1 1 0
0 1 0 4 1
变成

0 0 1 1 1
2 2 2 2 5
0 4 5 6 6
0 1 1 5 6
以此类推,直到出现问题的大小(1024x1024)

阵列需要更新(理想情况下),但如有必要,可以使用另一个阵列。内存占用不是什么大问题,但性能至关重要,因为这些阵列有数百万个元素,必须每秒处理数百次

由于单个单元的计算依赖于从左侧开始的值,因此它们似乎不可并行化,因此GPU加速似乎是不可能的。我已经调查了PLINQ,但是索引的必要性使得它很难实现

是否有另一种方法来组织数据以加快处理速度


如果使用创新技术实现高效的GPU处理是可行的,这将是非常可取的,因为这是当前必须从显卡中提取并推回到显卡的纹理数据。

嗯,我对GPU了解不多,但是我认为没有理由不能并行化它,因为依赖关系只是从左到右

行之间没有依赖关系。

0 0 1 0 0  - process on core1 |
2 0 0 0 3  - process on core1 |
-------------------------------
0 4 1 1 0  - process on core2 |
0 1 0 4 1  - process on core2 |
尽管上述说法并不完全正确。对于内存缓存,行之间仍然存在隐藏的依赖关系


可能会有缓存垃圾。为了理解这个问题,您可以阅读“缓存错误共享”,并了解如何克服它。

正如@Chris Eelmaa告诉您的那样,可以按行并行执行。使用Parallel.For可以这样重写:

static int[,] values = new int[,]{
    {0, 0, 1, 0, 0},
    {2, 0, 0, 0, 3},
    {0, 4, 1, 1, 0},
    {0, 1, 0, 4 ,1}};
static void Main(string[] args)
{
    int rows=values.GetLength(0);
    int columns=values.GetLength(1);
    Parallel.For(0, rows, (row) =>
    {
        for (var column = 1; column < columns; column++)
        {
            values[row, column] += values[row, column - 1];
        }
    });

    for (var row = 0; row < rows; row++)
    {
        for (var column = 0; column < columns; column++)
        {
            Console.Write("{0} ", values[row, column]);
        }
        Console.WriteLine();
    }
静态int[,]值=新int[,]{
{0, 0, 1, 0, 0},
{2, 0, 0, 0, 3},
{0, 4, 1, 1, 0},
{0, 1, 0, 4 ,1}};
静态void Main(字符串[]参数)
{
int rows=values.GetLength(0);
int columns=values.GetLength(1);
平行。对于(0,行,(行)=>
{
对于(变量列=1;列<列;列++)
{
值[行,列]+=值[行,列-1];
}
});
对于(变量行=0;行<行;行++)
{
对于(变量列=0;列<列;列++)
{
Write(“{0}”,值[行,列];
}
Console.WriteLine();
}
所以,正如你在问题中所说,你有一个一维数组,代码会快一点:

static void Main(string[] args)
{
    var values = new int[1024 * 1024];
    Random r = new Random();
    for (int i = 0; i < 1024; i++)
    {
        for (int j = 0; j < 1024; j++)
        {
            values[i * 1024 + j] = r.Next(25);
        }
    }

    int rows = 1024;
    int columns = 1024;

    Stopwatch sw = Stopwatch.StartNew();
    for (int i = 0; i < 100; i++)
    {
        Parallel.For(0, rows, (row) =>
        {
            for (var column = 1; column < columns; column++)
            {
                values[(row * columns) + column] += values[(row * columns) + column - 1];
            }
        });
    }

    Console.WriteLine(sw.Elapsed);
}
static void Main(字符串[]args)
{
var值=新整数[1024*1024];
随机r=新随机();
对于(int i=0;i<1024;i++)
{
对于(int j=0;j<1024;j++)
{
值[i*1024+j]=r.Next(25);
}
}
int行=1024;
int列=1024;
秒表sw=Stopwatch.StartNew();
对于(int i=0;i<100;i++)
{
平行。对于(0,行,(行)=>
{
对于(变量列=1;列<列;列++)
{
值[(行*列)+列]+=值[(行*列)+列-1];
}
});
}
控制台写入线(软件运行时间);
}

但速度不如GPU快。要使用并行GPU处理,您必须在中重写它,或者看看如何将此并行程序移植到cudafy:

正确的编码,并了解一下.NET如何知道这些东西也有帮助:-)

在这种情况下适用的一些经验法则:

  • 如果您可以提示JIT索引永远不会超出数组的边界,那么它将删除额外的分支
  • 如果它真的很慢(f.ex.>1秒),你应该只在多个线程中对它进行矢量化。否则,任务切换、缓存刷新等可能会消耗增加的速度,结果会更糟
  • 如果可能的话,让内存访问可以预测,甚至是按顺序进行。如果你需要另一个数组,那就这样吧——如果不需要,就这样做吧
  • 如果你想提高速度,尽可能少使用IL指令。通常这似乎是可行的
  • 测试多个迭代。单个迭代可能不够好
  • 使用这些规则,您可以制作一个小测试用例,如下所示。注意,我已经将赌注增加到4Kx4K,因为1K太快了,您无法测量它:-)

    publicstaticvoidmain(字符串[]args)
    {
    整数宽度=4096;
    内部高度=4096;
    int[]ar=新int[宽度*高度];
    随机rnd=新随机(213);
    对于(int i=0;iJIT会注意到这不会超出范围,因为[0
    {
    整数和=0;
    对于(变量i=宽度*a+1;i<宽度*(a+1);i++)
    {
    ar[i]=(和+=ar[i]);
    }
    });
    WriteLine(“这花费了{0:0.0000}”,sw.appead.TotalSeconds);
    }
    
    <>如果你真的需要性能,你可以编译它到C++,使用p/javek。即使你不使用GPU,我想SSE /AVX指令可能已经给你一个显著的性能提升,你不会用.NET/C语言来得到。我想指出的是,英特尔C++编译器可以自动矢量化你的代码——甚至到Xeon。PHI’s.不需要很多努力,这可能会给您的性能带来很好的提升。

    public static void Main(string[] args)
    {
        int width = 4096;
        int height = 4096;
    
        int[] ar = new int[width * height];
        Random rnd = new Random(213);
        for (int i = 0; i < ar.Length; ++i)
        {
            ar[i] = rnd.Next(0, 120);
        }
    
        // (5)...
        for (int j = 0; j < 10; ++j)
        {
            Stopwatch sw = Stopwatch.StartNew();
    
            int sum = 0;
            for (int i = 0; i < ar.Length; ++i)  // (3) sequential access
            {
                if ((i % width) == 0)
                {
                    sum = 0;
                }
    
                // (1) --> the JIT will notice this won't go out of bounds because [0<=i<ar.Length]
                // (5) --> '+=' is an expression generating a 'dup'; this creates less IL.
                ar[i] = (sum += ar[i]); 
            }
    
            Console.WriteLine("This took {0:0.0000}s", sw.Elapsed.TotalSeconds);
        }
        Console.ReadLine();
    }
    
    for (int j = 0; j < 10; ++j)
    {
        Stopwatch sw = Stopwatch.StartNew();
        Parallel.For(0, height, (a) =>
        {
            int sum = 0;
            for (var i = width * a + 1; i < width * (a + 1); i++)
            {
                ar[i] = (sum += ar[i]);
            }
        });
        Console.WriteLine("This took {0:0.0000}s", sw.Elapsed.TotalSeconds);
    }
    
    int[] texture;
    
    int[][] texture;
    
    private static Task ProcessRow(int[] row)
    {
        var v = row[0];
        for (var i = 1; i < row.Length; i++)
        {
            v = row[i] += v;
        }
    
        return Task.FromResult(true);
    }
    
    Task.WhenAll(texture.Select(ProcessRow)).Wait();
    
    private static Task ProcessRow(int[] texture, int start, int limit)
    {
        var v = texture[start];
        for (var i = start + 1; i < limit; i++)
        {
            v = texture[i] += v;
        }
    
        return Task.FromResult(true);
    }
    
    var rowSize = 1024;
    var rows =
        Enumerable.Range(0, texture.Length / rowSize)
        .Select(i => Tuple.Create(i * rowSize, (i * rowSize) + rowSize))
        .ToArray();
    
    Task.WhenAll(rows.Select(t => ProcessRow(texture, t.Item1, t.Item2)).Wait();