Skip to content

Commit

Permalink
feature: Disable unused multithreading to improve performance (scala-…
Browse files Browse the repository at this point in the history
…native#3670)

* Disable unused multithreading to improve performance
  • Loading branch information
WojciechMazur committed Jan 24, 2024
1 parent 944d04b commit 5d39cae
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 11 deletions.
1 change: 0 additions & 1 deletion nativelib/src/main/resources/scala-native/gc/boehm/gc.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
#include <gc/gc.h>
#include "shared/ScalaNativeGC.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "shared/Parsing.h"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,6 @@ void scalanative_GC_remove_roots(void *addr_low, void *addr_high) {
GC_Roots_RemoveByRange(customRoots, range);
}

#ifdef SCALANATIVE_MULTITHREADING_ENABLED
typedef void *RoutineArgs;
typedef struct {
ThreadStartRoutine fn;
Expand Down Expand Up @@ -183,8 +182,10 @@ void scalanative_GC_set_mutator_thread_state(GC_MutatorThreadState state) {
MutatorThread_switchState(currentMutatorThread, state);
}
void scalanative_GC_yield() {
#ifdef SCALANATIVE_MULTITHREADING_ENABLED
if (atomic_load_explicit(&Synchronizer_stopThreads, memory_order_relaxed))
Synchronizer_yield();
#endif
}
#endif // SCALANATIVE_MULTITHREADING_ENABLED
#endif

#endif // defined(SCALANATIVE_GC_COMMIX)
4 changes: 2 additions & 2 deletions nativelib/src/main/resources/scala-native/gc/immix/ImmixGC.c
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,6 @@ size_t scalanative_GC_get_max_heapsize() {
return Parse_Env_Or_Default("GC_MAXIMUM_HEAP_SIZE", Heap_getMemoryLimit());
}

#ifdef SCALANATIVE_MULTITHREADING_ENABLED
typedef void *RoutineArgs;
typedef struct {
ThreadStartRoutine fn;
Expand Down Expand Up @@ -147,10 +146,11 @@ void scalanative_GC_set_mutator_thread_state(GC_MutatorThreadState state) {
}

void scalanative_GC_yield() {
#ifdef SCALANATIVE_MULTITHREADING_ENABLED
if (atomic_load_explicit(&Synchronizer_stopThreads, memory_order_relaxed))
Synchronizer_yield();
#endif
}
#endif // SCALANATIVE_MULTITHREADING_ENABLED

void scalanative_GC_add_roots(void *addr_low, void *addr_high) {
AddressRange range = {addr_low, addr_high};
Expand Down
2 changes: 0 additions & 2 deletions nativelib/src/main/resources/scala-native/gc/none/gc.c
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,6 @@ void scalanative_GC_collect() {}

void scalanative_GC_register_weak_reference_handler(void *handler) {}

#ifdef SCALANATIVE_MULTITHREADING_ENABLED
#ifdef _WIN32
HANDLE scalanative_GC_CreateThread(LPSECURITY_ATTRIBUTES threadAttributes,
SIZE_T stackSize, ThreadStartRoutine routine,
Expand All @@ -152,7 +151,6 @@ int scalanative_GC_pthread_create(pthread_t *thread, pthread_attr_t *attr,
return pthread_create(thread, attr, routine, args);
}
#endif
#endif // SCALANATIVE_MULTITHREADING_ENABLED

// ScalaNativeGC interface stubs. None GC does not need STW
void scalanative_GC_set_mutator_thread_state(GC_MutatorThreadState unused){};
Expand Down
52 changes: 49 additions & 3 deletions tools/src/main/scala/scala/scalanative/build/Build.scala
Original file line number Diff line number Diff line change
Expand Up @@ -104,11 +104,18 @@ object Build {
checkWorkdirExists(initialConfig)

// validate Config
val config = Validator.validate(initialConfig)
var config = Validator.validate(initialConfig)
config.logger.debug(config.toString())
def linkNIRForEntries = ScalaNative.link(config, entries(config))

ScalaNative
.link(config, entries(config))
linkNIRForEntries
.flatMap { linkerResult =>
val (updatedConfig, needsToReload) =
postRechabilityAnalysisConfigUpdate(config, linkerResult)
config = updatedConfig
if (needsToReload) linkNIRForEntries
else Future.successful(linkerResult)
}
.flatMap(optimize(config, _))
.flatMap(linkerResult =>
codegen(config, linkerResult)
Expand Down Expand Up @@ -179,6 +186,45 @@ object Build {
LLVM.link(config, analysis, compiled)
}

/** Based on reachability analysis check if config can be tuned for better
* performance
*/
private def postRechabilityAnalysisConfigUpdate(
config: Config,
analysis: ReachabilityAnalysis.Result
): (Config, Boolean) = {
var currentConfig = config
var needsToReload = true

// Each block can modify currentConfig stat,
// modification should be lazy to not reconstruct object when not required
locally { // disable unused mulithreading
val envFlag = "SCALANATIVE_DISABLE_UNUSED_MULTITHREADING"
val suppressDisablingThreads = sys.env.get(envFlag).contains("0")
if (!suppressDisablingThreads && config.compilerConfig.multithreadingSupport) {
// format: off
val jlThread = nir.Global.Top("java.lang.Thread")
val jlThreadStart = jlThread.member(nir.Sig.Method("start", Seq(nir.Type.Unit)))
val jlPlatformContext = nir.Global.Top("java.lang.PlatformThreadContext")
val jlPlatformContextStart = jlPlatformContext.member(nir.Sig.Method("start", Seq(nir.Type.Ref(jlThread), nir.Type.Unit)))
val usesSystemThreads = analysis.infos.contains(jlThreadStart) || analysis.infos.contains(jlPlatformContextStart)
// format: on
if (!usesSystemThreads) {
config.logger.info(
"Detected enabled multithreading, but not found any usage of system threads. " +
"Multihreading will be disabled to improve performance. " +
s"This behavior can be disabled by setting enviornment variable $envFlag=0."
)
currentConfig = currentConfig.withCompilerConfig(
_.withMultithreadingSupport(false)
)
needsToReload = true
}
}
}
currentConfig -> needsToReload
}

/** Links the DWARF debug information found in the object files. */
private def postProcess(config: Config, artifact: Path): Path =
config.logger.time("Postprocessing") {
Expand Down

0 comments on commit 5d39cae

Please sign in to comment.