为什么数组初始化会减慢多线程处理的速度



我正在尝试为一个在多个维度上散列坐标的方法添加线程支持。这里的长期目标是包括FNV1A散列,但只要在Hash方法中简单地初始化坐标数组,就会出现速度减慢的情况。

我迭代了一百万次,对于一个线程,我得到了大约300ms的秒表时间。对于8个线程,时间会缩短到约6000ms。

这是一个虚假的共享问题吗?如果是这样的话,我担心散列会随着填充和偏移量注入数组而恶化。如果您对使用本地数组执行此操作有任何帮助,我们将不胜感激。非常感谢!

public class Foo : MonoBehaviour {
#region Fields
private readonly int iterations = 1000000;
private readonly int threadNum = 1;
private int iterationsCompleted = 0;
#endregion
void Start () {
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
Multithread();
stopWatch.Stop();
UnityEngine.Debug.Log(stopWatch.Elapsed.TotalMilliseconds);
}
private void Multithread() {
for (int i = 0; i < threadNum; i++) {
Hash hash = new Hash();
new Thread(() => {
while (Interlocked.Increment(ref iterationsCompleted) < iterations) {
hash.Get(0, 0, 0);
}
UnityEngine.Debug.Log("Finished thread");
}).Start();
}
while (iterationsCompleted < iterations);
}
}
public class Hash {
#region Fields
// FNV parameters can be found at http://www.isthe.com/chongo/tech/comp/fnv/#FNV-param
private const uint _FNVPrime = 16777619;
private const uint _FNVOffset = 2166136261;
private const uint _FNVMask8 = (1<<8)-1;
#endregion
#region Class Methods
private static uint FNV1A(uint[] data) {
uint hash = _FNVOffset;
int dataSize = data.Length * sizeof(UInt32);
byte[] byteArray = new byte[dataSize];
Buffer.BlockCopy(data, 0, byteArray, 0, dataSize);
for (int i = 0; i < dataSize; i++) {
hash = hash ^ byteArray[i];
hash = hash * _FNVPrime;
}
return hash;
}
public uint Get(int x, int y, uint seed) {
uint[] data = new uint[3] { (uint)x, (uint)y, seed };
//return FNV1A(data);
return 0;
}
#endregion
}

内存分配似乎是这里的问题。我制作了两个数组static,然后在函数之外分配了一次内存。每次访问它们时,Ilocked都会数组,以确保另一个Thread没有访问它们。即使使用了lock关键字,也不确定我的代码有多安全。

无相关问题

在主CCD_ 8上调用CCD_。这是不好的,每次调用Multithread()并仍在运行时,都会临时冻结Unity。我在上面添加了另一个Thread,它将启动调用的whenMultithread()。因此,现在从这个新的Thread而不是主Thread调用其他线程。

我的电脑上的测试结果

原始代码

1 thread = 454.3515
8 threads = 655.008

修改代码

1 thread = 296.794
8 threads = 107.8898

CCD_ 14线程的性能比CCD_。所有测试都使用代码中包含的return FNV1A(data);和从代码中删除/注释的return 0;进行。

现在,你的工作是确保你得到的数据/哈希是你所期望的。

public class Foo : MonoBehaviour
{
#region Fields
private readonly int iterations = 1000000;
private readonly int threadNum = 1;
private int iterationsCompleted = 0;
#endregion
void Start()
{
Multithread();
}
private void Multithread()
{
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
//Start new Thread so that while (iterationsCompleted < iterations) ; will not run in the main loop
new Thread(() =>
{
for (int i = 0; i < threadNum; i++)
{
Hash hash = new Hash();
new Thread(() =>
{
while (Interlocked.Increment(ref iterationsCompleted) < iterations)
{
hash.Get(0, 0, 0);
}
UnityEngine.Debug.Log("Finished thread");
}).Start();
}
while (iterationsCompleted < iterations) ;
stopWatch.Stop();
UnityEngine.Debug.Log(stopWatch.Elapsed.TotalMilliseconds);
}).Start();
}
}
public class Hash
{
#region Fields
// FNV parameters can be found at http://www.isthe.com/chongo/tech/comp/fnv/#FNV-param
private const uint _FNVPrime = 16777619;
private const uint _FNVOffset = 2166136261;
private const uint _FNVMask8 = (1 << 8) - 1;
private const int FIXEDSIZE = 3;
private readonly System.Object locker = new System.Object();
#endregion
#region Class Methods
private static uint FNV1A(uint[] data)
{
uint hash = _FNVOffset;
Buffer.BlockCopy(data, 0, byteArray, 0, byteArray.Length);
for (int i = 0; i < byteArray.Length; i++)
{
hash = hash ^ byteArray[i];
hash = hash * _FNVPrime;
}
return hash;
}
static byte[] byteArray = new byte[FIXEDSIZE * sizeof(UInt32)];
static uint[] data = new uint[3] { (uint)0, (uint)0, 0 };
public uint Get(int x, int y, uint seed)
{
lock (locker)
{
data[0] = (uint)x;
data[1] = (uint)y;
data[2] = (uint)seed;
return FNV1A(data);
}
}
#endregion
}

最新更新