本文介绍了用于Java开发机器学习和深度学习的Vector API
英语原文链接 https://software.intel.com/en-us/articles/vector-api-developer-program-for-java
Vector API教程
介绍
如今,大数据应用程序,分布式深度学习和人工智能解决方案可以直接在现有的Apache Spark *或Apache Hadoop *集群之上运行,并可以从有效的横向扩展中受益。为了在这些应用程序中获得理想的数据并行性,Open JDK Project Panama提供了Vector API。用于Java *软件的Vector API开发人员计划 提供了广泛的方法,可以丰富Java开发人员的机器学习和深度学习体验。
视频地址:https://www.youtube.com/embed/X49ucwtwuU0?feature=oembed&enablejsapi=1
本文向Java开发人员介绍Vector API,说明了如何在Java程序中开始使用API,并提供了矢量算法的示例。提供了有关如何构建矢量API以及如何使用它来构建Java应用程序的分步详细信息。此外,我们提供了有关如何在Java中为自己的算法实现Vector代码(后文翻译为
矢量
或
向量
)以提高性能的详细教程。
什么是SIMD?
单指令多数据(SIMD)允许在多个数据点上同时执行相同的操作,这得益于应用程序中数据级别的并行性。现代CPU具有高级SIMD操作支持,例如提供SIMD(指令)加速功能的AVX2,AVX3。
大数据应用程序(例如Apache Flink,Apache Spark机器学习库和Intel Big DL,数据分析和深度学习培训工作负载等)运行高度数据并行的算法。Java中的强大的SIMD支持将为扩展其中一些领域提供途径。
什么是Vector API?
用于Java *软件的Vector API开发人员项目使使用Java编写计算密集型应用程序,机器学习和人工智能算法,在没有Java本机接口(JNI)性能开销或对不可移植的本机代码的进一步维护需求的情况下,成为可能。API引入了一组用于对分大小的vector-types进行数据并行操作的方法,以便直接在Java中进行编程,而无需任何有关底层CPU的知识。JVM JIT编译器将这些低级API进一步有效地映射到现代CPU上的SIMD指令,以实现所需的性能加速;否则,将使用默认的VM实现将Java字节码映射为硬件指令。
Vector 接口
Vector API接口如下所示:
Vector Type
Vector Type(Vector <E,S>)采用’E’表示元素类型和’S’表示形状或向量的按位长度。基于最近的进展,Panama 项目支持以下元素和形状的Vectors创建。
1 Element types: Byte, Short, Integer, Long, Float, and Double
2 Shape types (bit-size): 128, 256, and 512
选择矢量形状以将它们紧密映射到CPU平台上可用的最大SIMD寄存器上。
Vector 运算
所有这些Vector类型都可以使用基本的Vector-Vector功能。典型的算术和三角函数的矢量运算均以掩码格式提供。mask用于if-else类型的条件操作。
示例部分展示了如何在程序中使用 Vector mask。
01 public abstract class DoubleVector<S extends Vector.Shape<Vector<?,?>>> implements Vector<Double,S> {
02 Vector<Double, S> add (Vector<Double, S> v2);
03 Vector<Double,S> add (Vector<Double, S> o, Mask<Double, S> m);
04 Vector<Double, S> mul (Vector<Double, S> v2);
05 Vector<Double, S> mul (Vector<Double, S> o, Mask<Double, S> m);
06 ….
07 Vector<Double, S> sin ();
08 Vector<Double, S> sin (Mask<Double, S> m);
09 Vector<Double, S> sqrt (),
10 …
11}
Vector API还提供了金融服务行业(FSI)和机器学习应用程序中经常需要的更高级的Vector操作。
01public abstract class IntVector<S extends Vector.Shape<Vector<?,?>>> implements Vector<Integer,S> {
02int sumAll ();
03 void intoArray(int[] a, int ix);
04 void intoArray (int [] is, int ix, Mask<Integer, S> m);
05 Vector<Integer, S> fromArray (int [] fs, int ix);
06 Vector<Integer, S> blend (Vector<Integer, S> o, Mask<Integer, S> m);
07 Vector<Integer, S> shuffle (Vector<Integer, S> o, Shuffle<Integer, S> s);
08 Vector<Integer, S> fromByte (byte f);
09 …
10 }
机器学习中的性能提升
基本线性代数子程序(BLAS)
使用Vector实现BLAS I,II和III 例程可以将性能提高3-4倍。
BLAS I和II 例程通常在Apache Spark机器学习库中使用。这些适用于班轮模型和决策树的分类和回归,协同过滤和聚类以及降维问题。BLAS-III例程(例如GEMM)广泛用于解决人工智能中使用的深度学习和神经网络问题。
*Open JDK Project Panama source build 0918201709182017。Java Hotspot 64位服务器VM(混合模式)。操作系统版本:Cent OS 7.3 64位
英特尔®至强®铂金8180处理器(使用512字节和1024字节的浮点数据块)。
JVM选项:-XX:+ UnlockDiagnosticVMOptions -XX:-CheckIntrinsics -XX:TypeProfileLevel = 121 -XX:+ UseVectorApiIntrinsics
图像处理过滤
使用Vector API,棕褐色过滤的速度最高可提高6倍。
编写Vector代码
在Java *中使用Vector API
Vector接口是com.oracle.vector软件包的一部分,我们从Vector API开始,在程序中导入以下内容。根据向量类型,用户可以选择导入FloatVector,IntVector等。
1import jdk.incubator.vector.FloatVector;
2 import jdk.incubator.vector.Vector;
3 import jdk.incubator.vector.Shapes;
矢量类型(Vector <E,S>)具有两个参数。
‘E’:元素类型,广泛支持int,float和double基本类型。
“ S”指定矢量的形状或按位大小。
在使用向量运算之前,程序员必须创建一个第一个向量实例来捕获元素类型和向量形状。使用该特定大小和形状的矢量可以被创建。
1 private static final FloatVector.FloatSpecies<Shapes.S256Bit> species = (FloatVector.FloatSpecies<Shapes.S256Bit>) Vector.speciesInstance (Float.class, Shapes.S_256_BIT);
2 IntVector.IntSpecies<Shapes.S512Bit> ispec = (IntVector.IntSpecies<Shapes.S512Bit>) Vector.speciesInstance(Integer.class, Shapes.S_512_BIT);
从此以后,用户可以创建FloatVector <Shapes.S256Bit>和IntVector <Shapes.S512Bit>类型的矢量实例。
简单的矢量循环
在本节中,我们提供了矢量API编程的风格。Vector API白皮书<
使用Java 编写自矢量算法以提高性能
>中提供了有关如何编写矢量算法的详细技巧和窍门。BLAS和FSI例程的示例矢量代码示例可在后续章节中找到。
第一个示例展示两个数组的向量加法。程序使用诸如fromArray(),intoArray()之类的向量操作将向量加载/存储到数组中。
向量add()运算用于算术运算。
1 public static void AddArrays (float [] left, float [] right, float [] res, int i) {
2 FloatVector.FloatSpecies<Shapes.S256Bit> species = (FloatVector.FloatSpecies<Shapes.S256Bit>)
3 Vector.speciesInstance (Float.class, Shapes.S_256_BIT);
4 FloatVector<Shapes.S256Bit> l = species.fromArray (left, i);
5 FloatVector<Shapes.S256Bit> r = species.fromArray (right, i);
6 FloatVector<Shapes.S256Bit> lr = l.add(r);
7 lr.intoArray (res, i);
8}
通过使用species.length()查询向量大小来编写向量循环。考虑下面的标量循环,它将数组A和B相加并将结果存储到数组C中。
1 for (int i = 0; i < C.length; i++) {
2 C[i] = A[i] + B[i];
3 }
向量化循环如下所示:
01 public static void add (int [] C, int [] A, int [] B) {
02 IntVector.IntSpecies<Shapes.S256Bit> species =
03 (IntVector.IntSpecies<Shapes.S256Bit>) Vector.speciesInstance(Integer.class, Shapes.S_256_BIT);
04 int i;
05 for (i = 0; (i + species.length()) < C.length; i += species.length ()) {
06 IntVector<Shapes.S256Bit> av = species.fromArray (A, i);
07 IntVector<Shapes.S256Bit> bv = species.fromArray (B, i);
08 av.add(bv).intoArray(C, i);
09 }
10 for (; i < C.length; i++) { // Cleanup loop
11 C[i] = A[i] + B[i];
12 }
13 }
也可以以长度不可知的方式编写该程序,而与向量大小无关。随后的程序通过Shape设置矢量代码的参数。
01public class AddClass<S extends Vector.Shape<Vector<?, ?>>> {
02 private final FloatVector.FloatSpecies<S> spec;
03 AddClass (FloatVector.FloatSpecies<S> v) {spec = v; }
04 //vector routine for add
05 void add (float [] A, float [] B, float [] C) {
06 int i=0;
07 for (; i+spec.length ()<C.length;i+=spec.length ()) {
08 FloatVector<S> av = spec.fromArray (A, i);
09 FloatVector<S> bv = spec.fromArray (B, i);
10 av.add (bv).intoArray(C, i);
11 }
12 //clean up loop
13 for (;i<a.length;i++) C[i]=A[i]+B[i];
条件语句中的运算可以使用掩码以矢量形式编写。
标量例程如下,
1for (int i = 0; i < SIZE; i++) {
2 float res = b[i];
3 if (a[i] > 1.0) {
4 res = res * a[i];
5 }
6 c[i] = res;
7 }
使用mask的Vector例程如下。
01public void useMask (float [] a, float [] b, float [] c, int SIZE) {
02 FloatVector.FloatSpecies<Shapes.S256Bit> species = (FloatVector.FloatSpecies <Shapes.S256Bit>) Vector.speciesInstance Float.class, Shapes.S_256_BIT);
03 FloatVector<Shapes.S256Bit> tv=species.broadcast (1.0f); int i = 0;
04 for (; i+ species.length() < SIZE; i+ = species.length()){
05 FloatVector<Shapes.S256Bit> rv = species.fromArray (b, i);
06 FloatVector<Shapes.S256Bit> av = species.fromArray (a, i);
07 Vector.Mask<Float,Shapes.S256Bit> mask = av.greaterThan (tv);
08 rv.mul (av, mask).intoArray(c, i);
09 }
10 //后续处理
11}
教程:编写own-vector算法
<Vector API:在Oracle Java中编写自矢量算法以提高性能>
白皮书提供了一些使用Vector API编写Java代码的技巧和窍门,并且还介绍了一些提高性能的方法。
这些示例应为您提供一些在Oracle Java *中进行矢量编程的准则和最佳实践,以帮助您成功地编写自己的计算密集型算法的矢量版本。
更多有关信息,请参见
PDF附件
。
教程:所有关于Vector API的知识
网址1 https://www.youtube.com/embed/jRyD1EIOOis?feature=oembed&enablejsapi=1
网址2 https://www.youtube.com/embed/videoseries?list=PLX8CzqL3ArzXJ2EGftrmz4SzS6NRr6p2n&enablejsapi=1
入门
构筑Vector API
本节假定用户熟悉基本的Linux实用程序。
将JDK8二进制文件设置为JAVA_HOME
Panama项目需要在系统上使用JDK8。可以从此位置
下载JDK
。
#export JAVA_HOME = / pathto / jdk1.8-u91
#export PATH = $ JAVA_HOME / bin:$ PATH
下载并编译Panama源码
可以使用商业资源控制管理工具下载Project Panama的源码。
# hg clone http://hg.openjdk.java.net/panama/panama/
# source get_source.sh
# ./configure
# make all
使用Panama JDK构建自己的应用程序
我们需要将vector.jar文件从包含Panama源的父文件目录中复制到Java应用程序的位置。
01import jdk.incubator.vector.IntVector;
02import jdk.incubator.vector.Shapes;
03import jdk.incubator.vector.Vector;
04
05public class HelloVectorApi {
06 public static void main(String[] args) {
07 IntVector.IntSpecies<Shapes.S128Bit> species =
08 (IntVector.IntSpecies<Shapes.S128Bit>) Vector.speciesInstance(
09 Integer.class, Shapes.S_128_BIT);
10 int val = 1;
11 IntVector<Shapes.S128Bit> hello = species.broadcast(val);
12 if (hello.sumAll() == val * species.length()) {
13 System.out.println("Hello Vector API!");
14 }
15 }
16}
运行你的应用程序
/pathto/panama/build/linux-x86_64-normal-server-release/images/jdk/bin/java –add-modules=jdk.incubator.vector -XX:TypeProfileLevel=121 HelloVectorApi
IDE配置
配置IntelliJ以进行OpenJDK Panama开发
1)创建一个新项目。如果是刚安装IntelliJ或没有打开过项目,则在出现的窗口中点击“Create New Project”(您可以在下面的窗口中看到)。
否则,File > New > Project… 也有相同的效果。
2)在出现的“New Project”窗口中,确保选择左侧的Java。选择Panama编译作为Project SDK。
如果尚未将Panama build设置为Project SDK,请按右侧的“New…”按钮。否则,请转到步骤4。
3)弹出的窗口叫做“Select Home Directory for JDK”。您要选择的路径是/ path / to / panama / build / linux-x86_64-normal-server-release / images / jdk。点击确定。