Skip to content

Commit

Permalink
Use SerialExecutor for lazy/eval (facebook#1561)
Browse files Browse the repository at this point in the history
Summary:
Pull Request resolved: facebook#1561

Avoid creating lots of threads by using a `SerialExecutor` with a
timeout in lazy compilation and eval.

Store the `SerialExecutor` in `BCProviderFromSrc` to keep it narrowly
scoped, because we don't need it in ordinary compilation or execution
from bytecode.

Reviewed By: neildhar

Differential Revision: D62604506

fbshipit-source-id: 73e6a72c1b22cf298864f69d925b5a7ebbfcfc5b
  • Loading branch information
avp authored and facebook-github-bot committed Nov 7, 2024
1 parent 032f59a commit a0d150d
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 11 deletions.
16 changes: 16 additions & 0 deletions include/hermes/BCGen/HBC/BCProviderFromSrc.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

#include "hermes/BCGen/HBC/BCProvider.h"
#include "hermes/BCGen/HBC/Bytecode.h"
#include "hermes/Support/SerialExecutor.h"

#include "llvh/ADT/Optional.h"

Expand Down Expand Up @@ -97,6 +98,17 @@ class BCProviderFromSrc final : public BCProviderBase {
/// variables/information.
CompilationData compilationData_;

/// Use an 8MB stack, which is the default size on mac and linux.
static constexpr size_t kExecutorStackSize = 1 << 23;

/// Idle for 1 second before letting the executor thread be cleaned up,
/// after which further tasks will start a new thread.
static constexpr std::chrono::milliseconds kExecutorTimeout =
std::chrono::milliseconds(1000);

/// The executor used to run the compiler.
SerialExecutor serialExecutor_{kExecutorStackSize, kExecutorTimeout};

/// The BytecodeModule that provides the bytecode data.
/// Placed below CompilationData to ensure its destruction before the
/// Module gets deleted.
Expand Down Expand Up @@ -242,6 +254,10 @@ class BCProviderFromSrc final : public BCProviderBase {
sourceHash_ = hash;
};

SerialExecutor &getSerialExecutor() {
return serialExecutor_;
}

static bool classof(const BCProviderBase *provider) {
return provider->getKind() == BCProviderKind::BCProviderFromSrc;
}
Expand Down
33 changes: 22 additions & 11 deletions lib/BCGen/HBC/HBC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@
#include "hermes/Support/PerfSection.h"
#include "hermes/Support/SimpleDiagHandler.h"

#include "llvh/Support/Threading.h"
#include "llvh/Support/raw_ostream.h"

#include <future>

#define DEBUG_TYPE "hbc-backend"

namespace hermes {
Expand Down Expand Up @@ -66,6 +67,21 @@ std::unique_ptr<BytecodeModule> generateBytecodeModuleForEval(

namespace {

/// Execute a function in a SerialExecutor, blocking until the function
/// completes.
/// \param executor the executor to use.
/// \param f the function to execute.
/// \param data the data to pass to the function.
static void executeBlockingInSerialExecutor(
SerialExecutor &executor,
void (*f)(void *),
void *data) {
std::packaged_task<void()> task([f, data]() { f(data); });
auto future = task.get_future();
executor.add([&task]() { task(); });
future.wait();
}

/// Data for the compileLazyFunctionWorker.
class LazyCompilationThreadData {
public:
Expand Down Expand Up @@ -344,12 +360,9 @@ std::pair<bool, llvh::StringRef> compileLazyFunction(

// Run on a thread to prevent stack overflow if this is run from deep inside
// JS execution.

// Use an 8MB stack, which is the default size on mac and linux.
constexpr unsigned kStackSize = 1 << 23;

LazyCompilationThreadData data{provider, funcID};
llvh::llvm_execute_on_thread(compileLazyFunctionWorker, &data, kStackSize);
executeBlockingInSerialExecutor(
provider->getSerialExecutor(), compileLazyFunctionWorker, &data);

if (data.success) {
return std::make_pair(true, llvh::StringRef{});
Expand Down Expand Up @@ -412,12 +425,10 @@ std::pair<std::unique_ptr<BCProviderFromSrc>, std::string> compileEvalModule(
const CompileFlags &compileFlags) {
// Run on a thread to prevent stack overflow if this is run from deep inside
// JS execution.

// Use an 8MB stack, which is the default size on mac and linux.
constexpr unsigned kStackSize = 1 << 23;

EvalThreadData data{std::move(src), provider, enclosingFuncID, compileFlags};
llvh::llvm_execute_on_thread(compileEvalWorker, &data, kStackSize);
executeBlockingInSerialExecutor(
provider->getSerialExecutor(), compileEvalWorker, &data);

return data.success
? std::make_pair(std::move(data.result), "")
: std::make_pair(std::unique_ptr<BCProviderFromSrc>{}, data.error);
Expand Down

0 comments on commit a0d150d

Please sign in to comment.