Java 19が2022/9/20にリリースされました。
https://mail.openjdk.org/pipermail/jdk-dev/2022-September/006933.html
今回、正式導入された機能がほとんどありませんが、Record PatternsやVirtual Threadは注目すべき機能です。
詳細はこちら
JDK 19 Release Notes
Java SE 19 Platform JSR 394
OpenJDK JDK 19 GA Release
APIドキュメントはこちら
Overview (Java SE 19 & JDK 19)
追加されたAPIまとめはこちら
https://docs.oracle.com/en/java/javase/19/docs/api/new-list.html
APIの差分はこちら。
https://cr.openjdk.java.net/~iris/se/19/latestSpec/apidiffs/overview-summary.html
MacやLinuxでのインストールにはSDKMAN!をお勧めします
Oracle OpenJDK以外に無償で商用利用できるディストリビューションとしては、次のようなものがあります。
(Java 20が出たためダウンロードリンクがなくなっているものもあります)
- Oracle JDK ※対外サーバーの運用には有償ライセンスが必要です
- Adoptium Temurin
- Azul Zulu
- Liberica JDK
- Amazon Corretto 19
- Microsoft Build of OpenJDK
- SapMachine
アップデートは10月に19.0.1が、1月に19.0.2がリリースされることになります。
JEP
大きめの変更はJEPでまとまっています。
https://openjdk.java.net/projects/jdk/19/
今回は7個のJEPが取り込まれました。すでにプレビューなどで出ていたものが3つあり、新たに取り込まれたものは4です。ただし、正式に取り込まれたのは1つだけです。
また、今回は削除された機能、非推奨になった大きな機能というのはありません。
JEP 405: Record Patterns (Preview)
JEP 422: Linux/RISC-V Port
JEP 424: Foreign Function & Memory API (Preview)
JEP 425: Virtual Threads (Preview)
JEP 426: Vector API (Fourth Incubator)
JEP 427: Pattern Matching for switch (Third Preview)
JEP 428: Structured Concurrency (Incubator)
ツール
今回ツール関係の変更はありませんでした。
言語機能
言語機能の変更としては、Record Patternsがプレビューとして入りました。また、Pattern Matching for switchが3rdプレビューになっています。
JEP 405: Record Patterns (Preview)
JEP 427: Pattern Matching for switch (Third Preview)
JEP 405: Record Patterns (Preview)
レコードの分解ができるパターンマッチです。
たとえばPointというレコードを用意します。
jshell> record Point(int x, int y) {}
| 次を作成しました: レコード Point
jshell> var p = new Point(3, 5)
p ==> Point[x=3, y=5]
そうすると次のようなパターンが書けます。
jshell> p instanceof Point(var xx, var yy)
$3 ==> true
条件式と組み合わせて、パターンマッチが成り立つ時にそのまま変数の値を扱うことができます。
jshell> p instanceof Point(var xx, var yy) ?
...> "x:%d y:%d".formatted(xx, yy) :
...> "no match"
$4 ==> "x:3 y:5"
固定値の指定は出来なさそう。
jshell> p instanceof Point(3, var yy)
| エラー:
| 型の開始が不正です
| p instanceof Point(3, var yy)
| ^
Pointを持つBoxを定義します。
jshell> record Box(Point topLeft, Point bottomRight){}
| 次を作成しました: レコード Box
jshell> var b = new Box(new Point(3, 4), new Point(7, 9))
b ==> Box[topLeft=Point[x=3, y=4], bottomRight=Point[x=7, y=9]]
そうすると、Box内のPointの値を直接取り出すことができます。
jshell> b instanceof Box(Point(var left, var top), Point(var right, var bottom)) ?
...> "(%d, %d)-(%d, %d)".formatted(left, top, right, bottom) : "no match"
$8 ==> "(3, 4)-(7, 9)"
次のように命令的にレコードの中身を得ていくよりも、やりたいことがはっきりします。
b instanceof Box b ?
"(%d, %d)-(%d, %d)".formatted(b.topLeft().x(), b.topLeft().y(),
b.bottomRight().x(), b.bottomRight().y()) :
"no match"
JEP 427: Pattern Matching for switch (Third Preview)
パターンマッチがswitch
で使えるようになります。
3rdプレビューになりました。今回大きな変更がなければ、きっとJava 20で正式機能になりそうです。
Java 19では、使うときに--enable-preview
が必要です。
「といってもパターンマッチってそんなに出番ないのでは?」
と思うかもしれませんが、大きな影響が。
switch
でcase null
が書けるようになります。
jshell> String s = null
s ==> null
jshell> switch (s) {
...> case "test" -> "テスト";
...> case null -> "ぬるぽ";
...> default -> "hello";
...> }
$13 ==> "ぬるぽ"
case null
がない場合は従来どおりNullPointerExceptionです。
jshell> switch (s) {
...> case "test" -> "テスト";
...> default -> "hello";
...> }
| 例外java.lang.NullPointerException: Cannot invoke "String.hashCode()" because "<local0>" is null
| at (#12:1)
""
とnull
で同じ処理を行う場合などに併記できるのが便利です。
jshell> switch(s){
...> case null, "" -> "empty";
...> default -> s;
...> }
$17 ==> "empty"
default
とnull
で同じ処理したいときにアロースタイルで書けないんでは?という疑問に応えて、default
がcase
に書けるようになっています。
jshell> switch (s) {
...> case "one" -> 1;
...> case "two" -> 2;
...> case null, default -> -1;
...> }
$21 ==> -1
で、パターンマッチですが、型と変数をswtichに書けるようになりました。これをタイプパターンといいます。
jshell> Object o = "abc"
o ==> "abc"
jshell> switch (o) {
...> case String s -> "--%s--".formatted(s);
...> case Integer i -> "%03d".formatted(i);
...> default -> o.toString();
...> }
$35 ==> "--abc--"
タイプパターンのあとにwhen
でつなげて条件を書くことでガード節とすることができます。
jshell> o = "helo"
o ==> "helo"
jshell> switch (o) {
...> case String s when s.length() < 5 -> "--%s--".formatted(s);
...> case String s -> s.toUpperCase();
...> case Integer i -> "%05d".formatted(i);
...> default -> o.toString();
...> }
$42 ==> "--helo--"
2ndプレビューではwhen
ではなく&&
を使っていました。
switch (o) {
case String s && s.length() < 5 -> "--%s--".formatted(s);
case String s -> s.toUpperCase();
case Integer i -> "%05d".formatted(i);
default -> o.toString();
}
定数パターンとタイプパターンをひとつのswitchで使うこともできます。定数パターンに使えるのはこれまでどおり整数、文字列、enumです。
jshell> s = "helo"
s ==> "helo"
jshell> switch (s) {
...> case "test" -> "テスト";
...> case String s when s.length() < 5 -> s.toUpperCase();
...> default -> "too long";
...> }
$29 ==> "HELO"
case句のタイプパターンで定義した変数のスコープは、case句の中だけです。
ここではswitch
の外にあるのと同じく変数s
を定義してcase句で上書きしていますが、switchの外に影響はありません。
jshell> s = "helo"
s ==> "helo"
jshell> switch (s) {
...> case String s when s.length() < 5 -> (s="Hello").toUpperCase();
...> default -> "too long";
...> }
$30 ==> "HELLO"
jshell> s
s ==> "helo"
Record Patternsと併用が本命ですね。
return switch(n) {
case IntExpr(int i) -> i;
case NegExpr(Expr n) -> -eval(n);
case AddExpr(Expr left, Expr right) -> eval(left) + eval(right);
case MulExpr(Expr left, Expr right) -> eval(left) * eval(right);
default -> throw new IllegalArgumentException(n);
};
ここでSealed Classと組み合わせて次のような定義になっているとします。
sealed interface Expression
permits IntExpr, NegExpr, AddExpr, MulExpr {}
record IntExpr(int i) implements Expression {}
record NegExpr(Expr n) implements Expression {}
record AddExpr(Expr left, Expr right) implements Expression {}
record MulExpr(Expr left, Expr right) implements Expression {}
そうすると、Expressionが取りうるのが4つのレコードで全てということがわかるのでdefault句が不要になります。また、全てをチェックしていることを保証できるのが、既存のコードと違うところで、パターンマッチのメリットです。
Expression n = getExp();
return switch(n) {
case IntExpr(int i) -> i;
case NegExpr(Expr n) -> -eval(n);
case AddExpr(Expr left, Expr right) -> eval(left) + eval(right);
case MulExpr(Expr left, Expr right) -> eval(left) * eval(right);
};
API
JEP 424: Foreign Function & Memory API (Preview)
JEP 425: Virtual Threads (Preview)
JEP 426: Vector API (Fourth Incubator)
JEP 428: Structured Concurrency (Incubator)
小さいもの
BigDecimal.TWO
これが正式機能で唯一多くの人が喜ぶ機能追加かもしれない・・・
jshell> BigDecimal.TWO
$1 ==> 2
Thread.sleep(Duration)
sleepメソッドにDurationが指定できるようになったので、よりわかりやすく書けるようになりました。
jshell> Thread.sleep(Duration.ofSeconds(3))
sleep(3000)
じゃ どんだけ待つか、仕様を知らないとわからないし、仕様を知っていても直観的じゃないですよね。
Localeのコンストラクタがdeprecatedに
代わりにof
メソッドを使います。
jshell> Locale.of("ja")
$12 ==> ja
jshell> Locale.of("ja","jp")
$13 ==> ja_JP
HashMap / HashSet / LinkedHashMap / LinkedHashSet newXxx
容量を指定してオブジェクトを生成する newHashMapなどのstaticメソッドがそれぞれ追加されています。
jshell> Map<String, String> m = HashMap.newHashMap(10)
m ==> {}
new HashMap(10)
との違いは、<>
がなくても「無検査変換」って出ないことと、指定した数ぴったりの容量ではなく、指定した数の要素を格納するのに十分な容量になる、ということかな。
jshell> Map<String, String> m = new HashMap(10)
| 警告:
| 無検査変換
| 期待値: java.util.Map<java.lang.String,java.lang.String>
| 検出値: java.util.HashMap
| Map<String, String> m = new HashMap(10);
| ^-------------^
m ==> {}
Double.PRECISION / Float.PRECISION
有効数字の精度bit数です。
jshell> Double.PRECISION
$2 ==> 53
jshell> Float.PRECISION
$3 ==> 24
Math.TAU
半径に対する円周比です。つまり2π。
jshell> Math.TAU
$4 ==> 6.283185307179586
jshell> Math.TAU / 2
$5 ==> 3.141592653589793
jshell> Math.PI
$6 ==> 3.141592653589793
Integer.compress, expand / Long.compress, expand
commpressが第一引数に指定した数値について第二引数で指定したbitだけ取り出して右に詰めます。
jshell> Integer.toHexString(Integer.compress(0xcafebabe, 0xf000f0f0))
$15 ==> "cbb"
expandは逆で、第一引数に指定した数値の下位bitを第二引数で指定したbitに広げます。
jshell> Integer.toHexString(Integer.expand(0xcbb, 0xf000f0f0))
$16 ==> "c000b0b0"
JEP 425: Virtual Threads (Preview)
仮想スレッドです。いままでJavaは、初期にプラットフォームスレッドを使うようになって以来、ずっとThreadとしてプラットフォームスレッドを使ってきました。
プラットフォームスレッドというのは、OSが管理するようなスレッドです。OSが管理するスレッドというのは、YouTubeで動画を見てTwitterして時計を表示してファイナルファンタジーやって、と何でも同時に動かせるように設計されています。
もちろんアプリケーションの中でいろいろな働きのスレッドを動かすのですが、多くのスレッドを立ち上げることになるのは、結局同じことをするスレッドです。
その中で、計算のために同じことをするというのはハードウェアの構成を考えないといけなくて、そのための構成がGPUだったりVector APIで扱うSIMD命令だったりするわけですね。
そして、アプリケーションの処理としては結局のところ計算の時間よりも通信待ちの時間のほうがはるかに長いということで、通信待ち時間を有効利用したいというのが並列処理の主な目的になっていました。
そうすると、計算の最中にバランスとりながら処理を切り替えるプラットフォームスレッドというのはオーバークオリティだったわけで、やりたいことに対してメモリも食うし限界も低いし遅いということになってたわけです。
ということで、現状でJavaではReactiveやRxといって通信のときに他の処理に制御を渡せるようなコードを書いているのですが、これが非常に難しくてやってられないということで、Javaから離れてKotlinのように自然にそういう処理が書ける言語に移行する人がでています。
という問題に対処するために、OSではなくJVMが管理するスレッドがVirtual Threadとして導入されます。Virtual ThreadはProject Loomで開発されていました。
Virtual Threadは既存のプラットフォームスレッドと同様に扱うことができるよう気をつけてAPIが設計されています。
ただ、プラットフォームスレッドではnew Thread()
としてスレッドを生成できましたが、コンストラクタでVirtual Threadを生成することはできません。
いままでのプラットフォームスレッドはこんな感じで使いました。
jshell> Thread t = new Thread(() -> System.out.println("hello"))
t ==> Thread[#220496,Thread-110304,5,main]
jshell> t.getState()
$33 ==> NEW
jshell> t.start()
jshell> hello
jshell> t.getState()
$35 ==> TERMINATED
Java 19ではThread.Builderが用意されて、ofPlatform
かofVirtual
でプラットフォームスレッドと仮想スレッドを分けれるようになります。
unstarted
メソッドを使うと、new Thread
と同様にまだ開始していないスレッドを得れます。
jshell> Thread t = Thread.ofPlatform().unstarted(() -> System.out.println("hello"))
t ==> Thread[#220491,Thread-110303,5,main]
jshell> t.start()
hello
jshell> t.getState()
$24 ==> TERMINATED
そしてofVirtual
に変更すると仮想スレッドを得れます。仮想スレッドはThreadクラスのオブジェクトとして扱えます。使い方はプラットフォームスレッドと同様です。
jshell> Thread t = Thread.ofVirtual().unstarted(() -> System.out.println("hello"))
t ==> VirtualThread[#220492]/new
jshell> t.start()
jshell> hello
jshell> t.getState()
$27 ==> TERMINATED
start
メソッドを使うと、スレッドの実行とThread
オブジェクトの取得が同時に行えます。
jshell> Thread t = Thread.ofVirtual().start(() -> System.out.println("hello"))
t ==> VirtualThread[#220497]/runnable
hello
jshell> t.getState()
$37 ==> TERMINATED
ExecutorService
で仮想スレッドを使う場合は、Executors.newVirtualThreadPerTaskExecutor
を使います。
jshell> ExecutorService ex = Executors.newVirtualThreadPerTaskExecutor()
ex ==> java.util.concurrent.ThreadPerTaskExecutor@28ba21f3
jshell> ex.submit(() -> System.out.println("hello"))
$40 ==> java.util.concurrent.ThreadPerTaskExecutor$ThreadBoundFuture@64a294a6[Not completed]
hello
jshell> ex.isTerminated()
$41 ==> false
jshell> ex.isShutdown()
$42 ==> false
jshell> ex.shutdown()
jshell> ex.isTerminated()
$44 ==> true
jshell> ex.isShutdown()
$45 ==> true
では、少し性能を見てみましょう。
3秒待つメソッドを定義しておきます。
jshell> import java.time.Duration
jshell> void s(){ try{ Thread.sleep(Duration.ofSeconds(3));} catch(Exception ex){}}
| 次を作成しました: メソッド s()
プラットフォームスレッドを100起動してそれぞれで3秒待ちます。
jshell> IntStream.range(0, 100).forEach(i -> Thread.ofPlatform().start(() -> s()))
スレッドが100増えてすぐ元にもどっていることがわかります。
仮想スレッドを100起動してそれぞれで3秒待ちます。
jshell> IntStream.range(0, 100).forEach(i -> Thread.ofVirtual().start(() -> s()))
そうすると、17スレッドだけ増えて31スレッドになります。
100増えないことと、終わってもスレッドが残っていることがわかります。
それでは10万スレッドを同時に実行してみましょう。
まずは仮想スレッドから。
jshell> IntStream.range(0, 100_000).forEach(i -> Thread.ofVirtual().start(() -> s()))
10:09のところで実行しています。スレッド数は31のまま、CPU使用率は7%くらいです。メモリは120MBくらい使っていますね。
プラットフォームスレッドで試してみます。
jshell> IntStream.range(0, 100_000).forEach(i -> Thread.ofPlatform().start(() -> s()))
まず、しばらく処理が終わりません。
スレッドは2万弱くらいまで使われていますが、その2万弱のスレッドでスレッドが終わったら別のスレッドというふうにやりくりして10万スレッドを処理できるまで、繰り返されます。
また、CPUは80%近くまで使われています。処理はSleepするだけなので、スレッド切り替えにCPUを使っていると推測されます。メモリはそれほど使っていませんね。
このように、多数のスレッドを処理するときにプラットフォームスレッドでは同時処理数に限界があることと、CPUパワーを余分に使ってしまうので、軽量な仮想スレッドが求められ実装されたわけです。
JEP 428: Structured Concurrency (Incubator)
並列処理では、複数の処理を実行するときに、両方が終われば正常終了とか、どちらか片方が終われば終了だとか、どちらか一方でも例外が発生したら終了だとか、同時に行う処理で連動することがあります。
しかし、これを既存のjoin
やwait
などで制御しようとすると、実際にはjoin
からwait
へのGo Toを書くようなコードになって、処理が追えなくなります。
そこで導入されるのが構造化並列性といいます。
こんな感じ。詳しくはあとで書きます!
Response handle() throws ExecutionException, InterruptedException {
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
Future<String> user = scope.fork(() -> findUser());
Future<Integer> order = scope.fork(() -> fetchOrder());
scope.join(); // Join both forks
scope.throwIfFailed(); // ... and propagate errors
// Here, both forks have succeeded, so compose their results
return new Response(user.resultNow(), order.resultNow());
}
}
JEP 426: Vector API (Fourth Incubator)
AVX命令のような、複数のデータに対する計算を同時に行う命令をJavaから利用できるようになります。
Project Panamaのひとつとして開発されていました。
Java 16でインキュベータとして導入されましたが、今回4thインキュベータになりました。
使うためには実行時やコンパイル時に--add-modules jdk.incubator.vector
をつける必要があります。
import jdk.incubator.vector.*;
static final VectorSpecies<Float> SPECIES = FloatVector.SPECIES_256;
void vectorComputation(float[] a, float[] b, float[] c) {
for (int i = 0; i < a.length; i += SPECIES.length()) { // SPECIES.length() = 256bit / 32bit -> 8
VectorMask<Float> m = SPECIES.indexInRange(i, a.length); // 端数がマスクされる
// a.lengthが11でiが8のとき最初の3つしか要素がないので [TTT.....]
// FloatVector va, vb, vc;
FloatVector va = FloatVector.fromArray(SPECIES, a, i, m);
FloatVector vb = FloatVector.fromArray(SPECIES, b, i, m);
FloatVector vc = va.mul(va).
add(vb.mul(vb)).
neg();
vc.intoArray(c, i, m);
}
}
利用できるのは次の6つの型です。それぞれに対応するVector型があって、これが基本になります。
型 | bit幅 | Vector |
---|---|---|
byte | 8 | ByteVector |
short | 16 | ShortVector |
int | 32 | IntVector |
long | 64 | LongVector |
float | 32 | FloatVector |
double | 64 | DoubleVector |
ただ、利用するにはVectorSpeciesが必要です。利用したいVectorにSPECIES_*という定数が用意されているので、それを使います。*は一度に計算するbit数ですね。
jshell> FloatVector.SP
SPECIES_128 SPECIES_256 SPECIES_512 SPECIES_64 SPECIES_MAX SPECIES_PREFERRED
MAXではそのハードウェアで使える最大、PREFERREDは推奨ビット数だけど、同じになるんじゃないのかな。ここでは256bitが推奨されて、floatが8個同時に計算できるようになっていますね。
jshell> FloatVector.SPECIES_PREFERRED
$11 ==> Species[float, 8, S_256_BIT]
ハードウェアで使えるbit数は搭載CPUに依存しますが、普通のIntel/AMDであれば256、XEONとか つよつよCPUなら512かな。M1は128でした。ハードウェアでサポートされないbit数を使おうとするとソフトウェア処理になるので遅くなります。
実際のVectorはfrom*というメソッドで取得します。fromArray、fromByteArray、fromByteBufferが用意されています。インキュベータに入る前はfromValuesがあったのですが、なくなってますね。
Vectorを得られたら、用意されたメソッドで計算します。ひととおりの算術命令はあります。
jshell> va.
abs() add( addIndex( bitSize() blend(
broadcast( byteSize() compare( div( elementSize()
elementType() eq( equals( fma( getClass()
hashCode() intoArray( intoByteArray( intoByteBuffer( lane(
lanewise( length() lt( max( min(
mul( neg() notify() notifyAll() pow(
rearrange( reduceLanes( reduceLanesToLong( reinterpretAsBytes() reinterpretShape(
selectFrom( shape() slice( sqrt() sub(
test( toArray() toDoubleArray() toIntArray() toLongArray()
toShuffle() toString() unslice( viewAsFloatingLanes() viewAsIntegralLanes()
wait( withLane(
ところで、こういったメソッド呼び出しの内部でAVX命令などを呼び出すのでは遅くなるんではという気がしますが、実際にはJVM intrinsicsという仕組みでJITコンパイラがこれらのメソッド呼び出しをネイティブ関数呼び出しに置き換えます。
JEP 424: Foreign Function & Memory API (Preview)
3rdインキュベータではなくPreviewになりましたが、Foreign Function APIの前身であるForeign Linker APIはJava 16から、Foreign Memory APIはJava 14からインキュベータで、結構ながくインキュベータやってます。なので もう番号ふるのをやめたのかな
Foreign Memory API
ヒープ外のメモリをアクセスする方法としては、ByteBufferを使う方法やUnsafeを使う方法、JNIを使う方法がありますが、それぞれ一長一短があります。
ByteBufferでdirect bufferを使う場合、intで扱える範囲の2GBまでに制限されたり、メモリの解放がGCに依存したりします。
Unsafeの場合は、性能もいいのですが、名前が示すとおり安全ではなく、解放済みのメモリにアクセスすればJVMがクラッシュします。
JNIを使うとCコードを書く必要があり、性能もよくないです。
ということで、ヒープ外のメモリを直接扱うAPIがJava 14でインキュベータモジュールとして導入され、今回6バージョン目のインキュベータです。
次のようなコードになります。
import java.lang.foreign.*;
import java.nio.ByteOrder;
VarHandle intHandle = MemoryHandles.varHandle(int.class, ByteOrder.nativeOrder());
try (MemorySegment segment = MemorySegment.allocateNative(100)) {
for (int i = 0 ; i < 25 ; i++) {
intHandle.set(seg, i * 4, i);
}
}
コンパイル、実行では--enable-preview
を付ける必要があります。
MemoryHandlesからwithOffsetとwithStrideが消えましたね。
jshell> import java.lang.foreign.*
jshell> MemoryHandles.
asAddressVarHandle( asUnsigned( class collectCoordinates( dropCoordinates(
filterCoordinates( filterValue( insertCoordinates( permuteCoordinates( varHandle(
Java 15ではMemoryHandlesからVarHandleを得るメソッドが拡充してました。
jshell> MemoryHandles.
asAddressVarHandle( asUnsigned( class collectCoordinates( dropCoordinates(
filterCoordinates( filterValue( insertCoordinates( permuteCoordinates( varHandle(
withOffset( withStride(
Java 14では次の3つのメソッドしかありませんでした。
jshell> MemoryHandles.
class varHandle( withOffset( withStride(
Java 16ではMemoryAccessというユーティリティが用意されて、VarHandleを使わずにシンプルにかけるようになっています。
import java.lang.foreign.*;
try (MemorySegment segment = MemorySegment.allocateNative(100)) {
for (int i = 0 ; i < 25 ; i++) {
MemoryAccess.setIntAtOffset(i * 4, i);
}
}
Foreign Function API
ネイティブライブラリの呼び出しを行う。
外部メモリのアクセスにはForeign Memory Access APIを使う
JNIの代替
Java 16でForeign Linkerとして1stインキュベータになり、今回4バージョン目のインキュベータです。
たとえばこんな感じのC関数があって。
size_t strlen(const char *s);
こんな感じでMethodHandle
を取り出して。
CLinker linker = CLinker.systemCLinker();
MethodHandle strlen = linker.downcallHandle(
linker.lookup("strlen").get(),
FunctionDescriptor.of(JAVA_LONG, ADDRESS)
);
こんな感じで呼び出すようです。
MemorySegment cString = implicitAllocator().allocateUtf8String("Hello");
long len = strlen.invoke(cString); // 5
JDK
JDKの変更、つまりリリース方針などの変更としては、今回RISC-Vへの対応が入りました。
JEP 422: Linux/RISC-V Port
対応したのはSIMD命令付64bitのRV64GVコンフィグレーションです。
まだVector APIにはちゃんと対応してないぽい。
[vectorIntrinsics] Vector API for RISC-V