Rustuna: A Faster Optuna Implementation in Rust
Since Optuna was launched in 2018, it has expanded far beyond hyperparameter optimization for machine learning, growing into a versatile tool used across diverse domains. However, as adoption has scaled into increasingly complex fields, new challenges have emerged, most notably regarding execution speed and memory consumption during large-scale optimizations.
Inspired by the success of tools like Ruff, Polars, and Pydantic V2, which achieved dramatic performance improvements by leveraging Rust, we have developed and released Rustuna, a faster Optuna implementation built with Rust. In this post, we introduce Rustuna’s key features, how to use it, and our vision for its future.
- GitHub: https://github.com/optuna/rustuna/
- Documentation: https://rustuna.readthedocs.io/
Alongside Rustuna, we are also excited to announce Optuna 5.0, the fifth major release of Optuna. For full details on the new features and improvements, check out the Optuna 5.0 Release Blog.
Getting Started with Rustuna
Rustuna provides high compatibility with Optuna. If you are already familiar with Optuna APIs, there is no need to learn a new API, so you can transition smoothly. Here is how to get started:
$ pip install rustunaimport rustuna
# 1. Define an objective function which returns a value you want to minimize.
def objective(trial: rustuna.Trial) -> float:
# 2. Define the search space via Suggest APIs.
x = trial.suggest_float("x", -10, 10)
y = trial.suggest_float("y", -10, 10)
return (x - 2) ** 2 + (y + 5) ** 2
# 3. Create a study which manages each experiment.
study = rustuna.create_study()
# 4. Evaluate your objective function.
study.optimize(objective, n_trials=100)
# 5. Print the best evaluation parameters.
print(study.best_trial)While Rustuna is not yet as feature-rich as Optuna, it already includes the core functionality most users need. Furthermore, except for a few specific features, Rustuna does not require Python dependencies. This minimal footprint helps mitigate risks associated with the recent surge in supply chain attacks. Even if execution speed or memory usage has never been a bottleneck for you, we encourage you to give it a try.
If you need features that Rustuna does not support, you can still leverage Optuna’s functionality. While certain use cases may not fully benefit from Rustuna’s speed advantages, this allows you to seamlessly use Optuna features, such as its visualization modules, after executing an optimization. For details, please refer to the official Rustuna documentation.
How Rustuna Got So Fast
Rustuna was built with a strong focus on its execution speed. Figure 1 compares the execution speeds of the sampling algorithms provided by Rustuna and Optuna (measured on a MacBook with M4 Pro).
TPE and MOTPE (Multi-Objective TPE) are the default optimization algorithms used in Optuna. With TPE, the computation time required to sample parameters increases proportionally with the number of trials. While Optuna recommended using TPE primarily for runs under 1,000 trials, Rustuna’s high-speed TPE implementation can complete 10,000 trials, ten times as many, in under a minute. This performance gap becomes even more pronounced when leveraging multi-threading (Table 1).
Algorithms like CMA-ES and NSGA-II also achieve massive speedups, but not simply because they were rewritten in Rust. Algorithmic theory dictates that sampling time for these methods should remain constant relative to trial count; however, due to architectural trade-offs, Optuna’s execution speed degrades as trial counts increase. Rustuna achieves its speed gains by combining two approaches: an optimized Rust implementation and a refined algorithmic architecture.
Memory-efficient Storage Implementation
Rustuna is also focused on optimizing memory consumption. In addition to performance gains achieved through lower-level architectural and implementation tuning, Rustuna differs significantly from Optuna by providing a feature to discard optimization histories that are no longer needed during optimization.
Get c-bata’s stories in your inbox
Join Medium for free to get updates from this writer.
Unlike TPE or Gaussian Process-based Bayesian optimization algorithms, methods such as CMA-ES and NSGA-II only reference a subset of evaluation histories while running. However, Optuna retains all evaluation histories in memory indefinitely, causing both memory usage and execution time to degrade as the optimization progresses. By using the apply_discard option provided across Rustuna’s storage classes, unnecessary trial history is automatically purged from memory during the run.
import rustuna
def objective(trial: rustuna.Trial) -> float:
x = trial.suggest_float("x", -10, 10)
trial.set_user_attr("large_text", "a" * 10000)
return (x - 2) ** 2
# Set apply_discard=True to enable feature to discard trials.
storage = rustuna.storages.JournalFileStorage("./rustuna-journal.db", apply_discard=True)
sampler = rustuna.samplers.CmaEsSampler()
study = rustuna.create_study(sampler=sampler, storage=storage, study_name="example")
study.optimize(objective, n_trials=100000)While discarded during optimization, there are cases where you still want to analyze the complete evaluation history afterward. By loading the storage file without specifying the apply_discard option, the full history, including all previously discarded trials, can be loaded:
storage = rustuna.storages.JournalFileStorage("./rustuna-journal.db")
study = rustuna.load_study(storage=storage, study_name="example")
print(study.best_trial)We also validated the impact of this feature in a real-world production service. Matlantis Corporation utilizes a custom NSGA-II-based method built on Optuna for their crystal structure prediction platform, Matlantis CSP (see: Crystal Structure Prediction Using Optuna in Matlantis CSP). Figure 2 shows the impact on memory usage and execution speed after migrating to Rustuna.
With the Optuna implementation, memory consumption increases as evaluation histories accumulate over time. Furthermore, because their custom NSGA-II algorithm sequentially scans the complete evaluation history to extract necessary values, processing times degrade as the list grows.
In contrast, Rustuna’s apply_discard option discards unneeded trials, keeping memory growth minimal and preventing execution slowdowns even over long runs. This enables larger trial counts and greater parallelism. Based on these validation results, active development is now underway to fully integrate Rustuna into production.
What’s Ahead
Below is a look at our upcoming development roadmap for Rustuna. While Rustuna does not yet offer every capability available in Optuna, it already supports a wide range of essential features, including key optimization algorithms, storage backends, and hyperparameter importance calculations, making it fully capable of handling the vast majority of use cases.
To further improve usability and expand possibilities, we plan to focus on the following enhancements:
- Improving Stability: Rustuna is still an emerging project, and new bugs are expected following this initial release. We plan to continuously refine and improve stability based on community feedback.
- Seamless Optuna Integration: Rustuna is not intended as a complete replacement for Optuna, nor is its goal to match Optuna’s vast feature set. Instead, it is designed to work in tandem with Optuna. We will continue strengthening integration so users can invoke Optuna features even more effortlessly.
- Multi-Language Support: Because Rustuna is implemented in Rust, it can easily target other languages via C APIs or WebAssembly. While development has focused primarily on Python bindings so far, we plan to explore support for Node.js and other ecosystems.
Additionally, we plan to bring the architectural insights gained from Rustuna back into Optuna itself. Because Optuna supports a large ecosystem, maintaining backward compatibility is critical, requiring a careful, step-by-step approach to structural updates. However, several design changes introduced in Rustuna, such as bulk-insert storage APIs, trial queues, and the apply_discard option, offer clear benefits for Optuna’s performance. We look forward to gradually porting these improvements back to Optuna while ensuring seamless compatibility.
Conclusion
In this post, we covered the background behind Rustuna’s development, how to get started, and its key advantages over Optuna.
For Rustuna to evolve into a mature and reliable software project, real-world usage and community feedback are vital. We invite you to give Rustuna a try, and if you encounter any issues or have suggestions, please feel free to open an issue on GitHub!









