Helllo!
我正在尝试构建一个程序,使用强力计算各种项目的最佳分数。我相信,借助线程,在具有多核CPU的系统上可以提高计算速度。如我错了请纠正我。但是我如何实现这个目标呢?
这是我的代码的一部分,它可以很好地完成工作(没有线程)。
private double _bestScore = 0.0;
private void BruteForce()
{
foreach (Stats head in _heads)
foreach (Stats chest in _chests)
foreach (Stats leg in _legs)
foreach (Stats feet in _feets)
{
int stamina = head.sta + chest.sta + leg.sta + feet.sta;
int power = head.power + chest.power + leg.power + feet.power;
int armor = head.armor + chest.armor + leg.armor + feet.armor;
int hit = head.hit + chest.hit + leg.hit + feet.hit;
double total = stamina * _scaleSta + power * _scalePower + armor * _scaleArmor;
if (total > _bestScore && hit >= 100)
{
_bestScore = total;
// Store best setup for output when done with bruteforce
_bestHead = head;
_bestChest = chest;
_bestLeg = leg;
_bestFeet = feet;
}
}
}
那么,无论如何都要通过线程来改善这一点吗?
编辑:修正了拼写错误并更新为包含其他统计信息。命中必须达到100才能成为“最佳得分”的一部分。这样做我不能先检查每个插槽,以尝试找到最好的装备。命中是一个非常重要的数据,达到100,但100之后的每一点都是无用的。 此外,我的代码的这一部分有更多的foreach循环(28而不是4)。所以迭代次数很多。但是,每个列表(_heads,_chests等)通常最多包含2个项目。
答案 0 :(得分:3)
如果您想要添加多线程的简单方法,请查看Parallel Extensions for .NET。出于性能原因,您只需要在最外层循环上调用Parallel.For()。
答案 1 :(得分:1)
我很确定会这样做,没有任何测试数据或编译器可以让我不是100%自信:
private void BestItems()
{
_bestHead = GetBestItem(_heads);
_bestChest = GetBestItem(_chests);
_bestLeg = GetBestItem(_legs);
_bestFeet = GetBestItem(_feets);
}
private Stats GetBestItem(List<Stats> items)
{
double best = 0.0;
Stats result = null;
foreach stats item in items
{
double total = item.stamina * _scaleSta + item.power * _scalePower + item.armor * _scaleArmor;
if (total > best)
{
result = item;
}
}
return result;
}
修改强>
步骤:
你将需要一个接一个的循环,但它比2 ^ 28更好我认为:p
Edit2:再次,这里仍然没有编译器......但这可能有效。你最终会得到一桶装的线程......
用于线程加入和等待see here (msdn)(查看互斥锁,监视器,ManualResetEvent,AutoResetEvent)
private void BruteForce()
{
var threads = new List<Thread>;
foreach (Stats head in _heads)
foreach (Stats chest in _chests)
foreach (Stats leg in _legs)
foreach (Stats feet in _feets)
{
if (threads.Count <= 2)
thread worker = new thread(addressof Process, new object() {head, chest, leg, feet, ...});
worker.start();
threads.add(worker);
}
foreach (Thread t in threads)
t.join(); //this might not be the best as it might make the main thread wait for each thread one after the other, not when all finished. A manual/auto reset is probably better here.
}
private void Process(params...)
{
int stamina = head.sta + chest.sta + leg.sta + feet.sta;
int power = head.power + chest.power + leg.power + feet.power;
int armor = head.armor + chest.armor + leg.armor + feet.armor;
int hit = head.hit + chest.hit + leg.hit + feet.hit;
double total = stamina * _scaleSta + power * _scalePower + armor * _scaleArmor;
lock _bestscore
{
if (total > _bestScore && hit >= 100)
{
_bestScore = total;
// Store best setup for output when done with bruteforce
_bestHead = head;
_bestChest = chest;
_bestLeg = leg;
_bestFeet = feet;
}
}
}
编辑4:猜猜谁还没有靠近他的编译器? 根据这一点,应该确保你在任何时候只有2个线程存活。
var threads = new Dictionary<Guid, Thread>;
private void BruteForce()
{
foreach (Stats head in _heads)
foreach (Stats chest in _chests)
foreach (Stats leg in _legs)
foreach (Stats feet in _feets)
{
while (threads.Count >= 2) {} //im sure thread.join or equivelent can do this instead of a nasty loop :p
var guid = Guid.NewGuid();
thread worker = new thread(addressof Process, new object() {guid, head, chest, leg, feet, ...});
worker.start();
threads.add(guid, worker);
}
foreach (Thread t in threads)
t.join(); //this might not be the best as it might make the main thread wait for each thread one after the other, not when all finished. A manual/auto reset is probably better here.
}
private void Process(params...)
{
int stamina = head.sta + chest.sta + leg.sta + feet.sta;
int power = head.power + chest.power + leg.power + feet.power;
int armor = head.armor + chest.armor + leg.armor + feet.armor;
int hit = head.hit + chest.hit + leg.hit + feet.hit;
double total = stamina * _scaleSta + power * _scalePower + armor * _scaleArmor;
lock _bestscore
{
if (total > _bestScore && hit >= 100)
{
_bestScore = total;
// Store best setup for output when done with bruteforce
_bestHead = head;
_bestChest = chest;
_bestLeg = leg;
_bestFeet = feet;
}
}
_threads.remove(guid)
}
答案 2 :(得分:0)
你可能会发现添加多个线程实际上会减慢速度。这个计算很慢吗?您对头部,胸部,腿部和脚部的平均预期数量是多少?
你可能最好找到每个头部,胸部等的最高值并丢弃其余部分,找到这些选定部分的最佳组合。
答案 3 :(得分:0)
这个问题的一个方法是取消一些循环,因此创建一些可以并行处理的工作项。像这样:
private struct Stat
{
int sta;
int power;
int armor;
int hit;
};
private List<Stat[]> workItems = new List<Stat[]>();
private const int NUMBER_OF_CPUS = 4;
private void BruteForce()
{
//create some work load to split into separate threads
//we do this by unnesting the first 3 loops.
//maybe more unnesting is needed to get some more work items
//for thread to process
//at least one item per thread/CPU makes sense
foreach (Stats head in _heads)
foreach (Stats chest in _chests)
foreach (Stats leg in _legs)
{
this.workItems .Add(new Stat[3] { head, chest, leg });
}
接下来将把工作项分成可以由单个线程处理的块。您将选择一些等于CPU核心数的线程。你的线程函数至少会有一个参数提供,线程工作项。
线程函数的开头可能如下所示:
private void Process(object param)
{
List<Stat[]> threadWorkitems = (List<Stat[]>)param;
foreach (Stat workItem in threadWorkitems)
{
Stats head = threadWorkitems[0];
Stats chest = threadWorkitems[1];
Stats leg = threadWorkitems[2];
foreach (Stats feet in _feets)
{
int stamina = head.sta + chest.sta + leg.sta + feet.sta;
int power = head.power + chest.power + leg.power + feet.power;
int armor = head.armor + chest.armor + leg.armor + feet.armor;
int hit = head.hit + chest.hit + leg.hit + feet.hit;
double total = stamina * _scaleSta + power * _scalePower + armor * _scaleArmor;
if (total > _bestScore && hit >= 100)
{
这段代码中缺少的是,你必须收集所有线程的最佳结果,然后比较它们以找到你真正的最佳设置。
我希望这会给出一些提示。
问候