wasmtime: Option to disable parallel compilation (#3169)
* Introduce parallel-compilation configuration switch * Plumb parallel_compilation config to compilation * Adjust obj.rs * Address review * Fix compilation fail in `cache` crate * Fix obj.rs Also remove the now unneeded feature in /Cargo.toml * fmt
This commit is contained in:
2
crates/cache/src/lib.rs
vendored
2
crates/cache/src/lib.rs
vendored
@@ -43,6 +43,8 @@ impl<'config> ModuleCacheEntry<'config> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Gets cached data if state matches, otherwise calls the `compute`.
|
/// Gets cached data if state matches, otherwise calls the `compute`.
|
||||||
|
// NOTE: This takes a function pointer instead of a closure so that it doesn't accidentally
|
||||||
|
// close over something not accounted in the cache.
|
||||||
pub fn get_data<T, U, E>(&self, state: T, compute: fn(T) -> Result<U, E>) -> Result<U, E>
|
pub fn get_data<T, U, E>(&self, state: T, compute: fn(T) -> Result<U, E>) -> Result<U, E>
|
||||||
where
|
where
|
||||||
T: Hash,
|
T: Hash,
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ pub struct Compiler {
|
|||||||
strategy: CompilationStrategy,
|
strategy: CompilationStrategy,
|
||||||
tunables: Tunables,
|
tunables: Tunables,
|
||||||
features: WasmFeatures,
|
features: WasmFeatures,
|
||||||
|
parallel_compilation: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Compiler {
|
impl Compiler {
|
||||||
@@ -55,6 +56,7 @@ impl Compiler {
|
|||||||
strategy: CompilationStrategy,
|
strategy: CompilationStrategy,
|
||||||
tunables: Tunables,
|
tunables: Tunables,
|
||||||
features: WasmFeatures,
|
features: WasmFeatures,
|
||||||
|
parallel_compilation: bool,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
isa,
|
isa,
|
||||||
@@ -68,6 +70,7 @@ impl Compiler {
|
|||||||
},
|
},
|
||||||
tunables,
|
tunables,
|
||||||
features,
|
features,
|
||||||
|
parallel_compilation,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -137,8 +140,9 @@ impl Compiler {
|
|||||||
) -> Result<Compilation, SetupError> {
|
) -> Result<Compilation, SetupError> {
|
||||||
let functions = mem::take(&mut translation.function_body_inputs);
|
let functions = mem::take(&mut translation.function_body_inputs);
|
||||||
let functions = functions.into_iter().collect::<Vec<_>>();
|
let functions = functions.into_iter().collect::<Vec<_>>();
|
||||||
let funcs = maybe_parallel!(functions.(into_iter | into_par_iter))
|
|
||||||
.map(|(index, func)| {
|
let funcs = self
|
||||||
|
.run_maybe_parallel(functions, |(index, func)| {
|
||||||
self.compiler.compile_function(
|
self.compiler.compile_function(
|
||||||
translation,
|
translation,
|
||||||
index,
|
index,
|
||||||
@@ -147,8 +151,7 @@ impl Compiler {
|
|||||||
&self.tunables,
|
&self.tunables,
|
||||||
types,
|
types,
|
||||||
)
|
)
|
||||||
})
|
})?
|
||||||
.collect::<Result<Vec<_>, _>>()?
|
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.collect::<CompiledFunctions>();
|
.collect::<CompiledFunctions>();
|
||||||
|
|
||||||
@@ -172,6 +175,33 @@ impl Compiler {
|
|||||||
funcs,
|
funcs,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Run the given closure in parallel if the compiler is configured to do so.
|
||||||
|
pub(crate) fn run_maybe_parallel<
|
||||||
|
A: Send,
|
||||||
|
B: Send,
|
||||||
|
E: Send,
|
||||||
|
F: Fn(A) -> Result<B, E> + Send + Sync,
|
||||||
|
>(
|
||||||
|
&self,
|
||||||
|
input: Vec<A>,
|
||||||
|
f: F,
|
||||||
|
) -> Result<Vec<B>, E> {
|
||||||
|
if self.parallel_compilation {
|
||||||
|
#[cfg(feature = "parallel-compilation")]
|
||||||
|
return input
|
||||||
|
.into_par_iter()
|
||||||
|
.map(|a| f(a))
|
||||||
|
.collect::<Result<Vec<B>, E>>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// In case the parallel-compilation feature is disabled or the parallel_compilation config
|
||||||
|
// was turned off dynamically fallback to the non-parallel version.
|
||||||
|
input
|
||||||
|
.into_iter()
|
||||||
|
.map(|a| f(a))
|
||||||
|
.collect::<Result<Vec<B>, E>>()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Hash for Compiler {
|
impl Hash for Compiler {
|
||||||
@@ -182,6 +212,7 @@ impl Hash for Compiler {
|
|||||||
isa,
|
isa,
|
||||||
tunables,
|
tunables,
|
||||||
features,
|
features,
|
||||||
|
parallel_compilation: _,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
// Hash compiler's flags: compilation strategy, isa, frontend config,
|
// Hash compiler's flags: compilation strategy, isa, frontend config,
|
||||||
|
|||||||
@@ -8,8 +8,6 @@ use crate::compiler::{Compilation, Compiler};
|
|||||||
use crate::link::link_module;
|
use crate::link::link_module;
|
||||||
use crate::object::ObjectUnwindInfo;
|
use crate::object::ObjectUnwindInfo;
|
||||||
use object::File as ObjectFile;
|
use object::File as ObjectFile;
|
||||||
#[cfg(feature = "parallel-compilation")]
|
|
||||||
use rayon::prelude::*;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@@ -112,8 +110,9 @@ impl CompilationArtifacts {
|
|||||||
.translate(data)
|
.translate(data)
|
||||||
.map_err(|error| SetupError::Compile(CompileError::Wasm(error)))?;
|
.map_err(|error| SetupError::Compile(CompileError::Wasm(error)))?;
|
||||||
|
|
||||||
let list = maybe_parallel!(translations.(into_iter | into_par_iter))
|
let list = compiler.run_maybe_parallel::<_, _, SetupError, _>(
|
||||||
.map(|mut translation| {
|
translations,
|
||||||
|
|mut translation| {
|
||||||
let Compilation {
|
let Compilation {
|
||||||
obj,
|
obj,
|
||||||
unwind_info,
|
unwind_info,
|
||||||
@@ -159,8 +158,9 @@ impl CompilationArtifacts {
|
|||||||
},
|
},
|
||||||
has_unparsed_debuginfo,
|
has_unparsed_debuginfo,
|
||||||
})
|
})
|
||||||
})
|
},
|
||||||
.collect::<Result<Vec<_>, SetupError>>()?;
|
)?;
|
||||||
|
|
||||||
Ok((
|
Ok((
|
||||||
main_module,
|
main_module,
|
||||||
list,
|
list,
|
||||||
@@ -226,10 +226,11 @@ impl CompiledModule {
|
|||||||
artifacts: Vec<CompilationArtifacts>,
|
artifacts: Vec<CompilationArtifacts>,
|
||||||
isa: &dyn TargetIsa,
|
isa: &dyn TargetIsa,
|
||||||
profiler: &dyn ProfilingAgent,
|
profiler: &dyn ProfilingAgent,
|
||||||
|
compiler: &Compiler,
|
||||||
) -> Result<Vec<Arc<Self>>, SetupError> {
|
) -> Result<Vec<Arc<Self>>, SetupError> {
|
||||||
maybe_parallel!(artifacts.(into_iter | into_par_iter))
|
compiler.run_maybe_parallel(artifacts, |a| {
|
||||||
.map(|a| CompiledModule::from_artifacts(a, isa, profiler))
|
CompiledModule::from_artifacts(a, isa, profiler)
|
||||||
.collect()
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates `CompiledModule` directly from `CompilationArtifacts`.
|
/// Creates `CompiledModule` directly from `CompilationArtifacts`.
|
||||||
|
|||||||
@@ -20,20 +20,6 @@
|
|||||||
)
|
)
|
||||||
)]
|
)]
|
||||||
|
|
||||||
#[cfg(feature = "parallel-compilation")]
|
|
||||||
macro_rules! maybe_parallel {
|
|
||||||
($e:ident.($serial:ident | $parallel:ident)) => {
|
|
||||||
$e.$parallel()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "parallel-compilation"))]
|
|
||||||
macro_rules! maybe_parallel {
|
|
||||||
($e:ident.($serial:ident | $parallel:ident)) => {
|
|
||||||
$e.$serial()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
mod code_memory;
|
mod code_memory;
|
||||||
mod compiler;
|
mod compiler;
|
||||||
mod instantiate;
|
mod instantiate;
|
||||||
|
|||||||
@@ -356,6 +356,7 @@ pub struct Config {
|
|||||||
pub(crate) async_stack_size: usize,
|
pub(crate) async_stack_size: usize,
|
||||||
pub(crate) async_support: bool,
|
pub(crate) async_support: bool,
|
||||||
pub(crate) deserialize_check_wasmtime_version: bool,
|
pub(crate) deserialize_check_wasmtime_version: bool,
|
||||||
|
pub(crate) parallel_compilation: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
@@ -392,6 +393,7 @@ impl Config {
|
|||||||
async_stack_size: 2 << 20,
|
async_stack_size: 2 << 20,
|
||||||
async_support: false,
|
async_support: false,
|
||||||
deserialize_check_wasmtime_version: true,
|
deserialize_check_wasmtime_version: true,
|
||||||
|
parallel_compilation: true,
|
||||||
};
|
};
|
||||||
ret.cranelift_debug_verifier(false);
|
ret.cranelift_debug_verifier(false);
|
||||||
ret.cranelift_opt_level(OptLevel::Speed);
|
ret.cranelift_opt_level(OptLevel::Speed);
|
||||||
@@ -1210,6 +1212,18 @@ impl Config {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Configure wether wasmtime should compile a module using multiple threads.
|
||||||
|
///
|
||||||
|
/// Disabling this will result in a single thread being used to compile the wasm bytecode.
|
||||||
|
///
|
||||||
|
/// By default parallel compilation is enabled.
|
||||||
|
#[cfg(feature = "parallel-compilation")]
|
||||||
|
#[cfg_attr(nightlydoc, doc(cfg(feature = "parallel-compilation")))]
|
||||||
|
pub fn parallel_compilation(&mut self, parallel: bool) -> &mut Self {
|
||||||
|
self.parallel_compilation = parallel;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn target_isa(&self) -> Box<dyn TargetIsa> {
|
pub(crate) fn target_isa(&self) -> Box<dyn TargetIsa> {
|
||||||
self.isa_flags
|
self.isa_flags
|
||||||
.clone()
|
.clone()
|
||||||
@@ -1226,7 +1240,13 @@ impl Config {
|
|||||||
let isa = self.target_isa();
|
let isa = self.target_isa();
|
||||||
let mut tunables = self.tunables.clone();
|
let mut tunables = self.tunables.clone();
|
||||||
allocator.adjust_tunables(&mut tunables);
|
allocator.adjust_tunables(&mut tunables);
|
||||||
Compiler::new(isa, self.strategy, tunables, self.features)
|
Compiler::new(
|
||||||
|
isa,
|
||||||
|
self.strategy,
|
||||||
|
tunables,
|
||||||
|
self.features,
|
||||||
|
self.parallel_compilation,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn build_allocator(&self) -> Result<Box<dyn InstanceAllocator>> {
|
pub(crate) fn build_allocator(&self) -> Result<Box<dyn InstanceAllocator>> {
|
||||||
|
|||||||
@@ -300,11 +300,19 @@ impl Module {
|
|||||||
engine.cache_config(),
|
engine.cache_config(),
|
||||||
)
|
)
|
||||||
.get_data((engine.compiler(), binary), |(compiler, binary)| {
|
.get_data((engine.compiler(), binary), |(compiler, binary)| {
|
||||||
CompilationArtifacts::build(compiler, binary, USE_PAGED_MEM_INIT)
|
CompilationArtifacts::build(
|
||||||
|
compiler,
|
||||||
|
binary,
|
||||||
|
USE_PAGED_MEM_INIT,
|
||||||
|
)
|
||||||
})?;
|
})?;
|
||||||
} else {
|
} else {
|
||||||
let (main_module, artifacts, types) =
|
let (main_module, artifacts, types) =
|
||||||
CompilationArtifacts::build(engine.compiler(), binary, USE_PAGED_MEM_INIT)?;
|
CompilationArtifacts::build(
|
||||||
|
engine.compiler(),
|
||||||
|
binary,
|
||||||
|
USE_PAGED_MEM_INIT,
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -312,6 +320,7 @@ impl Module {
|
|||||||
artifacts,
|
artifacts,
|
||||||
engine.compiler().isa(),
|
engine.compiler().isa(),
|
||||||
&*engine.config().profiler,
|
&*engine.config().profiler,
|
||||||
|
engine.compiler(),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Self::from_parts(engine, modules, main_module, Arc::new(types), &[])
|
Self::from_parts(engine, modules, main_module, Arc::new(types), &[])
|
||||||
|
|||||||
@@ -291,6 +291,7 @@ impl<'a> SerializedModule<'a> {
|
|||||||
.collect(),
|
.collect(),
|
||||||
engine.compiler().isa(),
|
engine.compiler().isa(),
|
||||||
&*engine.config().profiler,
|
&*engine.config().profiler,
|
||||||
|
engine.compiler(),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
assert!(!modules.is_empty());
|
assert!(!modules.is_empty());
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ pub fn compile_to_obj(
|
|||||||
},
|
},
|
||||||
tunables.clone(),
|
tunables.clone(),
|
||||||
features.clone(),
|
features.clone(),
|
||||||
|
true, // enable parallel compilation
|
||||||
);
|
);
|
||||||
|
|
||||||
let environ = ModuleEnvironment::new(compiler.isa().frontend_config(), &tunables, &features);
|
let environ = ModuleEnvironment::new(compiler.isa().frontend_config(), &tunables, &features);
|
||||||
|
|||||||
Reference in New Issue
Block a user