gpt4 book ai didi

java - Aparapi GPU 执行速度比 CPU 慢

转载 作者:行者123 更新时间:2023-11-30 06:12:28 24 4
gpt4 key购买 nike

我正在尝试测试 Aparapi 的性能。我看过一些blogs结果表明 Aparapi 在进行数据并行操作时确实提高了性能。

但我无法在测试中看到这一点。这是我所做的,我写了两个程序,一个使用 Aparapi,另一个使用普通循环。

程序 1:在 Aparapi 中

import com.amd.aparapi.Kernel;
import com.amd.aparapi.Range;

public class App
{
public static void main( String[] args )
{
final int size = 50000000;

final float[] a = new float[size];
final float[] b = new float[size];

for (int i = 0; i < size; i++) {
a[i] = (float) (Math.random() * 100);
b[i] = (float) (Math.random() * 100);
}

final float[] sum = new float[size];

Kernel kernel = new Kernel(){
@Override public void run() {
int gid = getGlobalId();
sum[gid] = a[gid] + b[gid];
}
};
long t1 = System.currentTimeMillis();
kernel.execute(Range.create(size));
long t2 = System.currentTimeMillis();
System.out.println("Execution mode = "+kernel.getExecutionMode());
kernel.dispose();
System.out.println(t2-t1);
}
}

程序2:使用循环

public class App2 {

public static void main(String[] args) {

final int size = 50000000;

final float[] a = new float[size];
final float[] b = new float[size];

for (int i = 0; i < size; i++) {
a[i] = (float) (Math.random() * 100);
b[i] = (float) (Math.random() * 100);
}

final float[] sum = new float[size];
long t1 = System.currentTimeMillis();
for(int i=0;i<size;i++) {
sum[i]=a[i]+b[i];
}

long t2 = System.currentTimeMillis();
System.out.println(t2-t1);

}
}

程序 1 大约需要 330 毫秒,而程序 2 只需要大约 55 毫秒。我在这里做错了什么吗?我确实在 Aparpai 程序中打印出执行模式,它打印出执行模式是 GPU

最佳答案

您没有做错任何事 - 除了基准测试本身。

基准测试总是很棘手,尤其是在涉及 JIT 的情况下(如 Java),以及许多细节对用户隐藏的库(如 Aparapi)。在这两种情况下,您至少应该多次执行要进行基准测试的代码部分。

对于 Java 版本,由于 JIT 的启动,人们可能期望当循环本身被多次执行时,单次执行循环的计算时间会减少。还有许多额外的注意事项需要考虑 - 了解详情,你应该引用this answer .在这个简单的测试中,JIT 的影响可能并不明显,但在更现实或更复杂的场景中,这会有所不同。总之:当重复循环10次时,在我的机器上执行一次循环的时间大约是70毫秒

对于 Aparapi 版本,可能的 GPU 初始化点已经在评论中提到。在这里,这确实是主要问题:当运行内核 10 次时,我机器上的时间是

1248
72
72
72
73
71
72
73
72
72

您会看到初始调用导致了所有开销。这样做的原因是,在第一次调用 Kernel#execute() 期间,它必须进行所有初始化(基本上是将字节码转换为 OpenCL、编译 OpenCL 代码等)。 KernelRunner类的文档中也提到了这一点:

The KernelRunner is created lazily as a result of calling Kernel.execute().

这样做的影响——即第一次执行的相对较大的延迟——导致了 Aparapi 邮件列表中的这个问题:A way to eagerly create KernelRunners .唯一的解决方法是创建一个像

这样的“初始化调用”
kernel.execute(Range.create(1));

没有真正的工作量,只触发整个设置,以便后续调用快速。 (这也适用于您的示例)。


您可能已经注意到,即使在 初始化之后,Aparapi 版本仍然不比普通Java 版本快。这样做的原因是像这样的简单 vector 加法任务是内存限制 - 有关详细信息,您可以引用this answer ,它解释了这个术语和一般 GPU 编程的一些问题。

作为您可能受益于 GPU 的情况的一个过于暗示性的示例,您可能想要修改您的测试,以创建一个人工计算绑定(bind)任务:当您将内核更改为涉及一些昂贵的三角函数,像这样

Kernel kernel = new Kernel() {
@Override
public void run() {
int gid = getGlobalId();
sum[gid] = (float)(Math.cos(Math.sin(a[gid])) + Math.sin(Math.cos(b[gid])));
}
};

和相应的普通 Java 循环版本,像这样

for (int i = 0; i < size; i++) {
sum[i] = (float)(Math.cos(Math.sin(a[i])) + Math.sin(Math.cos(b[i])));;
}

然后你会看到不同。在我的机器上(GeForce 970 GPU 与 AMD K10 CPU),Aparapi 版本的时间约为 140 毫秒,普通 Java 版本的时间高达 12000 毫秒 - 这是通过 Aparapi 加速近 90!

另请注意,即使在 CPU 模式下,与纯 Java 相比,Aparapi 也可能具有优势。在我的机器上,在 CPU 模式下,Aparapi 只需要 2300 毫秒,因为它仍然使用 Java 线程池并行执行。

关于java - Aparapi GPU 执行速度比 CPU 慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32858768/

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