gpt4 book ai didi

c# - 从多个并行线程读取大型数据集的性能

转载 作者:太空宇宙 更新时间:2023-11-03 21:44:20 26 4
gpt4 key购买 nike

我正在从事一个在 .Net 中开发的遗传机器学习项目(相对于 Matlab – My Norm)。我不是专业的 .net 编码员,所以请原谅任何笨拙的实现。

该项目本身非常庞大,因此我不会向您介绍全部细节,但基本上,大量人工神经网络(如决策树)都在一个问题域上进行评估,在本例中,该问题域使用了一系列感官输入。种群中表现最好的被允许繁殖和产生后代(继承 parent 双方的倾向),而表现不佳的则被杀死或从种群中繁殖出来。进化一直持续到找到可接受的解决方案。一旦找到,最终进化的“网络”就会从实验室中提取出来,并放置在一个轻量级的现实世界应用程序中。该技术可用于开发非常复杂的控制解决方案,通常编程几乎不可能或太耗时,例如自动汽车驾驶、机械稳定性控制、数据中心负载平衡等。

无论如何,该项目到目前为止取得了巨大的成功,并产生了惊人的结果,但唯一的问题是一旦我转向更大的数据集,性能就会非常慢。我希望只是我的代码,所以非常感谢专家的帮助。

在这个项目中,收敛到接近理想的解决方案通常需要大约 7 天的处理时间!只是对参数进行一点调整并等待结果太痛苦了。

基本上,多个并行线程需要读取一个非常大的数据集的顺序部分(数据一旦加载就不会改变)。该数据集由大约 300 到 1000 个连续的 Double 和超过 500k 行的任何内容组成。由于数据集可以超过 2GB 的 .Net 对象限制,它不能存储在普通的二维数组中——最简单的方法是使用单个数组的通用列表。

并行可扩展性似乎是一个很大的限制因素,因为在具有 32 个 Xeon 核心的服务器上运行代码通常吃大数据集作为早餐不会比 Corei3 桌面产生太多性能提升!

随着内核数量的增加,性能提升会迅速减少。

通过分析代码(以我有限的知识),我得到的印象是从多个线程读取数据集存在大量争用。

我曾尝试使用 Jagged 数组和各种并发集合来试验不同的数据集实现,但无济于事。

我已经为基准测试编写了一些快速而肮脏的代码,它类似于原始的核心实现,但仍然表现出类似的读取性能问题和并行可伸缩性问题。

任何想法或建议将不胜感激或确认这是我将要得到的最好的。

非常感谢

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading.Tasks;

//Benchmark script to time how long it takes to read dataset per iteration

