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:
Sergei Shulepov
2021-08-10 21:09:15 +02:00
committed by GitHub
parent 42acb72c54
commit cbabcacb0f
8 changed files with 81 additions and 30 deletions

View File

@@ -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,

View File

@@ -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,

View File

@@ -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`.

View File

@@ -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;

View File

@@ -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>> {

View File

@@ -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), &[])

View File

@@ -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());

View File

@@ -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);