我为代码性能编写了代码Methode#1和Methode#2。 Methode#1使用construnction,而Methode#2使用Parallel.Invoke。在第二种情况下工作非常缓慢。我无法理解为什么会这样?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Concurrent;
using Mpir.NET;
using System.Runtime.Serialization;
using System.Diagnostics;
namespace ConsoleApplication4
{
class Program
{
public class numbers
{
public numbers(mpz_t p, mpz_t q)
{
this.q = q;
this.p = p;
}
mpz_t q;
mpz_t p;
};
static void Main(string[] args)
{
Int32 arraySize = 400;
ConcurrentBag<numbers> pairsCB = new ConcurrentBag<numbers>();
ConcurrentBag<Action> cbTasks = new ConcurrentBag<Action>();
HashSet<numbers> uniqueCB = new HashSet<numbers>(pairsCB.ToArray());
mpz_t[] numbersArray = new mpz_t[arraySize];
for (Int32 i = 0; i < arraySize; i++)
{
numbersArray[i] = i*i+i+1;
}
//Methode #1
Stopwatch stopwatch1 = new Stopwatch();
stopwatch1.Start();
for (Int32 j = 0; j < arraySize; j++)
{
checkDivisible(numbersArray[j], pairsCB);
}
uniqueCB = new HashSet<numbers>(pairsCB.ToArray());
stopwatch1.Stop();
Console.WriteLine("Methode Count Unique Pairs Count:{0}\tTime elapsed:{1}", uniqueCB.Count(), stopwatch1.Elapsed);
//Methode #2
Stopwatch stopwatch2 = new Stopwatch();
stopwatch2.Start();
pairsCB = new ConcurrentBag<numbers>();
for(Int32 j = 0; j < arraySize; j++)
{
mpz_t value = numbersArray[j];
cbTasks.Add(new Action(() => checkDivisible(value, pairsCB)));
}
Action[] tasks = cbTasks.ToArray();
Parallel.Invoke(tasks);
stopwatch2.Stop();
Console.WriteLine("Methode Count Unique Pairs Count:{0}\tTime elapsed:{1}", uniqueCB.Count(), stopwatch2.Elapsed);
Console.ReadKey();
}
private static void checkDivisible(mpz_t n, ConcurrentBag<numbers> pq)
{
mpz_t p = 1;
mpz_t q = 1;
for (Int32 i = 2; i < n; i++)
{
if (n % i == 0)
{
q = i;
p = n / i;
pq.Add(new numbers(p, q));
}
}
}
}
}
答案 0 :(得分:2)
您可以使用替代方案:
//Methode #2
Stopwatch stopwatch2 = new Stopwatch();
stopwatch2.Start();
pairsCB = new ConcurrentBag<numbers>();
Parallel.For(0, arraySize, (index) =>
{
checkDivisible(numbersArray[index], pairsCB);
});
stopwatch2.Stop();
使用arraySize = 1000输出
Methode Count Unique Pairs Count:3878 Time elapsed:00:00:01.5671572
Methode Count Unique Pairs Count:3878 Time elapsed:00:00:00.7917211
答案 1 :(得分:2)
第二种方法速度慢的原因有很多。
checkDivisible
没有做足够的工作来证明并行化的合理性。并行化和同步的开销可能比任何好处都大。checkDivisible
的所有调用都会写入同一个集合,该集合将成为热点。最好从每次调用返回一个简单的数组,最后在最后一步中合并所有数组。Parallel.For
或Parallel.ForEach
知道所有呼叫都是相同的,因此他们可以根据处理器的数量对数据进行分区,从而确保并行化开销最小化。第一步是修改checkDivisible
:
private static List<number> checkDivisible(mpz_t n)
{
mpz_t p = 1;
mpz_t q = 1;
List<number> nums=new List<numbers>();
for (Int32 i = 2; i < n; i++)
{
if (n % i == 0)
{
q = i;
p = n / i;
nums.Add(new numbers(p, q));
}
}
return numbers;
}
我更喜欢迭代器方法,因为它避免了创建List只是为了收集结果。
然后你可以使用Parallel.For:
var results=new ConcurrentQueue<IList<numbers>>();
Parallel.For(0, arraySize, (index) =>
{
var local=checkDivisible(numbersArray[index]);
results.Add(local);
});
var final=results.SelectMany(r=>r).ToList();
最后一步可以是您想要的任何内容,以确保结果是您想要的形式,例如。使用ToDictionary或ToLookup根据键合并结果。
另一种选择是使用PLINQ以更简洁的方式做同样的事情。将checkDivision
更改为迭代器:
private static IEnumerable<number> checkDivisible(mpz_t n)
{
mpz_t p = 1;
mpz_t q = 1;
for (Int32 i = 2; i < n; i++)
{
if (n % i == 0)
{
q = i;
p = n / i;
yield return new numbers(p, q);
}
}
}
你可以写:
var results= (from n in numbersArray.AsParallel()
from number in checkDivisible(n)
select n).ToList();
就像Parallel.For一样,PLINQ将根据计算机上的核心数量对numbersArray
中的数据进行分区,并行处理分区,最后将它们合并到一个列表中。