You need to sign in or sign up before continuing.
Commit 5e58e512 authored by 李朝发's avatar 李朝发

Initial commit

parents
/libutil_jni/target
/target
/.apt_generated
Cargo.lock
jmh-result.json
HashBenchmark-Throughput.log
[package]
name = "util_jni"
version = "0.1.0"
edition = "2021"
[lib]
crate_type = ["cdylib"]
[dependencies]
jni = { version = "0.19", default-features = false }
fxhash = { version = "0.2", default-features = false }
[profile.release]
lto = true
codegen-units = 1
panic = "abort"
strip = true
use jni::{
objects::{JClass, ReleaseMode},
sys::{jbyteArray, jint},
JNIEnv,
};
const OFFSET: u32 = 0x811c9dc5;
const PRIME: u32 = 0x01000193;
#[no_mangle]
pub unsafe extern "system" fn JavaCritical_cn_qmai_JNIUtils_fnv1a_132(
len: jint,
bytes: jbyteArray,
) -> jint {
fnv1a(bytes as *const u8, len) as i32
}
#[no_mangle]
pub unsafe extern "system" fn Java_cn_qmai_JNIUtils_fnv1a_132(
env: JNIEnv,
_class: JClass,
bytes: jbyteArray,
) -> jint {
let bytes = match env.get_primitive_array_critical(bytes, ReleaseMode::NoCopyBack) {
Ok(bytes) => bytes,
Err(e) => panic!("{e}"),
};
let len = match bytes.size() {
Ok(len) => len,
Err(e) => panic!("{e}"),
};
fnv1a(bytes.as_ptr() as *const u8, len) as i32
}
#[no_mangle]
pub unsafe extern "system" fn JavaCritical_cn_qmai_JNIUtils_fnv1_132(
len: jint,
bytes: jbyteArray,
) -> jint {
fnv1(bytes as *const u8, len) as i32
}
#[no_mangle]
pub unsafe extern "system" fn Java_cn_qmai_JNIUtils_fnv1_132(
env: JNIEnv,
_class: JClass,
bytes: jbyteArray,
) -> jint {
let bytes = match env.get_primitive_array_critical(bytes, ReleaseMode::NoCopyBack) {
Ok(bytes) => bytes,
Err(e) => panic!("{e}"),
};
let len = match bytes.size() {
Ok(len) => len,
Err(e) => panic!("{e}"),
};
fnv1(bytes.as_ptr() as *const u8, len) as i32
}
#[inline]
unsafe fn fnv1(ptr: *const u8, len: i32) -> u32 {
let mut hash = OFFSET;
let mut ptr = ptr;
for _ in 0..len {
hash = hash.wrapping_mul(PRIME) ^ ((*ptr) as u32);
ptr = ptr.add(1);
}
hash
}
#[inline]
unsafe fn fnv1a(ptr: *const u8, len: i32) -> u32 {
let mut hash = OFFSET;
let mut ptr = ptr;
for _ in 0..len {
hash = (hash ^ ((*ptr) as u32)).wrapping_mul(PRIME);
ptr = ptr.add(1);
}
hash
}
use jni::{
objects::{JClass, ReleaseMode},
sys::{jbyteArray, jint},
JNIEnv,
};
use std::slice;
#[no_mangle]
pub unsafe extern "system" fn JavaCritical_cn_qmai_JNIUtils_fx_132(
len: jint,
bytes: jbyteArray,
) -> jint {
fxhash::hash32(slice::from_raw_parts(bytes as *const u8, len as usize)) as i32
}
#[no_mangle]
pub unsafe extern "system" fn Java_cn_qmai_JNIUtils_fx_132(
env: JNIEnv,
_class: JClass,
bytes: jbyteArray,
) -> jint {
let bytes = match env.get_primitive_array_critical(bytes, ReleaseMode::NoCopyBack) {
Ok(bytes) => bytes,
Err(e) => panic!("{e}"),
};
let len = match bytes.size() {
Ok(len) => len,
Err(e) => panic!("{e}"),
};
fxhash::hash32(slice::from_raw_parts(
bytes.as_ptr() as *const u8,
len as usize,
)) as i32
}
pub mod fnv;
pub mod fx;
pub mod mur3;
use jni::{
objects::{JClass, ReleaseMode},
sys::{jbyteArray, jint, jlongArray},
JNIEnv,
};
use std::{
ptr::{self},
slice,
};
const C1: u32 = 0xcc9e2d51;
const C2: u32 = 0x1b873593;
const C3: u32 = 0xe6546b64;
const C4: u32 = 0x85ebca6b;
const C5: u32 = 0xc2b2ae35;
const C1_64: u64 = 0x87c37b91114253d5;
const C2_64: u64 = 0x4cf5ad432745937f;
const C3_64: u64 = 0x52dce729;
const C4_64: u64 = 0x38495ab5;
#[no_mangle]
pub unsafe extern "system" fn JavaCritical_cn_qmai_JNIUtils_mur3_1128(
len: jint,
bytes: jbyteArray,
_: jint,
out: jlongArray,
seed: jint,
) {
let (h1, h2) = murmurhash3_128(bytes as *const u8, len, seed as u32);
let out = slice::from_raw_parts_mut(out as *mut u64, 2);
out[0] = h1;
out[1] = h2;
}
#[no_mangle]
pub unsafe extern "system" fn Java_cn_qmai_JNIUtils_mur3_1128(
env: JNIEnv,
_class: JClass,
bytes: jbyteArray,
out: jlongArray,
seed: jint,
) {
let bytes = match env.get_primitive_array_critical(bytes, ReleaseMode::NoCopyBack) {
Ok(bytes) => bytes,
Err(e) => panic!("{e}"),
};
let len = match bytes.size() {
Ok(len) => len,
Err(e) => panic!("{e}"),
};
if let Ok(out) = env.get_primitive_array_critical(out, ReleaseMode::NoCopyBack) {
let (h1, h2) = murmurhash3_128(bytes.as_ptr() as *const u8, len, seed as u32);
let out = slice::from_raw_parts_mut(out.as_ptr() as *mut u64, 2);
out[0] = h1;
out[1] = h2;
}
}
#[no_mangle]
pub unsafe extern "system" fn JavaCritical_cn_qmai_JNIUtils_mur3_1128_1zero(
len: jint,
bytes: jbyteArray,
_: jint,
out: jlongArray,
) {
let (h1, h2) = murmurhash3_128(bytes as *const u8, len, 0);
let out = slice::from_raw_parts_mut(out as *mut u64, 2);
out[0] = h1;
out[1] = h2;
}
#[no_mangle]
pub unsafe extern "system" fn Java_cn_qmai_JNIUtils_mur3_1128_1zero(
env: JNIEnv,
_class: JClass,
bytes: jbyteArray,
out: jlongArray,
) {
let bytes = match env.get_primitive_array_critical(bytes, ReleaseMode::NoCopyBack) {
Ok(bytes) => bytes,
Err(e) => panic!("{e}"),
};
let len = match bytes.size() {
Ok(len) => len,
Err(e) => panic!("{e}"),
};
if let Ok(out) = env.get_primitive_array_critical(out, ReleaseMode::NoCopyBack) {
let (h1, h2) = murmurhash3_128(bytes.as_ptr() as *const u8, len, 0);
let out = slice::from_raw_parts_mut(out.as_ptr() as *mut u64, 2);
out[0] = h1;
out[1] = h2;
}
}
#[no_mangle]
pub unsafe extern "system" fn JavaCritical_cn_qmai_JNIUtils_mur3_132(
len: jint,
bytes: jbyteArray,
seed: jint,
) -> jint {
murmurhash3_32(bytes as *const u8, len, seed as u32) as i32
}
#[no_mangle]
pub unsafe extern "system" fn Java_cn_qmai_JNIUtils_mur3_132(
env: JNIEnv,
_class: JClass,
bytes: jbyteArray,
seed: jint,
) -> jint {
let bytes = match env.get_primitive_array_critical(bytes, ReleaseMode::NoCopyBack) {
Ok(bytes) => bytes,
Err(e) => panic!("{e}"),
};
let len = match bytes.size() {
Ok(len) => len,
Err(e) => panic!("{e}"),
};
murmurhash3_32(bytes.as_ptr() as *const u8, len, seed as u32) as i32
}
#[no_mangle]
pub unsafe extern "system" fn JavaCritical_cn_qmai_JNIUtils_mur3_132_1zero(
len: jint,
bytes: jbyteArray,
) -> jint {
murmurhash3_32(bytes as *const u8, len, 0) as i32
}
#[no_mangle]
pub unsafe extern "system" fn Java_cn_qmai_JNIUtils_mur3_132_1zero(
env: JNIEnv,
_class: JClass,
bytes: jbyteArray,
) -> jint {
let bytes = match env.get_primitive_array_critical(bytes, ReleaseMode::NoCopyBack) {
Ok(bytes) => bytes,
Err(e) => panic!("{e}"),
};
let len = match bytes.size() {
Ok(len) => len,
Err(e) => panic!("{e}"),
};
murmurhash3_32(bytes.as_ptr() as *const u8, len, 0) as i32
}
#[inline]
fn murmurhash3_32(start: *const u8, len: i32, seed: u32) -> u32 {
let nblocks = len / 4;
let mut h = seed;
let mut begin = start;
for _ in 0..nblocks {
let k = u32::from_le(unsafe { ptr::read_unaligned(begin as *const u32) });
h = feed32(h, k);
begin = unsafe { begin.add(4) };
}
unsafe { finish_tail32(begin as *const u8, start.add(len as usize), len as u64, h) }
}
#[inline]
fn feed32(mut h: u32, mut k: u32) -> u32 {
k = k.wrapping_mul(C1).rotate_left(15).wrapping_mul(C2);
h ^= k;
h.rotate_left(13).wrapping_mul(5).wrapping_add(C3)
}
#[inline]
unsafe fn finish_tail32(mut tail: *const u8, end: *const u8, total: u64, mut h: u32) -> u32 {
if tail != end {
let mut k: u32 = 0;
for i in 0..3 {
k ^= ((*tail) as u32) << (8 * i);
tail = tail.add(1);
if tail == end {
break;
}
}
k = k.wrapping_mul(C1).rotate_left(15).wrapping_mul(C2);
h ^= k;
}
fmix32(h ^ total as u32)
}
#[inline]
fn fmix32(mut h: u32) -> u32 {
h ^= h >> 16;
h = h.wrapping_mul(C4);
h ^= h >> 13;
h = h.wrapping_mul(C5);
h ^ (h >> 16)
}
pub fn murmurhash3_128(start: *const u8, len: i32, seed: u32) -> (u64, u64) {
let nblocks = len / 16;
let mut h1 = seed as u64;
let mut h2 = seed as u64;
let mut start = start;
for _ in 0..nblocks {
let (k1, k2) = unsafe {
let k1 = ptr::read_unaligned(start as *const u64);
start = start.add(8);
let k2 = ptr::read_unaligned(start as *const u64);
start = start.add(8);
(u64::from_le(k1), u64::from_le(k2))
};
let res = feed128(h1, h2, k1, k2);
h1 = res.0;
h2 = res.1;
}
unsafe { finish_tail128(start as *const u8, len as usize % 16, len as u64, h1, h2) }
}
#[inline]
fn fmix64(mut k: u64) -> u64 {
k ^= k >> 33;
k = k.wrapping_mul(0xff51afd7ed558ccd);
k ^= k >> 33;
k = k.wrapping_mul(0xc4ceb9fe1a85ec53);
k ^ (k >> 33)
}
#[inline]
fn feed128(mut h1: u64, mut h2: u64, mut k1: u64, mut k2: u64) -> (u64, u64) {
k1 = k1.wrapping_mul(C1_64);
k1 = k1.rotate_left(31);
k1 = k1.wrapping_mul(C2_64);
h1 ^= k1;
h1 = h1.rotate_left(27);
h1 = h1.wrapping_add(h2);
h1 = h1.wrapping_mul(5).wrapping_add(C3_64);
k2 = k2.wrapping_mul(C2_64);
k2 = k2.rotate_left(33);
k2 = k2.wrapping_mul(C1_64);
h2 ^= k2;
h2 = h2.rotate_left(31);
h2 = h2.wrapping_add(h1);
h2 = h2.wrapping_mul(5).wrapping_add(C4_64);
(h1, h2)
}
#[inline]
unsafe fn read_u64(start: *const u8, len: usize) -> (*const u8, usize, u64) {
if len >= 8 {
return (
start.add(8),
len - 8,
u64::from_le((start as *const u64).read_unaligned()),
);
}
let res = read_partial_u64(start, len);
(start, 0, res)
}
#[inline]
unsafe fn read_partial_u64(start: *const u8, len: usize) -> u64 {
let mut res = 0;
match len {
7 => read_7(&mut res, start),
6 => read_6(&mut res, start),
5 => read_5(&mut res, start),
4 => read_4(&mut res, start),
3 => read_3(&mut res, start),
2 => read_2(&mut res, start),
1 => read_1(&mut res, start),
_ => (),
}
res
}
#[inline]
unsafe fn finish_tail128(
tail: *const u8,
remain: usize,
total: u64,
mut h1: u64,
mut h2: u64,
) -> (u64, u64) {
if remain > 0 {
let res = read_u64(tail, remain);
let mut k = res.2;
k = k.wrapping_mul(C1_64);
k = k.rotate_left(31);
k = k.wrapping_mul(C2_64);
h1 ^= k;
if res.1 > 0 {
k = read_partial_u64(res.0, res.1);
k = k.wrapping_mul(C2_64);
k = k.rotate_left(33);
k = k.wrapping_mul(C1_64);
h2 ^= k;
}
}
h1 ^= total;
h2 ^= total;
h1 = h1.wrapping_add(h2);
h2 = h2.wrapping_add(h1);
h1 = fmix64(h1);
h2 = fmix64(h2);
h1 = h1.wrapping_add(h2);
h2 = h2.wrapping_add(h1);
(h1, h2)
}
macro_rules! read_n_val {
($name:ident, $next:ident, $n:expr) => {
#[inline]
fn $name(res: &mut u64, p: *const u8) {
*res |= unsafe { (*p.add($n)) as u64 } << (8 * $n);
$next(res, p);
}
};
}
read_n_val!(read_7, read_6, 6);
read_n_val!(read_6, read_5, 5);
read_n_val!(read_5, read_4, 4);
#[inline]
fn read_4(res: &mut u64, p: *const u8) {
*res |= unsafe { u32::from_le((p as *const u32).read_unaligned()) as u64 }
}
read_n_val!(read_3, read_2, 2);
read_n_val!(read_2, read_1, 1);
#[inline]
fn read_1(res: &mut u64, p: *const u8) {
*res |= unsafe { *p as u64 };
}
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.qmai</groupId>
<artifactId>util_jni</artifactId>
<version>0.0.1</version>
<name>util_jni</name>
<url>https://www.qmai.cn</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.7</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>31.1-jre</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.15</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-generator-annprocess</artifactId>
<version>1.35</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
<distributionManagement>
<repository>
<id>nexus-releases</id>
<name>maven-releases</name>
<url>https://hub.zmcms.cn/repository/maven-releases/</url>
</repository>
<snapshotRepository>
<id>nexus-snapshots</id>
<name>maven-snapshots</name>
<url>https://hub.zmcms.cn/repository/maven-snapshots/</url>
</snapshotRepository>
</distributionManagement>
</project>
\ No newline at end of file
package cn.qmai;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
/**
* @author lizhaofa
* @date 2022-09-21 16:08
*/
public class JNIUtils {
final private static String ARCH = System.getProperty("os.arch");
final private static String TMP_DIR = System.getProperty("java.io.tmpdir");
final private static String LIB = "libutil_jni.so";
static {
File file = null;
try {
file = File.createTempFile("qmai_", null, new File(TMP_DIR).getCanonicalFile());
file.delete();
file.createNewFile();
file.deleteOnExit();
} catch (Exception e) {
System.err.println("创建临时文件失败! " + e.getMessage());
System.exit(0);
}
ClassLoader classLoader = JNIUtils.class.getClassLoader();
try (InputStream in = classLoader.getResourceAsStream(ARCH + File.separator + LIB);
OutputStream out = new FileOutputStream(file)) {
int b;
while ((b = in.read()) != -1) {
out.write(b);
}
System.load(file.getAbsolutePath());
} catch (Exception e) {
System.err.println("驱动加载失败! " + e.getMessage());
System.exit(0);
}
}
public static native int fx_32(byte[] bytes);
public static native int fnv1a_32(byte[] bytes);
public static native int fnv1_32(byte[] bytes);
public static native int mur3_32(byte[] bytes, int seed);
public static native int mur3_32_zero(byte[] bytes);
public static native void mur3_128(byte[] bytes, long[] out, int seed);
public static native void mur3_128_zero(byte[] bytes, long[] out);
}
package cn.qmai;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
/**
* Unit test for simple App.
*/
public class AppTest
{
/**
* Rigorous Test :-)
*/
@Test
public void shouldAnswerWithTrue()
{
assertTrue( true );
}
}
package cn.qmai;
import java.util.concurrent.TimeUnit;
import org.apache.commons.codec.digest.MurmurHash3;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Threads;
import org.openjdk.jmh.annotations.Warmup;
import org.openjdk.jmh.infra.Blackhole;
import org.openjdk.jmh.results.format.ResultFormatType;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import com.google.common.hash.Hashing;
import cn.hutool.core.util.HashUtil;
import cn.hutool.core.util.RandomUtil;
/**
* @author morning
* @date 2022-09-19 10:25
*/
@State(Scope.Thread)
@BenchmarkMode(Mode.Throughput)
@Warmup(iterations = 3, time = 10, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 10, time = 5, timeUnit = TimeUnit.SECONDS)
@Threads(4)
@Fork(2)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
public class HashBenchmark {
@Param(value = { "8", "16", "32", "64", "128" })
private Integer size;
private byte[] bytes;
@Setup
public void setup() {
bytes = RandomUtil.randomBytes(1024 * size);
}
@Benchmark
public void jniMur3(Blackhole hole) {
hole.consume(JNIUtils.mur3_32_zero(bytes));
}
@Benchmark
public void apacheMur3(Blackhole hole) {
hole.consume(MurmurHash3.hash32x86(bytes));
}
@Benchmark
public void guavaMur3(Blackhole hole) {
hole.consume(Hashing.murmur3_32_fixed().hashBytes(bytes));
}
@Benchmark
public void hutoolMur3(Blackhole hole) {
hole.consume(HashUtil.murmur32(bytes));
}
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(HashBenchmark.class.getSimpleName()).output("HashBenchmark-Throughput.log")
.resultFormat(ResultFormatType.JSON)
.build();
new Runner(opt).run();
}
}
package cn.qmai;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
/**
* @author lizhaofa
* @date 2022-09-21 16:09
*/
public class JNIUtilsTest {
@Test
public void murmur3() {
assertEquals(-1898877446, JNIUtils.mur3_32("你".getBytes(), 0));
assertEquals(-1898877446, JNIUtils.mur3_32_zero("你".getBytes()));
assertEquals(337357348, JNIUtils.mur3_32("你好".getBytes(), 0));
assertEquals(337357348, JNIUtils.mur3_32_zero("你好".getBytes()));
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment