我正在尝试使用Alea加快正在研究的程序,但我需要一些帮助。
我需要做的是许多位和位操作,其中有两个数组中存储的值。
对于我的第一个数组的每个元素,我必须做一个dotwise&与我的第二个数组的每个元素一起操作,然后将设置的位计为&结果。
如果结果大于/等于某个值,我需要退出内部并转到我第一个数组的下一个元素。
第一个数组通常是一个很大的数组,有数百万个元素,第二个元素通常小于200.000个元素。
尝试并行执行所有这些操作,这是我的代码:
[GpuManaged]
private long[] Check(long[] arr1, long[] arr2, int limit)
{
Gpu.FreeAllImplicitMemory(true);
var gpu = Gpu.Default;
long[] result = new long[arr1.Length];
gpu.For(0, arr1.Length, i =>
{
bool found = false;
long b = arr1[i];
for (int i2 = 0; i2 < arr2.Length; i2++)
{
if (LibDevice.__nv_popcll(b & arr2[i2]) >= limit)
{
found = true;
break;
}
}
if (!found)
{
result[i] = b;
}
});
return result;
}
这是按预期工作的,但比我在Quad Core CPU上并行运行的版本要快一点。
我肯定在这里错过了一些东西,这是我第一次编写GPU代码。
顺便说一句,我的nvidia是Geforce GT 740m。
编辑
至少在我的PC上,以下代码比上一个代码快2倍。非常感谢迈克尔·兰德尔(Michael Randall(将我指向正确的方向。
private static int[] CheckWithKernel(Gpu gpu, int[] arr1, int[] arr2, int limit)
{
var lp = new LaunchParam(16, 256);
var result = new int[arr1.Length];
try
{
using (var dArr1 = gpu.AllocateDevice(arr1))
using (var dArr2 = gpu.AllocateDevice(arr2))
using (var dResult = gpu.AllocateDevice<int>(arr1.Length))
{
gpu.Launch(Kernel, lp, arr1.Length, arr2.Length, dArr1.Ptr, dArr2.Ptr, dResult.Ptr, limit);
Gpu.Copy(dResult, result);
return result;
}
}
finally
{
Gpu.Free(arr1);
Gpu.Free(arr2);
Gpu.Free(result);
}
}
private static void Kernel(int a1, int a2, deviceptr<int> arr1, deviceptr<int> arr2, deviceptr<int> arr3, int limit)
{
var iinit = blockIdx.x * blockDim.x + threadIdx.x;
var istep = gridDim.x * blockDim.x;
for (var i = iinit; i < a1; i += istep)
{
bool found = false;
int b = arr1[i];
for (var j = 0; j < a2; j++)
{
if (LibDevice.__nv_popcll(b & arr2[j]) >= limit)
{
found = true;
break;
}
}
if (!found)
{
arr3[i] = b;
}
}
}
更新
似乎固定无法使用gchandle.alloc((
但是,这个答案的目的是,您将获得更大的性能从直接内存访问中获得。
http://www.aleagpu.com/release/3_0_3/doc/advanced_features_csharp.html
直接与设备内存
设备内存提供了更大的灵活性,因为它也允许所有 一种指针算术。设备内存分配
Memory<T> Gpu.AllocateDevice<T>(int length)
Memory<T> Gpu.AllocateDevice<T>(T[] array)
第一个过载为指定的设备内存对象创建了一个设备内存对象 在选定的GPU上键入
T
和长度。第二个分配 在GPU上存储并将.NET阵列复制到其中。两者都返回一个Memory<T>
对象,该对象实现IDisposable
,因此可以 支持使用语法,该语法可确保一旦Memory<T>
对象不在范围。A Memory<T>
对象具有属性 确定长度,GPU或其寿命的设备。这Memory<T>.Ptr
属性返回deviceptr<T>
,可以在 GPU代码以访问实际数据或执行指针算术。 以下示例说明了设备的简单用例 指针。内核仅根据数据的一部分运行,由 偏移。
using (var dArg1 = gpu.AllocateDevice(arg1))
using (var dArg2 = gpu.AllocateDevice(arg2))
using (var dOutput = gpu.AllocateDevice<int>(Length/2))
{
// pointer arithmetics to access subset of data
gpu.Launch(Kernel, lp, dOutput.Length, dOutput.Ptr, dArg1.Ptr + Length/2, dArg2.Ptr + Length / 2);
var result = dOutput.ToArray();
var expected = arg1.Skip(Length/2).Zip(arg2.Skip(Length/2), (x, y) => x + y);
Assert.That(result, Is.EqualTo(expected));
}
原始答案
忽略了进行的逻辑,或与GPU代码有多相关。但是,您可以赞美您的并行例行程序,并可能通过固定 arrays 在使用GCHandle.Alloc()
和GCHandleType.Pinned
标志的内存中加快速度。>直接指针访问(如果可以运行unsafe
代码(
注释
您将因固定内存而受到打击,但是对于大数组,您可以从直接访问中实现很多性能*
您必须在构建属性中标记您的汇编*
这显然是未经测试的,只是一个例子*
您可以使用固定,但是平行的lambda使它变得烦恼
示例
private unsafe long[] Check(long[] arr1, long[] arr2, int limit)
{
Gpu.FreeAllImplicitMemory(true);
var gpu = Gpu.Default;
var result = new long[arr1.Length];
// Create some pinned memory
var resultHandle = GCHandle.Alloc(result, GCHandleType.Pinned);
var arr2Handle = GCHandle.Alloc(result, GCHandleType.Pinned);
var arr1Handle = GCHandle.Alloc(result, GCHandleType.Pinned);
// Get the addresses
var resultPtr = (int*)resultHandle.AddrOfPinnedObject().ToPointer();
var arr2Ptr = (int*)arr2Handle.AddrOfPinnedObject().ToPointer();
var arr1Ptr = (int*)arr2Handle.AddrOfPinnedObject().ToPointer();
// I hate nasty lambda statements. I always find local methods easier to read.
void Workload(int i)
{
var found = false;
var b = *(arr1Ptr + i);
for (var j = 0; j < arr2.Length; j++)
{
if (LibDevice.__nv_popcll(b & *(arr2Ptr + j)) >= limit)
{
found = true;
break;
}
}
if (!found)
{
*(resultPtr + i) = b;
}
}
try
{
gpu.For(0, arr1.Length, i => Workload(i));
}
finally
{
// Make sure we free resources
arr1Handle.Free();
arr2Handle.Free();
resultHandle.Free();
}
return result;
}
其他资源
gchandle.alloc方法(对象(
一种保护物体免受垃圾收集的新gchandle。这 不再需要时,必须免费释放gchandle。
gchandletype枚举
固定:此手柄类型类似于正常人,但允许固定对象的地址。这可以防止垃圾 收集器从移动物体中,因此破坏了效率 垃圾收集器。使用免费方法释放分配的 尽快处理。
不安全的代码和指针(C#编程指南(
在公共语言运行时(CLR(中,不安全的代码称为 无法验证的代码。C#中不安全的代码不一定是危险的;它 只是CLR无法验证安全性的代码。CLR会 因此,只有在完全信任的情况下执行不安全的代码 集会。如果您使用不安全的代码,则有责任确保 您的代码不会引入安全风险或指针错误。
- 注释,此后有一个更新,以下更新:
http://www.aleagpu.com/release/3_0_3/doc/advanced_features_csharp.html
现在是:
http://www.aleagpu.com/release/3_0_4/doc/advanced_features_csharp.html
在版本3.0.4中更改或移动了一些样本和信息。