Loop yang menjumlahkan jutaan angka umumnya memproses satu elemen per langkah — padahal CPU modern memproses operasi pada 8, 16, bahkan 32 angka sekaligus lewat instruksi SIMD (AVX, SVE, NEON). Selama bertahun-tahun, Java hanya bisa menikmati kecepatan itu secara tidak langsung lewat optimasi otomatis compiler atau keluar ke pustaka native. Vector API, yang dirancang sejak Java 16 di bawah JEP 338 dan kini memasuki incubator kesepuluhnya lewat JEP 508 di Java 25, membawa gaya lanewise programming ke Java: satu operasi pada seluruh elemen sebuah vektor sekaligus. Chapter ini memperkenalkan konsepnya dengan simulasi murni Java yang bisa dijalankan langsung, lalu menampilkan wujud asli API — yang masih incubator dan perlu flag khusus.
Konsep Lane dan Operasi Lanewise #
Unit dasarnya adalah Vector<E>: wadah berisi lane sebanyak lebar register SIMD. Sebuah IntVector ber-16 lane menampung 16 nilai int; operasi a.add(b) menjumlahkan lane ke-n dari a dan b secara serentak — satu pemanggilan method, enam belas penjumlahan. Lebar lane optimal ditentukan CPU lewat VectorSpecies, misalnya IntVector.SPECIES_PREFERRED; kode yang sama berjalan pada register 128-bit hingga 512-bit tanpa penyesuaian manual, karena API memilih bentuk terbaik saat runtime.
Simulasi Konsep Lane dalam Java Murni #
Sebelum menyentuh API sungguhan, mari kita tiru polanya dengan array biasa: loop scalar memproses satu elemen, loop “lanewise” memproses potongan empat elemen per langkah:
public class SimulasiLane {
// Lebar lane ilustratif: peniruan konsep, bukan SIMD sungguhan.
static final int LANE = 4;
public static void main(String[] args) {
int[] data = {3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8};
// 1) Loop scalar: satu elemen diproses per langkah.
long totalScalar = 0;
int langkahScalar = 0;
for (int nilai : data) {
totalScalar += (long) nilai * nilai;
langkahScalar++;
}
// 2) Loop "lanewise": satu potongan 4 elemen per langkah.
long totalLane = 0;
int langkahLane = 0;
for (int basis = 0; basis < data.length; basis += LANE) {
int[] potongan = new int[LANE];
for (int i = 0; i < LANE; i++) {
potongan[i] = data[basis + i]; // muat satu lane penuh
}
long jumlahLane = 0;
for (int i = 0; i < LANE; i++) {
jumlahLane += (long) potongan[i] * potongan[i]; // kuadrat per lane
}
totalLane += jumlahLane;
langkahLane++;
}
System.out.println("total scalar : " + totalScalar);
System.out.println("total lane : " + totalLane);
System.out.println("langkah loop scalar: " + langkahScalar);
System.out.println("langkah loop lane : " + langkahLane);
}
}Output yang Dihasilkan:
total scalar : 296
total lane : 296
langkah loop scalar: 12
langkah loop lane : 3Bedah baris-per-baris untuk bagian pentingnya:
- Konstanta
LANE = 4: lebar potongan ilustratif — dalam SIMD sungguhan angka ini ditentukanVectorSpeciesdan bisa 8, 16, atau 32 tergantung CPU. - Loop pertama: dua belas langkah untuk dua belas elemen; setiap langkah menghitung kuadrat satu nilai — inilah loop scalar klasik.
- Loop kedua (
basis += LANE): hanya tiga langkah karena empat elemen dimuat sekaligus per potongan; jumlah langkah loop pembawa berkurang empat kali lipat meski total kerja per elemen tetap sama. - Baris
total lane : 296: hasil identik dengan jalur scalar — invarian yang sama dijamin Vector API sungguhan: hasil matematis tidak berubah, hanya cara CPU mengeksekusinya.
Wujud Asli Vector API #
Kode berikut memakai API sungguhan — masih incubator sehingga ditampilkan tanpa klaim keluaran otomatis dari tool Course ini, tetapi telah dijalankan di JDK 25 untuk memastikan kebenarannya:
import jdk.incubator.vector.IntVector;
import jdk.incubator.vector.VectorOperators;
import jdk.incubator.vector.VectorSpecies;
public class JumlahKuadrat {
static final VectorSpecies<Integer> SPESIES = IntVector.SPECIES_PREFERRED;
static long jumlahKuadrat(int[] data) {
long total = 0;
int i = 0;
// loopBound: panjang terbesar yang habis dibagi lebar lane.
int batas = SPESIES.loopBound(data.length);
for (; i < batas; i += SPESIES.length()) {
IntVector v = IntVector.fromArray(SPESIES, data, i);
total += v.mul(v).reduceLanesToLong(VectorOperators.ADD);
}
// Ekor: sisa elemen yang tidak memenuhi satu lane penuh.
for (; i < data.length; i++) {
total += (long) data[i] * data[i];
}
return total;
}
public static void main(String[] args) {
int[] data = {3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8, 9, 7, 9, 3};
System.out.println("lane : " + SPESIES.length());
System.out.println("jumlah kuadrat: " + jumlahKuadrat(data));
}
}Jalankan dengan modul incubator aktif:
java --add-modules jdk.incubator.vector JumlahKuadrat.javaPada pengujian di JDK 25 dengan CPU 512-bit, program mencetak jumlah kuadrat: 516 — angka yang sama dengan versi scalar untuk data tersebut, sementara nilai lane mengikuti lebar register CPU mesin eksekusi. Tiga idiom pentingnya: loopBound memisahkan bagian array yang habis dibagi lebar lane, fromArray/intoArray memindahkan data masuk-keluar vektor, dan loop ekor yang tetap scalar menangani sisa — pola wajib karena panjang data dunia nyata jarang habis dibagi.
Status Incubator dan Kapan Memakainya #
| Aspek | Keterangan |
|---|---|
| Status di Java 25 | Incubator kesepuluh (JEP 508) |
| Flag wajib | --add-modules jdk.incubator.vector |
| Jaminan API | Belum ada — signature dapat berubah antar rilis |
| Target pengguna | Pustaka performa tinggi, bukan aplikasi bisnis biasa |
Komputasi vektor menutup sisi performa. Bab berikutnya kembali ke tata kelola task konkuren dengan structured concurrency — masih berstatus preview di Java 25 dan karenanya dibahas sebagai konsep dengan penanda flag.
Referensi #
- OpenJDK: JEP 508 — Vector API (Tenth Incubator)
- OpenJDK: JEP 338 — Vector API (Incubator)
- Oracle: Class jdk.incubator.vector.Vector (Incubator API)
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
SimulasiLane.java
Unduh berkaspublic class SimulasiLane {
// Lebar lane ilustratif: peniruan konsep, bukan SIMD sungguhan.
static final int LANE = 4;
public static void main(String[] args) {
int[] data = {3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8};
// 1) Loop scalar: satu elemen diproses per langkah.
long totalScalar = 0;
int langkahScalar = 0;
for (int nilai : data) {
totalScalar += (long) nilai * nilai;
langkahScalar++;
}
// 2) Loop "lanewise": satu potongan 4 elemen per langkah.
long totalLane = 0;
int langkahLane = 0;
for (int basis = 0; basis < data.length; basis += LANE) {
int[] potongan = new int[LANE];
for (int i = 0; i < LANE; i++) {
potongan[i] = data[basis + i]; // muat satu lane penuh
}
long jumlahLane = 0;
for (int i = 0; i < LANE; i++) {
jumlahLane += (long) potongan[i] * potongan[i]; // kuadrat per lane
}
totalLane += jumlahLane;
langkahLane++;
}
System.out.println("total scalar : " + totalScalar);
System.out.println("total lane : " + totalLane);
System.out.println("langkah loop scalar: " + langkahScalar);
System.out.println("langkah loop lane : " + langkahLane);
}
}