java - Aparapi GPU 执行速度比 CPU 慢

标签 java parallel-processing aparapi

我正在尝试测试 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/

相关文章:

java - 无法访问 Servlet 中的 AJAX 请求参数

java - 矩阵乘法顺序与并行性能测试

c - C语言中使用MPI添加数组

java - 使用浮点或整数数组计算 Pi

java - 将数组作为参数传递时泛型方法的问题

java - 如何在java程序中从access数据库中检索整数列的最大值?

java - 对数组元素的奇数索引求和

parallel-processing - Ansible是同时管理所有主机还是仅管理五个主机? (-f和:serial)

java - 错误: "UnsatisfedLinkError: com.aparapi.internal.jni.OPENCLJNI.getPlatforms()" JNI configuration

使用 CPU 和 GPU 添加数组的 Java 基准测试并比较性能