namespace Benchmark_Simple
{
class Program

{
public static TrainingDataSet _DataSet;
public static int Features = 100; //Real test will require 300+
public static int Rows = 200000; //Real test will require 500K+
public static int _PopulationSize = 500; //Real test will require 1000+
public static int _Iterations = 10;
public static List<NeuralNetwork> _NeuralNetworkPopulation = new List<NeuralNetwork>();

static void Main()
{
Stopwatch _Stopwatch = new Stopwatch();

//Create Dataset
Console.WriteLine("Creating Training DataSet");
_DataSet = new TrainingDataSet(Features, Rows);
Console.WriteLine("Finished Creating Training DataSet");

//Create Neural Network Population
for (int i = 0; i <= _PopulationSize - 1; i++)
{
_NeuralNetworkPopulation.Add(new NeuralNetwork());
}

//Main Loop
for (int i = 0; i <= _Iterations - 1; i++)
{
_Stopwatch.Restart();

Parallel.ForEach(_NeuralNetworkPopulation, _Network => { EvaluateNetwork(_Network); });

//######## Removed for simplicity ##########

//Run Evolutionary Genetic Algorithm on population - I.E. Breed the strong, kill of the weak

//##########################################
//Repeat until acceptable solution is found

Console.WriteLine("Iteration time: {0}", _Stopwatch.ElapsedMilliseconds / 1000);

_Stopwatch.Stop();

}

Console.ReadLine();

}

private static void EvaluateNetwork(NeuralNetwork Network)
{
//Evaluate network on 10% of the Training Data at a random starting point

double Score = 0;

Random Rand = new Random();

int Count = (Rows / 100) * 10;

int RandonStart = Rand.Next(0, Rows - Count);

//The data must be read sequentially
for (int i = RandonStart; i <= RandonStart + Count; i++)
{
double[] NetworkInputArray = _DataSet.GetDataRow(i);

//####### Dummy Evaluation - just give it somthing to do for the sake of it
double[] Temp = new double[NetworkInputArray.Length + 1];
for (int j = 0; j <= NetworkInputArray.Length - 1; j++)
{
Temp[j] = Math.Log(NetworkInputArray[j] * Rand.NextDouble());
}
Score += Rand.NextDouble();
//##################
}
Network.Score = Score;
}

public class TrainingDataSet
{
//Simple demo class of fake data for benchmarking

private List<double[]> DataList = new List<double[]>();

public TrainingDataSet(int Features, int Rows)
{
Random Rand = new Random();

for (int i = 1; i <= Rows; i++)
{
double[] NewRow = new double[Features];
for (int j = 0; j <= Features - 1; j++)
{
NewRow[j] = Rand.NextDouble();
}
DataList.Add(NewRow);
}

}

public double[] GetDataRow(int Index)
{
return DataList[Index];
}

}

public class NeuralNetwork
{
//Simple Class to represent a dummy Neural Network -
private double _Score;
public NeuralNetwork()
{
}

public double Score
{
get { return _Score; }
set { _Score = value; }
}

}
}
}

最佳答案

首先,回答任何性能问题的唯一方法是分析应用程序。我正在使用 VS 2012 内置分析器 - 还有其他的 https://stackoverflow.com/a/100490/19624

从最初通读代码开始,即静态分析,唯一让我印象深刻的是循环内 Temp 的持续重新分配;这效率不高,如果可能的话需要移出循环。

使用分析器,您可以看到发生了什么:

Profile Summary

我首先使用您发布的代码进行了概要分析(如果您没有发布完整的可编译问题示例,我将获得最高分)。

这告诉我,大部分在循环内部,我将分配移动到 Parallel.ForEach 循环。

Parallel.ForEach(_NeuralNetworkPopulation, _Network => 
{
double[] Temp = new double[Features + 1];
EvaluateNetwork(_Network, Temp);
});

Original Code

所以我从上面可以看出,在重新分配上有 4.4% 的浪费;但可能不足为奇的是,内循环占据了 87.6%。

这让我想到了我的第一条优化规则,即首先检查您的算法而不是优化代码。好的算法的糟糕实现通常比高度优化的糟糕算法更快。

去掉Temp的重复分配,图片略有变化;

After moving allocation to outer loop

还值得通过指定并行度进行一些调整;我发现 Parallel.ForEach 足以满足我的使用需求,但同样,通过手动将工作划分为队列可能会获得更好的结果。

Parallel.ForEach(_NeuralNetworkPopulation, 
new ParallelOptions { MaxDegreeOfParallelism = 32 },
_Network =>
{
double[] Temp = new double[Features + 1];
EvaluateNetwork(_Network, Temp);
});

在运行时,我得到了我所期望的 CPU 使用率:尽管我的机器还在运行另一个耗时的进程,该进程处于基本级别(下表中的峰值是在分析此程序时)。

enter image description here

总结一下

  1. 回顾最常执行的部分,并在可能的情况下提出新算法。
  2. 目标机器上的配置文件
  3. 只有当您确定上面的 (1) 时,才值得考虑优化算法;考虑到以下a) 代码优化b) 内存调整/数据分区以在缓存中保留尽可能多的数据c) 线程使用的改进

关于c# - 从多个并行线程读取大型数据集的性能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17838456/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com