Beim Anhören von Rust, der Systemprogrammiersprache (?), Gibt es einige Geschichten über FFI (Schnittstelle für Fremdfunktionen). Ich werde. Also nenne ich es von Java, das ich normalerweise schreibe.
Diejenigen, die Java und Rust ein wenig lesen können und wissen, dass es für Java ein Projektmanagement-Tool namens Maven gibt. Es ist nicht schwierig, es geht um die Ebene der Erstellung einer Vorlage für das Projekt.
Die Entwicklungsumgebung verwendet VS-Code. Die Rust- und Java-Ausführungsumgebung wurde mit Docker erstellt. DockerFiile Die Basis ist ein Beispiel einer Java-Entwicklungsumgebung (Debean 10), die von Microsoft bereitgestellt, gespalten und für mich selbst gespielt wurde. Die einzige Änderung ist, dass die Java-Version von 14 auf 11 geändert wurde. Fucking
Installieren Sie dort den Rust-Compiler. Fügen Sie Folgendes hinzu.
DockerFiile
ENV RUSTUP_HOME=/usr/local/rustup \
CARGO_HOME=/usr/local/cargo \
PATH=/usr/local/cargo/bin:$PATH
RUN set -eux; \
\
url="https://static.rust-lang.org/rustup/dist/x86_64-unknown-linux-gnu/rustup-init"; \
wget "$url"; \
chmod +x rustup-init; \
./rustup-init -y --no-modify-path --default-toolchain nightly; \
rm rustup-init; \
chmod -R a+w $RUSTUP_HOME $CARGO_HOME; \
rustup --version; \
cargo --version; \
rustc --version;
RUN apt-get update && apt-get install -y lldb python3-minimal libpython3.7 python3-dev gcc \
&& apt-get autoremove -y && apt-get clean -y && rm -rf /var/lib/apt/lists/* /tmp/library-scripts
Wie der Inhalt
Der Grund, warum der zu installierende Rost ** nächtlich ** ist, wird später beschrieben. Rust Die diesmal auf der Rost-Seite erstellte Datei lautet wie folgt
tree
workspace
│ Cargo.toml
│
├─sample-jna
│ │ Cargo.toml
│ │
│ └─src
│ lib.rs
│
└─scripts
cargo-build.sh
Dies liegt daran, dass ich den Frachtbefehl auf der obersten Ebene des Arbeitsbereichs verwenden wollte.
Erklärung unten Cargo.toml
Cargo.toml
[workspace]
members = ["sample-jna"]
[profile.release]
lto = true
In den beiden oberen Zeilen wird das Verzeichnis sample-jna im Arbeitsbereich als Projekt erkannt. ** lto = true ** ist eine Option zum Reduzieren der Dateigröße während des Builds.
sample-jna/Cargo.toml
Cargo.toml
[package]
name = "sample-jna"
version = "0.1.0"
authors = ["uesugi6111 <[email protected]>"]
edition = "2018"
[lib]
crate-type = ["cdylib"]
** [Paket] ** wird mit ** Ladung neu ** erstellt, es gibt also kein Problem. ** Kistentyp ** von ** [lib] ** ist der Typ nach der Kompilierung. Die angenommene dynamische Bibliothek, die aus einer anderen Sprache aufgerufen werden soll, wird in Referenz geschrieben, um ** cdylib ** anzugeben. , Folge es.
lib.rs Dies ist der Hauptteil der Bibliothek. Dieses Mal habe ich ein Programm vorbereitet, das die Primzahlen bis zum Argument mit einem Algorithmus auflistet, der dem zuvor geschriebenen Eratostenes-Sieb ähnelt und die Zahl zurückgibt.
lib.rs
#[no_mangle]
pub extern fn sieve_liner(n: usize) -> usize{
let mut primes = vec![];
let mut d = vec![0usize; n + 1];
for i in 2..n + 1 {
if d[i] == 0 {
primes.push(i);
d[i] = i;
}
for p in &primes {
if p * i > n {
break;
}
d[*p * i] = *p;
}
}
primes.len()
}
Bei der normalen Kompilierung wird der Funktionsname in einen anderen Namen konvertiert, und wenn Sie ihn von einem anderen Programm aus aufrufen, kennen Sie den Namen nicht. Um dies zu verhindern, fügen Sie der Funktion ** # [no_mangle] ** hinzu.
cargo-build.sh
cargo-build.sh
#!/bin/bash
cargo build --release -Z unstable-options --out-dir ./src/main/resources
Es wird das Build-Skript der Bibliothek sein. --release Gibt den Build mit der Release-Option an. -Z unstable-options --out-dir ./src/main/resources Es ist eine Option, das Verzeichnis anzugeben, das nach dem Erstellen ausgegeben werden soll. Diese Option ist jedoch nur für ** nächtliche ** verfügbar. Daher ist ** nightly ** für die Installation in einer mit Docker erstellten Umgebung angegeben.
Das Ziel des Verzeichnisses wird auf den Speicherort festgelegt, an dem es in der JAR-Datei abgelegt wird, wenn es auf der Java-Seite kompiliert wird.
Java Die auf der Java-Seite erstellte Datei lautet wie folgt.
workspace
│ pom.xml
└─src
└─main
├─java
│ └─com
│ └─mycompany
│ └─app
│ App.java
│
└─resources
Das Verzeichnis ist tief, aber es hat nichts zu bedeuten.
pom.xml Fügen Sie Folgendes zu ** \ <Abhängigkeiten> ** hinzu
pom.xml
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna</artifactId>
<version>5.6.0</version>
</dependency>
App.java
App.java
package com.mycompany.app;
import java.util.ArrayList;
import java.util.List;
import com.sun.jna.Library;
import com.sun.jna.Native;
public class App {
private static final int N = 100000000;
public interface SampleJna extends Library {
SampleJna INSTANCE = Native.load("/libsample_jna.so", SampleJna.class);
int sieve_liner(int value);
};
public static void main(String[] args) {
System.out.println("N = " + N);
System.out.println("FFI :" + executeFFI(N) + "ms");
System.out.println("Java :" + executeJava(N) + "ms");
}
public static long executeFFI(int n) {
long startTime = System.currentTimeMillis();
SampleJna.INSTANCE.sieve_liner(n);
return System.currentTimeMillis() - startTime;
}
public static long executeJava(int n) {
long startTime = System.currentTimeMillis();
sieveLiner(n);
return System.currentTimeMillis() - startTime;
}
public static int sieveLiner(int n) {
List<Integer> primes = new ArrayList<>();
int d[] = new int[n + 1];
for (int i = 2; i < n + 1; ++i) {
if (d[i] == 0) {
primes.add(i);
d[i] = i;
}
for (int p : primes) {
if (p * i > n) {
break;
}
d[p * i] = p;
}
}
return primes.size();
}
}
Implementieren Sie dieselbe Logik wie auf der Rust-Seite und vergleichen Sie die Ausführungszeiten. Die Bibliothek anrufen SampleJna INSTANCE = Native.load("/libsample_jna.so", SampleJna.class); Ich mache es bei. Es scheint in Form von Native.load (Pfad der Bibliothek, Schnittstelle, die die Bibliothek definiert) beschrieben zu werden. Dieses Mal wird die Bibliothek direkt unter main / resources platziert, sodass sie mit einem absoluten Pfad (?) Geschrieben wird.
Maven Bis zu diesem Punkt können Sie den Vorgang überprüfen, aber ich habe ihn auch festgelegt, als ich darüber nachdachte, daraus ein Glas zu machen. Fließen, um Glas zu erstellen --Kompilieren Sie Rust und legen Sie es im Ressourcenverzeichnis auf der Java-Seite ab
Dies erfolgt in einer Aktion mit den Funktionen von Maven.
maven-assembly-plugin Fügen Sie abhängige Bibliotheken in jar ein
maven-assembly-plugin
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.3.0</version>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<classpathPrefix>/</classpathPrefix>
<mainClass>com.mycompany.app.App</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
exec-maven-plugin Erforderlich, um Shell-Skripte während der Maven-Verarbeitung auszuführen.
exec-maven-plugin
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.3.2</version>
<executions>
<execution>
<id>dependencies</id>
<phase>generate-resources</phase>
<goals>
<goal>exec</goal>
</goals>
<configuration>
<workingDirectory>${project.basedir}</workingDirectory>
<executable>${project.basedir}/scripts/cargo-build.sh </executable>
</configuration>
</execution>
</executions>
</plugin>
Ein kleiner Kommentar phase Legen Sie den Zeitpunkt für die Ausführung des Shell-Skripts fest. Maven hat ein Konzept für den Lebenszyklus. Geben Sie daher das Konzept an, das dem Zeitpunkt entspricht, den Sie ausführen möchten. Referenz executable Geben Sie hier das Ziel an, das Sie ausführen möchten.
pom.xml Bisher angepasste Dateien
pom.xml
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.app</groupId>
<artifactId>my-app</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>my-app</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.7.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna</artifactId>
<version>5.6.0</version>
</dependency>
</dependencies>
<properties>
<jdk.version>11</jdk.version>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.3.0</version>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<classpathPrefix>/</classpathPrefix>
<mainClass>com.mycompany.app.App</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M3</version>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.3.2</version>
<executions>
<execution>
<id>dependencies</id>
<phase>generate-resources</phase>
<goals>
<goal>exec</goal>
</goals>
<configuration>
<workingDirectory>${project.basedir}</workingDirectory>
<executable>${project.basedir}/scripts/cargo-build.sh </executable>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Führen Sie die folgenden Schritte im Stammverzeichnis des Arbeitsbereichs aus
mvn package
Dann
[INFO] --- maven-assembly-plugin:3.3.0:single (make-assembly) @ my-app ---
[INFO] Building jar: /workspace/target/my-app-1.0-SNAPSHOT-jar-with-dependencies.jar
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
Ein solches Protokoll wird ausgegeben und die Kompilierung abgeschlossen.
Führen Sie danach die JAR-Ausgabe im angezeigten Pfad aus.
Beispiel
java -jar ./target/my-app-1.0-SNAPSHOT-jar-with-dependencies.jar
Ausgabe
N = 100000000
FFI :1668ms
Java :3663ms
Die Zeit (ms) von Java und FFI (Rust), die durch Zählen der Anzahl der Primzahlen bis zu 10 ^ 8 benötigt wurde, wurde ausgegeben. Ich weiß nicht, ob es viel Overhead gibt, weil Java mit N kleiner schneller ist.
Vorerst ist es umgezogen, daher wird es einmal fertiggestellt. Es wird die verwendete Quelle sein https://github.com/uesugi6111/java-rust
Es gibt viele Geschichten, die ich normalerweise nicht anfasse, und ich verstehe immer noch nicht viel, aber ich werde sie nach und nach untersuchen.
Recommended Posts