Skip wasm bodies on serialized module hits
This commit is contained in:
+180
-13
@@ -109,6 +109,7 @@ struct WasmUnitModuleLoadProfile
|
||||
bool serialized_cache_hit = false;
|
||||
u64 lookup_us = 0;
|
||||
u64 read_us = 0;
|
||||
u64 read_bytes = 0;
|
||||
u64 parse_us = 0;
|
||||
u64 compile_us = 0;
|
||||
u64 classify_us = 0;
|
||||
@@ -158,6 +159,7 @@ struct WasmRequestProfile
|
||||
u64 unit_module_compile_count = 0;
|
||||
u64 unit_module_lookup_total_us = 0;
|
||||
u64 unit_module_read_total_us = 0;
|
||||
u64 unit_module_read_bytes = 0;
|
||||
u64 unit_module_parse_total_us = 0;
|
||||
u64 unit_module_compile_total_us = 0;
|
||||
u64 unit_module_classify_total_us = 0;
|
||||
@@ -671,6 +673,142 @@ static bool wasm_read_file(const String& path, std::vector<u8>& out)
|
||||
return((bool)in);
|
||||
}
|
||||
|
||||
static bool wasm_pread_all(int fd, u64 offset, u8* out, size_t size, u64& bytes_read)
|
||||
{
|
||||
size_t done = 0;
|
||||
while(done < size)
|
||||
{
|
||||
ssize_t n = pread(fd, out + done, size - done, (off_t)(offset + done));
|
||||
if(n < 0 && errno == EINTR)
|
||||
continue;
|
||||
if(n <= 0)
|
||||
return(false);
|
||||
done += (size_t)n;
|
||||
bytes_read += (u64)n;
|
||||
}
|
||||
return(true);
|
||||
}
|
||||
|
||||
static bool wasm_read_uleb_fd(int fd, u64& pos, u64 end, u64& out, u64& bytes_read)
|
||||
{
|
||||
out = 0;
|
||||
u32 shift = 0;
|
||||
while(pos < end && shift < 64)
|
||||
{
|
||||
u8 byte = 0;
|
||||
if(!wasm_pread_all(fd, pos++, &byte, 1, bytes_read))
|
||||
return(false);
|
||||
out |= ((u64)(byte & 0x7f)) << shift;
|
||||
if((byte & 0x80) == 0)
|
||||
return(true);
|
||||
shift += 7;
|
||||
}
|
||||
return(false);
|
||||
}
|
||||
|
||||
static void wasm_write_uleb(std::vector<u8>& out, u64 value)
|
||||
{
|
||||
do
|
||||
{
|
||||
u8 byte = (u8)(value & 0x7f);
|
||||
value >>= 7;
|
||||
out.push_back(value ? byte | 0x80 : byte);
|
||||
}
|
||||
while(value);
|
||||
}
|
||||
|
||||
static bool wasm_read_metadata_file(const String& path, std::vector<u8>& metadata, String& error, u64& bytes_read,
|
||||
u64 expected_modified_ns, u64 expected_changed_ns, u64 expected_size)
|
||||
{
|
||||
bytes_read = 0;
|
||||
int fd = open(path.c_str(), O_RDONLY | O_CLOEXEC);
|
||||
if(fd < 0)
|
||||
return(false);
|
||||
struct stat st;
|
||||
if(fstat(fd, &st) != 0 || st.st_size < 8)
|
||||
{
|
||||
close(fd);
|
||||
error = "not a wasm module";
|
||||
return(false);
|
||||
}
|
||||
u64 modified_ns = (u64)st.st_mtim.tv_sec * 1000000000ull + (u64)st.st_mtim.tv_nsec;
|
||||
u64 changed_ns = (u64)st.st_ctim.tv_sec * 1000000000ull + (u64)st.st_ctim.tv_nsec;
|
||||
if(modified_ns != expected_modified_ns || changed_ns != expected_changed_ns || (u64)st.st_size != expected_size)
|
||||
{
|
||||
close(fd);
|
||||
error = "wasm artifact changed while loading metadata";
|
||||
return(false);
|
||||
}
|
||||
u64 file_size = (u64)st.st_size;
|
||||
u8 header[8];
|
||||
if(!wasm_pread_all(fd, 0, header, sizeof(header), bytes_read) || memcmp(header, "\0asm\1\0\0\0", sizeof(header)) != 0)
|
||||
{
|
||||
close(fd);
|
||||
error = "not a supported wasm module";
|
||||
return(false);
|
||||
}
|
||||
metadata.assign(header, header + sizeof(header));
|
||||
u64 pos = sizeof(header);
|
||||
while(pos < file_size)
|
||||
{
|
||||
u8 section_id = 0;
|
||||
if(!wasm_pread_all(fd, pos++, §ion_id, 1, bytes_read))
|
||||
{
|
||||
error = "malformed wasm section header";
|
||||
break;
|
||||
}
|
||||
u64 section_size = 0;
|
||||
if(!wasm_read_uleb_fd(fd, pos, file_size, section_size, bytes_read) || section_size > file_size - pos)
|
||||
{
|
||||
error = "malformed wasm section header";
|
||||
break;
|
||||
}
|
||||
u64 section_end = pos + section_size;
|
||||
if(section_id == 0)
|
||||
{
|
||||
u64 cursor = pos;
|
||||
u64 name_len = 0;
|
||||
if(!wasm_read_uleb_fd(fd, cursor, section_end, name_len, bytes_read) || name_len > section_end - cursor)
|
||||
{
|
||||
error = "malformed custom section name";
|
||||
break;
|
||||
}
|
||||
String name;
|
||||
if(name_len <= 64)
|
||||
{
|
||||
name.resize((size_t)name_len);
|
||||
if(name_len && !wasm_pread_all(fd, cursor, (u8*)&name[0], (size_t)name_len, bytes_read))
|
||||
{
|
||||
error = "malformed custom section name";
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(name == "dylink.0" || name == "uce.abi")
|
||||
{
|
||||
if(section_size > 1024 * 1024)
|
||||
{
|
||||
error = "oversized wasm metadata section";
|
||||
break;
|
||||
}
|
||||
std::vector<u8> section((size_t)section_size);
|
||||
if(section_size && !wasm_pread_all(fd, pos, section.data(), section.size(), bytes_read))
|
||||
{
|
||||
error = "cannot read wasm metadata section";
|
||||
break;
|
||||
}
|
||||
metadata.push_back(0);
|
||||
wasm_write_uleb(metadata, section_size);
|
||||
metadata.insert(metadata.end(), section.begin(), section.end());
|
||||
}
|
||||
}
|
||||
pos = section_end;
|
||||
}
|
||||
close(fd);
|
||||
if(error != "")
|
||||
return(false);
|
||||
return(true);
|
||||
}
|
||||
|
||||
// ---- worker (per process): engine + compiled module caches ----------------
|
||||
|
||||
class WasmWorker
|
||||
@@ -797,13 +935,25 @@ public:
|
||||
unit->modified_ns = modified_ns;
|
||||
unit->changed_ns = changed_ns;
|
||||
unit->size = (u64)st.st_size;
|
||||
String unit_cached_path = cached_wasm_path(wasm_path);
|
||||
auto compile_start = std::chrono::steady_clock::now();
|
||||
auto compiled_or_cached = load_current_serialized_module(engine, unit_cached_path, wasm_path);
|
||||
profile.compile_us = (u64)std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
std::chrono::steady_clock::now() - compile_start).count();
|
||||
profile.serialized_cache_hit = compiled_or_cached.has_value();
|
||||
std::vector<u8> bytes;
|
||||
auto read_start = std::chrono::steady_clock::now();
|
||||
if(!wasm_read_file(wasm_path, bytes))
|
||||
bool read_ok = profile.serialized_cache_hit
|
||||
? wasm_read_metadata_file(wasm_path, bytes, error, profile.read_bytes, modified_ns, changed_ns, unit->size)
|
||||
: wasm_read_file(wasm_path, bytes);
|
||||
if(!profile.serialized_cache_hit)
|
||||
profile.read_bytes = bytes.size();
|
||||
if(!read_ok)
|
||||
{
|
||||
profile.read_us = (u64)std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
std::chrono::steady_clock::now() - read_start).count();
|
||||
error = "cannot read " + wasm_path;
|
||||
if(error == "")
|
||||
error = "cannot read " + wasm_path;
|
||||
return(nullptr);
|
||||
}
|
||||
profile.read_us = (u64)std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
@@ -828,12 +978,14 @@ public:
|
||||
error = wasm_path + ": missing uce.abi stamp";
|
||||
return(nullptr);
|
||||
}
|
||||
String unit_cached_path = cached_wasm_path(wasm_path);
|
||||
String compile_error;
|
||||
auto compile_start = std::chrono::steady_clock::now();
|
||||
auto compiled_or_cached = load_or_compile_cached_module(engine, unit_cached_path, wasm_path, bytes, compile_error, profile.serialized_cache_hit);
|
||||
profile.compile_us = (u64)std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
std::chrono::steady_clock::now() - compile_start).count();
|
||||
if(!compiled_or_cached)
|
||||
{
|
||||
compile_start = std::chrono::steady_clock::now();
|
||||
compiled_or_cached = compile_and_cache_module(engine, unit_cached_path, bytes, compile_error);
|
||||
profile.compile_us += (u64)std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
std::chrono::steady_clock::now() - compile_start).count();
|
||||
}
|
||||
if(!compiled_or_cached)
|
||||
{
|
||||
error = wasm_path + ": compile failed: " + compile_error;
|
||||
@@ -888,10 +1040,8 @@ private:
|
||||
return(wasm_path + ".cwasm");
|
||||
}
|
||||
|
||||
static std::optional<wasmtime::Module> load_or_compile_cached_module(wasmtime::Engine& engine, const String& cached_path, const String& wasm_path,
|
||||
std::vector<u8>& bytes, String& compile_error, bool& serialized_cache_hit)
|
||||
static std::optional<wasmtime::Module> load_current_serialized_module(wasmtime::Engine& engine, const String& cached_path, const String& wasm_path)
|
||||
{
|
||||
serialized_cache_hit = false;
|
||||
struct stat wasm_stat;
|
||||
struct stat cached_stat;
|
||||
if(stat(cached_path.c_str(), &cached_stat) == 0 && stat(wasm_path.c_str(), &wasm_stat) == 0)
|
||||
@@ -901,13 +1051,15 @@ private:
|
||||
{
|
||||
auto deserialized = wasmtime::Module::deserialize_file(engine, cached_path);
|
||||
if(deserialized)
|
||||
{
|
||||
serialized_cache_hit = true;
|
||||
return(deserialized.ok());
|
||||
}
|
||||
}
|
||||
}
|
||||
return(std::nullopt);
|
||||
}
|
||||
|
||||
static std::optional<wasmtime::Module> compile_and_cache_module(wasmtime::Engine& engine, const String& cached_path,
|
||||
std::vector<u8>& bytes, String& compile_error)
|
||||
{
|
||||
auto compiled = wasmtime::Module::compile(engine, bytes);
|
||||
if(!compiled)
|
||||
{
|
||||
@@ -940,6 +1092,19 @@ private:
|
||||
return(result);
|
||||
}
|
||||
|
||||
static std::optional<wasmtime::Module> load_or_compile_cached_module(wasmtime::Engine& engine, const String& cached_path, const String& wasm_path,
|
||||
std::vector<u8>& bytes, String& compile_error, bool& serialized_cache_hit)
|
||||
{
|
||||
serialized_cache_hit = false;
|
||||
auto cached = load_current_serialized_module(engine, cached_path, wasm_path);
|
||||
if(cached)
|
||||
{
|
||||
serialized_cache_hit = true;
|
||||
return(cached);
|
||||
}
|
||||
return(compile_and_cache_module(engine, cached_path, bytes, compile_error));
|
||||
}
|
||||
|
||||
static wasmtime::Engine make_engine()
|
||||
{
|
||||
wasmtime::Config config;
|
||||
@@ -1397,6 +1562,7 @@ private:
|
||||
unit_module_compile_count += !module_profile.cache_hit && !module_profile.serialized_cache_hit ? 1 : 0;
|
||||
unit_module_lookup_total_us += module_profile.lookup_us;
|
||||
unit_module_read_total_us += module_profile.read_us;
|
||||
unit_module_read_bytes += module_profile.read_bytes;
|
||||
unit_module_parse_total_us += module_profile.parse_us;
|
||||
unit_module_compile_total_us += module_profile.compile_us;
|
||||
unit_module_classify_total_us += module_profile.classify_us;
|
||||
@@ -2042,6 +2208,7 @@ private:
|
||||
response["unit_module_compile_count"] = (f64)self->unit_module_compile_count;
|
||||
response["unit_module_lookup_us"] = (f64)self->unit_module_lookup_total_us;
|
||||
response["unit_module_read_us"] = (f64)self->unit_module_read_total_us;
|
||||
response["unit_module_read_bytes"] = (f64)self->unit_module_read_bytes;
|
||||
response["unit_module_parse_us"] = (f64)self->unit_module_parse_total_us;
|
||||
response["unit_module_compile_us"] = (f64)self->unit_module_compile_total_us;
|
||||
response["unit_module_classify_us"] = (f64)self->unit_module_classify_total_us;
|
||||
|
||||
Reference in New Issue
Block a user