Skip to content

Commit

Permalink
Fix vt performance on web
Browse files Browse the repository at this point in the history
Instead of directly convert web worker requests and responses to
`JsValue` we serialize them with bincode instead and transfer to and
from web workers using `ArrayBuffer`s by `serde_bytes`. This improves
performance of the transfer back and forth by a factor of 10.
  • Loading branch information
Maximkaaa committed Feb 1, 2025
1 parent 609452e commit 91e4be9
Show file tree
Hide file tree
Showing 7 changed files with 98 additions and 26 deletions.
6 changes: 5 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ exclude = [
"wasm_examples/with_egui",
]

[profile.release]
debug = true

[workspace.package]
authors = ["Maxim Gritsenko <[email protected]>"]
categories = ["science::geo"]
Expand All @@ -40,7 +43,8 @@ approx = "0.5"
assert_matches = "1.5"
async-trait = "0.1"
base64 = "0.21"
bincode = "1.3"
bincode = "2.0.0-rc.3"
bitcode = "0.6"
bytemuck = "1.14"
bytes = "1.4"
cfg-if = "1"
Expand Down
4 changes: 2 additions & 2 deletions galileo/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ winit = { workspace = true, default-features = true, features = ["rwh_06"], opti
anyhow = { workspace = true }
approx = { workspace = true }
assert_matches = { workspace = true }
bincode = { workspace = true }
bincode = { workspace = true, features = ["serde"] }
csv = { workspace = true }
eframe = { workspace = true }
egui = { workspace = true }
Expand Down Expand Up @@ -88,7 +88,7 @@ wasm-bindgen-derive = { workspace = true }
js-sys = { workspace = true }
serde = { workspace = true, features = ["std", "derive"] }
serde_bytes = { workspace = true }
bincode = { workspace = true }
bincode = { workspace = true, features = ["serde"] }
serde-wasm-bindgen = { workspace = true }
maybe-sync = { workspace = true, features = [] }
getrandom = { workspace = true, features = ["js"] }
Expand Down
7 changes: 6 additions & 1 deletion galileo/examples/feature_layers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,12 @@ fn create_map(countries_layer: impl Layer + 'static) -> Map {
}

fn load_countries() -> Vec<Country> {
bincode::deserialize(include_bytes!("data/countries.data")).expect("invalid countries data")
bincode::serde::decode_from_slice(
include_bytes!("data/countries.data"),
bincode::config::legacy(),
)
.expect("invalid countries data")
.0
}

fn load_cities() -> Vec<City> {
Expand Down
8 changes: 6 additions & 2 deletions galileo/examples/lambert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,12 @@ pub(crate) fn run() {
}

fn load_countries() -> Vec<Country> {
bincode::deserialize(include_bytes!("data/countries_simpl.data"))
.expect("invalid countries data")
bincode::serde::decode_from_slice(
include_bytes!("data/countries_simpl.data"),
bincode::config::legacy(),
)
.expect("invalid countries data")
.0
}

fn create_mouse_handler(
Expand Down
19 changes: 14 additions & 5 deletions galileo/src/layer/vector_tile_layer/style.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ pub struct StyleRule {
#[serde(default)]
pub properties: HashMap<String, String>,
/// Symbol to draw a feature with.
#[serde(default, skip_serializing_if = "VectorTileSymbol::is_none")]
#[serde(default)]
pub symbol: VectorTileSymbol,
}

Expand Down Expand Up @@ -101,10 +101,6 @@ impl Default for VectorTileSymbol {
}

impl VectorTileSymbol {
pub(crate) fn is_none(&self) -> bool {
matches!(self, Self::None)
}

pub(crate) fn line(&self) -> Option<&VectorTileLineSymbol> {
match self {
Self::Line(symbol) => Some(symbol),
Expand Down Expand Up @@ -211,4 +207,17 @@ mod tests {
assert!(value.as_object().unwrap().get("point").is_some());
assert!(value.as_object().unwrap().get("polygon").is_none());
}

#[test]
fn serialize_with_bincode() {
let rule = StyleRule {
layer_name: None,
properties: HashMap::new(),
symbol: VectorTileSymbol::None,
};

let serialized = bincode::serde::encode_to_vec(&rule, bincode::config::standard()).unwrap();
let _: (StyleRule, _) =
bincode::serde::decode_from_slice(&serialized, bincode::config::standard()).unwrap();
}
}
79 changes: 65 additions & 14 deletions galileo/src/platform/web/web_workers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,9 @@ impl TryFrom<Result<WebWorkerResponsePayload, WebWorkerError>> for RenderBundle
) -> Result<Self, Self::Error> {
match value {
Ok(WebWorkerResponsePayload::ProcessVtTile { result }) => result.map(|bytes| {
let converted: TessellatingRenderBundleBytes = bincode::deserialize(&bytes)
.expect("Failed to deserialize render bundle bytes");
let (converted, _): (TessellatingRenderBundleBytes, _) =
bincode::serde::decode_from_slice(&bytes, bincode::config::standard())
.expect("Failed to deserialize render bundle bytes");
RenderBundle(RenderBundleType::Tessellating(
TessellatingRenderBundle::from_bytes_unchecked(converted),
))
Expand Down Expand Up @@ -167,12 +168,17 @@ impl WebWorkerService {
let (sender, receiver) = oneshot::channel();
let request_id = WebWorkerRequestId::next();
self.add_request(request_id, sender);

let start = web_time::Instant::now();
self.send_request(&WebWorkerRequest {
request_id,
payload,
});

log::debug!("Sent request {request_id} to web worker");
log::debug!(
"Sent request {request_id} to web worker in {} ms",
start.elapsed().as_millis()
);

receiver.await.expect("failed to read ww result channel")
}
Expand All @@ -189,9 +195,12 @@ impl WebWorkerService {

fn send_request(&self, request: &WebWorkerRequest) {
let worker = self.next_worker();
let bytes = bincode::serde::encode_to_vec(request, bincode::config::standard())
.expect("failed to serialize ww request");
let buf = serde_bytes::ByteBuf::from(bytes);
worker
.post_message(
&serde_wasm_bindgen::to_value(request).expect("failed to serialize ww request"),
&serde_wasm_bindgen::to_value(&buf).expect("failed to serialize ww request"),
)
.expect("failed to send a message to a web worker");
}
Expand All @@ -212,7 +221,8 @@ impl WebWorkerService {
let worker_state_clone = worker_state.clone();
let callback: Closure<dyn FnMut(web_sys::MessageEvent)> = Closure::new(
move |event: web_sys::MessageEvent| {
let response: WebWorkerResponse = match serde_wasm_bindgen::from_value(event.data())
let start = web_time::Instant::now();
let bytes: serde_bytes::ByteBuf = match serde_wasm_bindgen::from_value(event.data())
{
Ok(v) => v,
Err(err) => {
Expand All @@ -224,9 +234,22 @@ impl WebWorkerService {
}
};

let response: WebWorkerResponse =
match bincode::serde::decode_from_slice(&bytes, bincode::config::standard()) {
Ok(v) => v.0,
Err(err) => {
log::error!(
"Failed to deserialize message ({:?}) from web worker: {err:?}",
event.data()
);
return;
}
};

log::info!(
"Received response for request {} from a web worker",
response.request_id
"Received response for request {} from a web worker in {} ms",
response.request_id,
start.elapsed().as_millis(),
);

match response.payload {
Expand Down Expand Up @@ -264,6 +287,7 @@ impl WebWorkerService {

mod worker {
use galileo_mvt::MvtTile;
use serde_bytes::ByteBuf;
use wasm_bindgen::prelude::wasm_bindgen;
use wasm_bindgen::{JsCast, JsValue};

Expand Down Expand Up @@ -293,8 +317,11 @@ mod worker {
}

fn send_response(response: WebWorkerResponse) {
let bytes = bincode::serde::encode_to_vec(&response, bincode::config::standard())
.expect("failed to serialize ww response");
let buf = serde_bytes::ByteBuf::from(bytes);
let js_value =
serde_wasm_bindgen::to_value(&response).expect("failed to convert response to JsValue");
serde_wasm_bindgen::to_value(&buf).expect("failed to convert response to JsValue");

js_sys::global()
.dyn_into::<web_sys::DedicatedWorkerGlobalScope>()
Expand All @@ -312,24 +339,48 @@ mod worker {
pub fn process_message(msg: JsValue) -> JsValue {
log::debug!("Web worker received a message");

let request: WebWorkerRequest = serde_wasm_bindgen::from_value(msg)
let start = web_time::Instant::now();
let buf: ByteBuf = serde_wasm_bindgen::from_value(msg)
.expect("failed to decode JsValue into web worker request");
let bytes = buf.into_vec();
let (request, _): (WebWorkerRequest, _) =
bincode::serde::decode_from_slice(&bytes, bincode::config::standard())
.expect("failed to decode bytes into request");

let WebWorkerRequest {
request_id,
payload: request_payload,
} = request;

log::debug!("Web worker processing request {request_id}");
log::debug!(
"Web worker processing request {request_id}. Decoded in {} ms",
start.elapsed().as_millis()
);

let start = web_time::Instant::now();
let payload = process_request(request_payload);
let response = WebWorkerResponse {
request_id,
payload,
};
log::debug!(
"Processed request {request_id} in {} ms",
start.elapsed().as_millis()
);

log::debug!("Web worker processed request {request_id}");
let start = web_time::Instant::now();
let bytes = bincode::serde::encode_to_vec(&response, bincode::config::standard())
.expect("failed to serialize ww response");
let buf = serde_bytes::ByteBuf::from(bytes);
let result =
serde_wasm_bindgen::to_value(&buf).expect("failed to convert response to JsValue");

log::debug!(
"Web worker encoded request {request_id} in {} ms",
start.elapsed().as_millis()
);

serde_wasm_bindgen::to_value(&response).expect("failed to convert response to JsValue")
result
}

fn process_request(request: WebWorkerRequestPayload) -> WebWorkerResponsePayload {
Expand Down Expand Up @@ -357,8 +408,8 @@ mod worker {
let RenderBundle(RenderBundleType::Tessellating(tessellating)) = bundle;

let bytes = tessellating.into_bytes();
let serialized =
bincode::serialize(&bytes).expect("failed to serialize render bundle");
let serialized = bincode::serde::encode_to_vec(&bytes, bincode::config::standard())
.expect("failed to serialize render bundle");
Ok(serialized)
}
Err(_) => Err(TileProcessingError::Rendering),
Expand Down
1 change: 0 additions & 1 deletion justfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,3 @@ default:
web_example NAME:
wasm-pack build web-example --release --target no-modules --target-dir target --features {{NAME}}
cd web-example && python3 -m http.server
# cd web-example && trunk serve --features {{NAME}} --release

0 comments on commit 91e4be9

Please sign in to comment.