generated from oracle/template-repo
-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
WIP: Micronaut scaling Pandas with multi-context
- Loading branch information
Showing
7 changed files
with
266 additions
and
25 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
3 changes: 0 additions & 3 deletions
3
...onaut-multithreaded/src/main/java/graalpy/micronaut/multithreaded/DataAnalysisModule.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
49 changes: 49 additions & 0 deletions
49
...hreaded/src/main/java/graalpy/micronaut/multithreaded/DataAnalysisModuleMultiContext.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
package graalpy.micronaut.multithreaded; | ||
|
||
import java.util.concurrent.ExecutionException; | ||
|
||
import io.micronaut.context.annotation.Bean; | ||
|
||
@Bean | ||
public class DataAnalysisModuleMultiContext implements DataAnalysisModule { | ||
|
||
private final PythonPool pool; | ||
|
||
public DataAnalysisModuleMultiContext(PythonPool pool) { | ||
this.pool = pool; | ||
for (int i = 0; i < pool.getPoolSize(); i++) { | ||
pool.submit(() -> getModule()); | ||
} | ||
} | ||
|
||
private DataAnalysisModule getModule() { | ||
return pool.eval("import data_analysis; data_analysis").as(DataAnalysisModule.class); | ||
} | ||
|
||
@Override | ||
public double calculateMean(String csv, int column) { | ||
try { | ||
return pool.submit(() -> getModule().calculateMean(csv, column)).get(); | ||
} catch (InterruptedException | ExecutionException e) { | ||
throw new RuntimeException(e); | ||
} | ||
} | ||
|
||
@Override | ||
public double calculateMedian(String csv, int column) { | ||
try { | ||
return pool.submit(() -> getModule().calculateMedian(csv, column)).get(); | ||
} catch (InterruptedException | ExecutionException e) { | ||
throw new RuntimeException(e); | ||
} | ||
} | ||
|
||
@Override | ||
public String describe(String csv) { | ||
try { | ||
return pool.submit(() -> getModule().describe(csv)).get(); | ||
} catch (InterruptedException | ExecutionException e) { | ||
throw new RuntimeException(e); | ||
} | ||
} | ||
} |
7 changes: 7 additions & 0 deletions
7
...readed/src/main/java/graalpy/micronaut/multithreaded/DataAnalysisModuleSingleContext.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package graalpy.micronaut.multithreaded; | ||
|
||
import io.micronaut.graal.graalpy.annotations.GraalPyModule; | ||
|
||
@GraalPyModule("data_analysis") | ||
public interface DataAnalysisModuleSingleContext extends DataAnalysisModule { | ||
} |
40 changes: 40 additions & 0 deletions
40
...tithreaded/src/main/java/graalpy/micronaut/multithreaded/PythonContextBuilderFactory.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
package graalpy.micronaut.multithreaded; | ||
|
||
import java.io.IOException; | ||
import java.nio.file.Path; | ||
|
||
import org.graalvm.polyglot.Context; | ||
import org.graalvm.python.embedding.utils.GraalPyResources; | ||
import org.graalvm.python.embedding.utils.VirtualFileSystem; | ||
|
||
import io.micronaut.context.annotation.Bean; | ||
import io.micronaut.context.annotation.Replaces; | ||
import io.micronaut.graal.graalpy.GraalPyContextBuilderFactory; | ||
import jakarta.inject.Singleton; | ||
|
||
@Bean | ||
@Singleton | ||
@Replaces(GraalPyContextBuilderFactory.class) | ||
public class PythonContextBuilderFactory implements GraalPyContextBuilderFactory { | ||
@Override | ||
public Context.Builder createBuilder() { | ||
var resourcesDir = Path.of(System.getProperty("user.dir"), "graalpy.resources.single"); | ||
var rf = resourcesDir.toFile(); | ||
synchronized (PythonContextBuilderFactory.class) { | ||
if (!rf.isDirectory() || rf.lastModified() / 1000 < ProcessHandle.current().info().startInstant().get().getEpochSecond()) { | ||
var fs = VirtualFileSystem.create(); | ||
try { | ||
GraalPyResources.extractVirtualFileSystemResources(fs, resourcesDir); | ||
} catch (IOException e) { | ||
throw new RuntimeException(e); | ||
} | ||
} | ||
} | ||
return GraalPyResources.contextBuilder(resourcesDir) | ||
.allowExperimentalOptions(true) | ||
.option("python.WarnExperimentalFeatures", "false") | ||
.option("python.IsolateNativeModules", "true") | ||
.allowNativeAccess(true) | ||
.allowCreateProcess(true); | ||
} | ||
} |
126 changes: 126 additions & 0 deletions
126
...lpy-micronaut-multithreaded/src/main/java/graalpy/micronaut/multithreaded/PythonPool.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
package graalpy.micronaut.multithreaded; | ||
|
||
import java.io.IOException; | ||
import java.nio.file.Path; | ||
import java.util.List; | ||
import java.util.concurrent.AbstractExecutorService; | ||
import java.util.concurrent.BlockingQueue; | ||
import java.util.concurrent.ExecutorService; | ||
import java.util.concurrent.Executors; | ||
import java.util.concurrent.LinkedBlockingQueue; | ||
import java.util.concurrent.ThreadFactory; | ||
import java.util.concurrent.TimeUnit; | ||
|
||
import org.graalvm.polyglot.Context; | ||
import org.graalvm.polyglot.Engine; | ||
import org.graalvm.polyglot.Value; | ||
import org.graalvm.python.embedding.utils.GraalPyResources; | ||
import org.graalvm.python.embedding.utils.VirtualFileSystem; | ||
|
||
@io.micronaut.context.annotation.Context | ||
public class PythonPool extends AbstractExecutorService { | ||
private final Engine engine; | ||
private final ThreadLocal<Context> thisContext; | ||
private final BlockingQueue<Context> contexts; | ||
private final ExecutorService threadPool; | ||
private final int size; | ||
|
||
private static Context createContext(Engine engine) { | ||
var resourcesDir = Path.of(System.getProperty("user.dir"), "graalpy.resources"); | ||
var rf = resourcesDir.toFile(); | ||
synchronized (PythonPool.class) { | ||
if (!rf.isDirectory() || rf.lastModified() / 1000 < ProcessHandle.current().info().startInstant().get().getEpochSecond()) { | ||
var fs = VirtualFileSystem.create(); | ||
try { | ||
GraalPyResources.extractVirtualFileSystemResources(fs, resourcesDir); | ||
} catch (IOException e) { | ||
throw new RuntimeException(e); | ||
} | ||
} | ||
} | ||
var context = GraalPyResources.contextBuilder(resourcesDir) | ||
.engine(engine) | ||
.allowExperimentalOptions(true) | ||
.option("python.PythonHome", "") | ||
.option("python.WarnExperimentalFeatures", "false") | ||
.option("python.IsolateNativeModules", "true") | ||
.allowNativeAccess(true) | ||
.allowCreateProcess(true) | ||
.build(); | ||
context.initialize("python"); | ||
return context; | ||
} | ||
|
||
public Value eval(String code) { | ||
var c = thisContext.get(); | ||
if (c == null) { | ||
throw new IllegalStateException("PythonPool#eval can only be called from inside a submitted task"); | ||
} | ||
return c.eval("python", code); | ||
} | ||
|
||
public PythonPool() { | ||
this(5); | ||
} | ||
|
||
public int getPoolSize() { | ||
return size; | ||
} | ||
|
||
private PythonPool(int nContexts) { | ||
size = nContexts; | ||
engine = Engine.create(); | ||
contexts = new LinkedBlockingQueue<>(); | ||
thisContext = new ThreadLocal<>(); | ||
threadPool = Executors.newFixedThreadPool(nContexts, new ThreadFactory() { | ||
@Override | ||
public Thread newThread(Runnable r) { | ||
return new Thread(() -> { | ||
var c = createContext(engine); | ||
contexts.add(c); | ||
thisContext.set(c); | ||
r.run(); | ||
}); | ||
} | ||
}); | ||
} | ||
|
||
public static PythonPool newFixedPool(int nContexts) { | ||
return new PythonPool(nContexts); | ||
} | ||
|
||
@Override | ||
public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { | ||
return threadPool.awaitTermination(timeout, unit); | ||
} | ||
|
||
@Override | ||
public void execute(Runnable command) { | ||
threadPool.execute(command); | ||
} | ||
|
||
@Override | ||
public void shutdown() { | ||
threadPool.shutdown(); | ||
contexts.stream().forEach(c -> c.close()); | ||
contexts.clear(); | ||
} | ||
|
||
@Override | ||
public List<Runnable> shutdownNow() { | ||
var r = threadPool.shutdownNow(); | ||
contexts.stream().forEach(c -> c.close(true)); | ||
contexts.clear(); | ||
return r; | ||
} | ||
|
||
@Override | ||
public boolean isShutdown() { | ||
return threadPool.isShutdown(); | ||
} | ||
|
||
@Override | ||
public boolean isTerminated() { | ||
return threadPool.isTerminated(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters