Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

perf(compile): code cache #26528

Merged
merged 25 commits into from
Nov 18, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
7236756
perf(compile): use less memory
dsherret Oct 22, 2024
1928daf
working now with typescript
dsherret Oct 23, 2024
32913e7
Merge branch 'main' into perf_deno_compile_less_memory
dsherret Oct 23, 2024
788f4ab
Tell v8 that something is a string like before.
dsherret Oct 23, 2024
742ae3f
fix byonm issue
dsherret Oct 23, 2024
1ad5f62
Merge branch 'main' into perf_deno_compile_less_memory
dsherret Oct 23, 2024
fd13339
maybe fix ci
dsherret Oct 24, 2024
1e0dabd
do not store data urls in the binary
dsherret Oct 24, 2024
d7cd10b
switch to le because this is not network
dsherret Oct 24, 2024
c156944
review
dsherret Oct 24, 2024
938c3e0
perf(compile): code cache for initial load
dsherret Oct 24, 2024
0a7c050
use distinct strategies for compile
dsherret Oct 24, 2024
0957e09
use distinct strategies for compile
dsherret Oct 24, 2024
bad329a
tests
dsherret Oct 24, 2024
e9e4ad2
support --no-code-cache
dsherret Oct 24, 2024
8a910fd
lint
dsherret Oct 24, 2024
bc08455
Merge branch 'main' into perf_compile_code_cache
dsherret Oct 24, 2024
815be3a
remove unused use
dsherret Oct 24, 2024
ec0d0c7
fix test
dsherret Oct 24, 2024
1140105
Do not subtract with overflow when deserializing.
dsherret Oct 24, 2024
5b13157
maybe fix test failing because they had the same binary name
dsherret Oct 24, 2024
d360dda
Merge branch 'main' into perf_compile_code_cache
dsherret Nov 18, 2024
488f6dc
update after merge
dsherret Nov 18, 2024
0e38fab
lint
dsherret Nov 18, 2024
b1a784b
Merge branch 'main' into perf_compile_code_cache
dsherret Nov 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
use distinct strategies for compile
  • Loading branch information
dsherret committed Oct 24, 2024
commit 0a7c050bbc7c90c36d56da8f559ca1445f93bde1
10 changes: 10 additions & 0 deletions cli/cache/code_cache.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.

use std::sync::Arc;

use deno_ast::ModuleSpecifier;
use deno_core::error::AnyError;
use deno_runtime::code_cache;
use deno_runtime::deno_webstorage::rusqlite::params;

use crate::worker::CliCodeCache;

use super::cache_db::CacheDB;
use super::cache_db::CacheDBConfiguration;
use super::cache_db::CacheDBHash;
Expand Down Expand Up @@ -82,6 +86,12 @@ impl CodeCache {
}
}

impl CliCodeCache for CodeCache {
fn as_code_cache(self: Arc<Self>) -> Arc<dyn code_cache::CodeCache> {
self
}
}

impl code_cache::CodeCache for CodeCache {
fn get_sync(
&self,
Expand Down
245 changes: 145 additions & 100 deletions cli/standalone/code_cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use std::io::Read;
use std::io::Write;
use std::path::Path;
use std::path::PathBuf;
use std::sync::Arc;

use deno_ast::ModuleSpecifier;
use deno_core::anyhow::bail;
Expand All @@ -19,37 +20,11 @@ use deno_runtime::code_cache::CodeCacheType;

use crate::cache::FastInsecureHasher;
use crate::util::path::get_atomic_file_path;
use crate::worker::CliCodeCache;

struct MutableData {
cache: HashMap<String, DenoCompileCodeCacheEntry>,
modified: bool,
add_count: usize,
}

impl MutableData {
fn take_from_cache(
&mut self,
specifier: &ModuleSpecifier,
source_hash: u64,
) -> Option<Vec<u8>> {
let entry = self.cache.remove(specifier.as_str())?;
if entry.source_hash != source_hash {
return None;
}
Some(entry.data)
}

fn take_cache_data(
&mut self,
) -> Option<HashMap<String, DenoCompileCodeCacheEntry>> {
// always purge this from memory
let cache_data = std::mem::take(&mut self.cache);

if !self.modified {
return None;
}
Some(cache_data)
}
enum CodeCacheStrategy {
FirstRun(FirstRunCodeCacheStrategy),
SubsequentRun(SubsequentRunCodeCacheStrategy),
}

#[derive(Debug, Clone)]
Expand All @@ -59,49 +34,37 @@ pub struct DenoCompileCodeCacheEntry {
}

pub struct DenoCompileCodeCache {
cache_key: String,
file_path: PathBuf,
finished: AtomicFlag,
data: Mutex<MutableData>,
strategy: CodeCacheStrategy,
}

impl DenoCompileCodeCache {
pub fn new(file_path: PathBuf, cache_key: String) -> Self {
// attempt to deserialize the cache data
let cache = match deserialize(&file_path, &cache_key) {
Ok(cache) => cache,
Err(err) => {
log::debug!("Failed to deserialize code cache: {}", err);
HashMap::new()
}
};

Self {
cache_key,
file_path,
finished: AtomicFlag::lowered(),
data: Mutex::new(MutableData {
cache,
modified: false,
add_count: 0,
}),
}
}

fn write_cache_data(
&self,
cache_data: &HashMap<String, DenoCompileCodeCacheEntry>,
) {
let temp_file = get_atomic_file_path(&self.file_path);
match serialize(&temp_file, &self.cache_key, cache_data) {
Ok(()) => {
if let Err(err) = std::fs::rename(&temp_file, &self.file_path) {
log::debug!("Failed to rename code cache: {}", err);
match deserialize(&file_path, &cache_key) {
Ok(data) => {
log::debug!("Loaded {} code cache entries", data.len());
Self {
strategy: CodeCacheStrategy::SubsequentRun(
SubsequentRunCodeCacheStrategy {
is_finished: AtomicFlag::lowered(),
data: Mutex::new(data),
},
),
}
}
Err(err) => {
let _ = std::fs::remove_file(&temp_file);
log::debug!("Failed to serialize code cache: {}", err);
log::debug!("Failed to deserialize code cache: {:#}", err);
Self {
strategy: CodeCacheStrategy::FirstRun(FirstRunCodeCacheStrategy {
cache_key,
file_path,
is_finished: AtomicFlag::lowered(),
data: Mutex::new(FirstRunCodeCacheData {
cache: HashMap::new(),
add_count: 0,
}),
}),
}
}
}
}
Expand All @@ -111,60 +74,142 @@ impl CodeCache for DenoCompileCodeCache {
fn get_sync(
&self,
specifier: &ModuleSpecifier,
code_cache_type: CodeCacheType,
_code_cache_type: CodeCacheType,
source_hash: u64,
) -> Option<Vec<u8>> {
if self.finished.is_raised() {
return None;
}
let mut data = self.data.lock();
match data.take_from_cache(specifier, source_hash) {
Some(data) => Some(data),
None => {
data.add_count += 1;
match &self.strategy {
CodeCacheStrategy::FirstRun(strategy) => {
if !strategy.is_finished.is_raised() {
strategy.data.lock().add_count += 1;
}
None
}
CodeCacheStrategy::SubsequentRun(strategy) => {
if strategy.is_finished.is_raised() {
return None;
}
strategy.take_from_cache(specifier, source_hash)
}
}
}

fn set_sync(
&self,
specifier: ModuleSpecifier,
code_cache_type: CodeCacheType,
_code_cache_type: CodeCacheType,
source_hash: u64,
bytes: &[u8],
) {
if self.finished.is_raised() {
return;
}
let data_to_serialize = {
let mut data = self.data.lock();
data.cache.insert(
specifier.to_string(),
DenoCompileCodeCacheEntry {
source_hash,
data: bytes.to_vec(),
},
);
data.modified = true;
if data.add_count != 0 {
data.add_count -= 1;
match &self.strategy {
CodeCacheStrategy::FirstRun(strategy) => {
if strategy.is_finished.is_raised() {
return;
}

let data_to_serialize = {
let mut data = strategy.data.lock();
data.cache.insert(
specifier.to_string(),
DenoCompileCodeCacheEntry {
source_hash,
data: bytes.to_vec(),
},
);
if data.add_count != 0 {
data.add_count -= 1;
}
if data.add_count == 0 {
// don't allow using the cache anymore
strategy.is_finished.raise();
if data.cache.is_empty() {
None
} else {
Some(std::mem::take(&mut data.cache))
}
} else {
None
}
};
if let Some(cache_data) = &data_to_serialize {
strategy.write_cache_data(&cache_data);
}
}
if data.add_count == 0 {
// don't allow using the cache anymore
self.finished.raise();
data.take_cache_data()
} else {
None
CodeCacheStrategy::SubsequentRun(_) => {
// do nothing
}
};
if let Some(cache_data) = &data_to_serialize {
self.write_cache_data(&cache_data);
}
}
}

impl CliCodeCache for DenoCompileCodeCache {
fn enabled(&self) -> bool {
!self.finished.is_raised()
match &self.strategy {
CodeCacheStrategy::FirstRun(strategy) => {
!strategy.is_finished.is_raised()
}
CodeCacheStrategy::SubsequentRun(strategy) => {
!strategy.is_finished.is_raised()
}
}
}

fn as_code_cache(self: Arc<Self>) -> Arc<dyn CodeCache> {
self
}
}

struct FirstRunCodeCacheData {
cache: HashMap<String, DenoCompileCodeCacheEntry>,
add_count: usize,
}

struct FirstRunCodeCacheStrategy {
cache_key: String,
file_path: PathBuf,
is_finished: AtomicFlag,
data: Mutex<FirstRunCodeCacheData>,
}

impl FirstRunCodeCacheStrategy {
fn write_cache_data(
&self,
cache_data: &HashMap<String, DenoCompileCodeCacheEntry>,
) {
let count = cache_data.len();
let temp_file = get_atomic_file_path(&self.file_path);
match serialize(&temp_file, &self.cache_key, cache_data) {
Ok(()) => {
if let Err(err) = std::fs::rename(&temp_file, &self.file_path) {
log::debug!("Failed to rename code cache: {}", err);
} else {
log::debug!("Serialized {} code cache entries", count);
}
}
Err(err) => {
let _ = std::fs::remove_file(&temp_file);
log::debug!("Failed to serialize code cache: {}", err);
}
}
}
}

struct SubsequentRunCodeCacheStrategy {
is_finished: AtomicFlag,
data: Mutex<HashMap<String, DenoCompileCodeCacheEntry>>,
}

impl SubsequentRunCodeCacheStrategy {
fn take_from_cache(
&self,
specifier: &ModuleSpecifier,
source_hash: u64,
) -> Option<Vec<u8>> {
let mut data = self.data.lock();
let entry = data.remove(specifier.as_str())?;
if entry.source_hash != source_hash {
return None;
}
Some(entry.data)
}
}

Expand Down
3 changes: 2 additions & 1 deletion cli/standalone/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ use crate::resolver::NpmModuleLoader;
use crate::util::progress_bar::ProgressBar;
use crate::util::progress_bar::ProgressBarStyle;
use crate::util::v8::construct_v8_flags;
use crate::worker::CliCodeCache;
use crate::worker::CliMainWorkerFactory;
use crate::worker::CliMainWorkerOptions;
use crate::worker::ModuleLoaderAndSourceMapGetter;
Expand All @@ -100,7 +101,7 @@ struct SharedModuleLoaderState {
workspace_resolver: WorkspaceResolver,
node_resolver: Arc<CliNodeResolver>,
npm_module_loader: Arc<NpmModuleLoader>,
code_cache: Arc<dyn deno_runtime::code_cache::CodeCache>,
code_cache: Arc<dyn CliCodeCache>,
}

impl SharedModuleLoaderState {
Expand Down
15 changes: 12 additions & 3 deletions cli/worker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,15 @@ pub trait HmrRunner: Send + Sync {
async fn run(&mut self) -> Result<(), AnyError>;
}

pub trait CliCodeCache: code_cache::CodeCache {
/// Gets if the code cache is still enabled.
fn enabled(&self) -> bool {
true
}

fn as_code_cache(self: Arc<Self>) -> Arc<dyn code_cache::CodeCache>;
}

#[async_trait::async_trait(?Send)]
pub trait CoverageCollector: Send + Sync {
async fn start_collecting(&mut self) -> Result<(), AnyError>;
Expand Down Expand Up @@ -129,7 +138,7 @@ struct SharedWorkerState {
blob_store: Arc<BlobStore>,
broadcast_channel: InMemoryBroadcastChannel,
cjs_resolution_store: Arc<CjsResolutionStore>,
code_cache: Option<Arc<dyn code_cache::CodeCache>>,
code_cache: Option<Arc<dyn CliCodeCache>>,
compiled_wasm_module_store: CompiledWasmModuleStore,
feature_checker: Arc<FeatureChecker>,
fs: Arc<dyn deno_fs::FileSystem>,
Expand Down Expand Up @@ -427,7 +436,7 @@ impl CliMainWorkerFactory {
pub fn new(
blob_store: Arc<BlobStore>,
cjs_resolution_store: Arc<CjsResolutionStore>,
code_cache: Option<Arc<dyn code_cache::CodeCache>>,
code_cache: Option<Arc<dyn CliCodeCache>>,
feature_checker: Arc<FeatureChecker>,
fs: Arc<dyn deno_fs::FileSystem>,
maybe_file_watcher_communicator: Option<Arc<WatcherCommunicator>>,
Expand Down Expand Up @@ -607,7 +616,7 @@ impl CliMainWorkerFactory {
),
feature_checker,
permissions,
v8_code_cache: shared.code_cache.clone(),
v8_code_cache: shared.code_cache.clone().map(|c| c.as_code_cache()),
};
let options = WorkerOptions {
bootstrap: BootstrapOptions {
Expand Down
Loading