本文介绍了用于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。点击确定。
4)单击下一步。此时,您可以选择从模板创建项目。继续并选择“Command Line App”,然后再次单击下一步。
5)为您的项目命名和位置,然后单击“完成”。
6)创建项目后,需要执行几个步骤才能成功使用Vector API。转到File > Project Structure…
7)确保在左窗格中选择了“项目”。将“Project language level:”更改为“9 - Modules, private methods in interfaces etc.”。最后,点击OK。
8)在显示目录结构的左窗格中,右键单击“ src”文件夹。导航到New > module-info.java
9)在此文件中,添加以下行“ requires jdk.incubator.vector;”。保存文件。
10)返回Main.java。添加使用API的所需代码。有关示例,请参见HelloVectorApi.java。
11)在运行应用程序之前,需要编辑运行配置。按下带有“play”按钮旁边的带有类名的按钮。您应该看到“Edit Configurations…”。点击。
12)在VM选项中,需要添加“ -XX:TypeProfileLevel = 121 -XX:+ UseVectorApiIntrinsics”。这两项在以后都可能成为可选的。如果您想开启/关闭将VectorApi转换为优化的x86内在函数的优化(出于稳定性考虑),您将需要添加:
“-XX:-UseVectorApiIntrinsics”。
13)按下“play”按钮以编译并运行该应用程序。在终端窗口的屏幕底部,您应该看到“ Hello Vector API!”或任何应用程序打印出的输出。
Vector 范例
BLAS机器学习
版权所有2017英特尔公司
如果满足以下条件,则允许以源代码和二进制形式进行重新分发和使用,无论是否经过修改,都可以:
1.重新分发源代码必须保留以上版权声明,此条件列表和以下免责声明。
2.以二进制形式重新分发必须在分发随附的文档和/或其他材料中复制以上版权声明,此条件列表以及以下免责声明。
3.未经事先特别书面许可,不得使用版权所有者的姓名或其贡献者的名字来认可或促销从本软件衍生的产品。
版权持有者和贡献者按“原样”提供此软件,不提供任何明示或暗示的担保,包括但不限于针对特定目的的适销性和适用性的暗示担保。在任何情况下,版权持有人或贡献者均不对任何直接,间接,偶发,特殊,专有或后果性的损害(包括但不限于,替代商品或服务的购买,使用,数据,或业务中断),无论基于合同,严格责任或侵权行为(包括疏忽或其他方式),无论是出于任何责任,无论是否出于使用本软件的目的,即使已经事先告知,也已作了规定。