mirror of
https://github.com/servo/servo.git
synced 2025-08-04 13:10:20 +01:00
net: Report memory usage for image cache. (#36556)
These changes add a new report for image cache memory usage for each script thread. Testing: Looked at the numbers after browsing various stock photo sites that show galleries of images. Signed-off-by: Josh Matthews <josh@joshmatthews.net>
This commit is contained in:
parent
f16f625c9b
commit
afe98e9e1e
6 changed files with 49 additions and 7 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -4900,6 +4900,7 @@ dependencies = [
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"pixels",
|
"pixels",
|
||||||
|
"profile_traits",
|
||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
"serde",
|
"serde",
|
||||||
"servo_arc",
|
"servo_arc",
|
||||||
|
|
|
@ -2,8 +2,10 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::cell::{LazyCell, RefCell};
|
||||||
use std::collections::hash_map::Entry::{Occupied, Vacant};
|
use std::collections::hash_map::Entry::{Occupied, Vacant};
|
||||||
|
use std::collections::{HashMap, HashSet};
|
||||||
|
use std::ffi::c_void;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::{mem, thread};
|
use std::{mem, thread};
|
||||||
|
|
||||||
|
@ -11,6 +13,8 @@ use compositing_traits::{CrossProcessCompositorApi, SerializableImageData};
|
||||||
use imsz::imsz_from_reader;
|
use imsz::imsz_from_reader;
|
||||||
use ipc_channel::ipc::IpcSharedMemory;
|
use ipc_channel::ipc::IpcSharedMemory;
|
||||||
use log::{debug, warn};
|
use log::{debug, warn};
|
||||||
|
use malloc_size_of::{MallocSizeOf as MallocSizeOfTrait, MallocSizeOfOps};
|
||||||
|
use malloc_size_of_derive::MallocSizeOf;
|
||||||
use net_traits::image_cache::{
|
use net_traits::image_cache::{
|
||||||
ImageCache, ImageCacheResult, ImageOrMetadataAvailable, ImageResponder, ImageResponse,
|
ImageCache, ImageCacheResult, ImageOrMetadataAvailable, ImageResponder, ImageResponse,
|
||||||
PendingImageId, UsePlaceholder,
|
PendingImageId, UsePlaceholder,
|
||||||
|
@ -18,6 +22,8 @@ use net_traits::image_cache::{
|
||||||
use net_traits::request::CorsSettings;
|
use net_traits::request::CorsSettings;
|
||||||
use net_traits::{FetchMetadata, FetchResponseMsg, FilteredMetadata, NetworkError};
|
use net_traits::{FetchMetadata, FetchResponseMsg, FilteredMetadata, NetworkError};
|
||||||
use pixels::{CorsStatus, Image, ImageMetadata, PixelFormat, load_from_memory};
|
use pixels::{CorsStatus, Image, ImageMetadata, PixelFormat, load_from_memory};
|
||||||
|
use profile_traits::mem::{Report, ReportKind};
|
||||||
|
use profile_traits::path;
|
||||||
use servo_config::pref;
|
use servo_config::pref;
|
||||||
use servo_url::{ImmutableOrigin, ServoUrl};
|
use servo_url::{ImmutableOrigin, ServoUrl};
|
||||||
use webrender_api::units::DeviceIntSize;
|
use webrender_api::units::DeviceIntSize;
|
||||||
|
@ -98,6 +104,7 @@ type ImageKey = (ServoUrl, ImmutableOrigin, Option<CorsSettings>);
|
||||||
|
|
||||||
// Represents all the currently pending loads/decodings. For
|
// Represents all the currently pending loads/decodings. For
|
||||||
// performance reasons, loads are indexed by a dedicated load key.
|
// performance reasons, loads are indexed by a dedicated load key.
|
||||||
|
#[derive(MallocSizeOf)]
|
||||||
struct AllPendingLoads {
|
struct AllPendingLoads {
|
||||||
// The loads, indexed by a load key. Used during most operations,
|
// The loads, indexed by a load key. Used during most operations,
|
||||||
// for performance reasons.
|
// for performance reasons.
|
||||||
|
@ -180,6 +187,7 @@ enum CacheResult<'a> {
|
||||||
/// Images that fail to load (due to network or decode
|
/// Images that fail to load (due to network or decode
|
||||||
/// failure) are still stored here, so that they aren't
|
/// failure) are still stored here, so that they aren't
|
||||||
/// fetched again.
|
/// fetched again.
|
||||||
|
#[derive(MallocSizeOf)]
|
||||||
struct CompletedLoad {
|
struct CompletedLoad {
|
||||||
image_response: ImageResponse,
|
image_response: ImageResponse,
|
||||||
id: PendingImageId,
|
id: PendingImageId,
|
||||||
|
@ -197,9 +205,10 @@ struct DecoderMsg {
|
||||||
image: Option<Image>,
|
image: Option<Image>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(MallocSizeOf)]
|
||||||
enum ImageBytes {
|
enum ImageBytes {
|
||||||
InProgress(Vec<u8>),
|
InProgress(Vec<u8>),
|
||||||
Complete(Arc<Vec<u8>>),
|
Complete(#[conditional_malloc_size_of] Arc<Vec<u8>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ImageBytes {
|
impl ImageBytes {
|
||||||
|
@ -234,6 +243,7 @@ impl ImageBytes {
|
||||||
// A key used to communicate during loading.
|
// A key used to communicate during loading.
|
||||||
type LoadKey = PendingImageId;
|
type LoadKey = PendingImageId;
|
||||||
|
|
||||||
|
#[derive(MallocSizeOf)]
|
||||||
struct LoadKeyGenerator {
|
struct LoadKeyGenerator {
|
||||||
counter: u64,
|
counter: u64,
|
||||||
}
|
}
|
||||||
|
@ -257,6 +267,7 @@ enum LoadResult {
|
||||||
|
|
||||||
/// Represents an image that is either being loaded
|
/// Represents an image that is either being loaded
|
||||||
/// by the resource thread, or decoded by a worker thread.
|
/// by the resource thread, or decoded by a worker thread.
|
||||||
|
#[derive(MallocSizeOf)]
|
||||||
struct PendingLoad {
|
struct PendingLoad {
|
||||||
/// The bytes loaded so far. Reset to an empty vector once loading
|
/// The bytes loaded so far. Reset to an empty vector once loading
|
||||||
/// is complete and the buffer has been transmitted to the decoder.
|
/// is complete and the buffer has been transmitted to the decoder.
|
||||||
|
@ -315,6 +326,7 @@ impl PendingLoad {
|
||||||
// ======================================================================
|
// ======================================================================
|
||||||
// Image cache implementation.
|
// Image cache implementation.
|
||||||
// ======================================================================
|
// ======================================================================
|
||||||
|
#[derive(MallocSizeOf)]
|
||||||
struct ImageCacheStore {
|
struct ImageCacheStore {
|
||||||
// Images that are loading over network, or decoding.
|
// Images that are loading over network, or decoding.
|
||||||
pending_loads: AllPendingLoads,
|
pending_loads: AllPendingLoads,
|
||||||
|
@ -323,12 +335,14 @@ struct ImageCacheStore {
|
||||||
completed_loads: HashMap<ImageKey, CompletedLoad>,
|
completed_loads: HashMap<ImageKey, CompletedLoad>,
|
||||||
|
|
||||||
// The placeholder image used when an image fails to load
|
// The placeholder image used when an image fails to load
|
||||||
|
#[conditional_malloc_size_of]
|
||||||
placeholder_image: Arc<Image>,
|
placeholder_image: Arc<Image>,
|
||||||
|
|
||||||
// The URL used for the placeholder image
|
// The URL used for the placeholder image
|
||||||
placeholder_url: ServoUrl,
|
placeholder_url: ServoUrl,
|
||||||
|
|
||||||
// Cross-process compositor API instance.
|
// Cross-process compositor API instance.
|
||||||
|
#[ignore_malloc_size_of = "Channel from another crate"]
|
||||||
compositor_api: CrossProcessCompositorApi,
|
compositor_api: CrossProcessCompositorApi,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -439,6 +453,22 @@ impl ImageCache for ImageCacheImpl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn memory_report(&self, prefix: &str) -> Report {
|
||||||
|
let seen_pointer =
|
||||||
|
move |ptr| SEEN_POINTERS.with(|pointers| !pointers.borrow_mut().insert(ptr));
|
||||||
|
let mut ops = MallocSizeOfOps::new(
|
||||||
|
servo_allocator::usable_size,
|
||||||
|
None,
|
||||||
|
Some(Box::new(seen_pointer)),
|
||||||
|
);
|
||||||
|
let size = self.store.lock().unwrap().size_of(&mut ops);
|
||||||
|
Report {
|
||||||
|
path: path![prefix, "image-cache"],
|
||||||
|
kind: ReportKind::ExplicitSystemHeapSize,
|
||||||
|
size,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn get_image(
|
fn get_image(
|
||||||
&self,
|
&self,
|
||||||
url: ServoUrl,
|
url: ServoUrl,
|
||||||
|
@ -641,3 +671,7 @@ impl ImageCacheImpl {
|
||||||
warn!("Couldn't find cached entry for listener {:?}", id);
|
warn!("Couldn't find cached entry for listener {:?}", id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
thread_local!(static SEEN_POINTERS: LazyCell<RefCell<HashSet<*const c_void>>> = const {
|
||||||
|
LazyCell::new(|| RefCell::new(HashSet::new()))
|
||||||
|
});
|
||||||
|
|
|
@ -2435,11 +2435,14 @@ impl ScriptThread {
|
||||||
let documents = self.documents.borrow();
|
let documents = self.documents.borrow();
|
||||||
let urls = itertools::join(documents.iter().map(|(_, d)| d.url().to_string()), ", ");
|
let urls = itertools::join(documents.iter().map(|(_, d)| d.url().to_string()), ", ");
|
||||||
|
|
||||||
let mut reports = self.get_cx().get_reports(format!("url({})", urls));
|
let prefix = format!("url({urls})");
|
||||||
|
let mut reports = self.get_cx().get_reports(prefix.clone());
|
||||||
for (_, document) in documents.iter() {
|
for (_, document) in documents.iter() {
|
||||||
document.window().layout().collect_reports(&mut reports);
|
document.window().layout().collect_reports(&mut reports);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
reports.push(self.image_cache.memory_report(&prefix));
|
||||||
|
|
||||||
reports_chan.send(ProcessReports::new(reports));
|
reports_chan.send(ProcessReports::new(reports));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,7 @@ mime = { workspace = true }
|
||||||
num-traits = { workspace = true }
|
num-traits = { workspace = true }
|
||||||
percent-encoding = { workspace = true }
|
percent-encoding = { workspace = true }
|
||||||
pixels = { path = "../../pixels" }
|
pixels = { path = "../../pixels" }
|
||||||
|
profile_traits = { path = "../profile" }
|
||||||
rustls-pki-types = { workspace = true }
|
rustls-pki-types = { workspace = true }
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
servo_arc = { workspace = true }
|
servo_arc = { workspace = true }
|
||||||
|
|
|
@ -10,6 +10,7 @@ use ipc_channel::ipc::IpcSender;
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use malloc_size_of_derive::MallocSizeOf;
|
use malloc_size_of_derive::MallocSizeOf;
|
||||||
use pixels::{Image, ImageMetadata};
|
use pixels::{Image, ImageMetadata};
|
||||||
|
use profile_traits::mem::Report;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use servo_url::{ImmutableOrigin, ServoUrl};
|
use servo_url::{ImmutableOrigin, ServoUrl};
|
||||||
|
|
||||||
|
@ -36,7 +37,7 @@ pub enum ImageOrMetadataAvailable {
|
||||||
/// and image, and returned to the specified event loop when the
|
/// and image, and returned to the specified event loop when the
|
||||||
/// image load completes. It is typically used to trigger a reflow
|
/// image load completes. It is typically used to trigger a reflow
|
||||||
/// and/or repaint.
|
/// and/or repaint.
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
|
||||||
pub struct ImageResponder {
|
pub struct ImageResponder {
|
||||||
pipeline_id: PipelineId,
|
pipeline_id: PipelineId,
|
||||||
pub id: PendingImageId,
|
pub id: PendingImageId,
|
||||||
|
@ -73,11 +74,11 @@ impl ImageResponder {
|
||||||
#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
|
#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
|
||||||
pub enum ImageResponse {
|
pub enum ImageResponse {
|
||||||
/// The requested image was loaded.
|
/// The requested image was loaded.
|
||||||
Loaded(#[ignore_malloc_size_of = "Arc"] Arc<Image>, ServoUrl),
|
Loaded(#[conditional_malloc_size_of] Arc<Image>, ServoUrl),
|
||||||
/// The request image metadata was loaded.
|
/// The request image metadata was loaded.
|
||||||
MetadataLoaded(ImageMetadata),
|
MetadataLoaded(ImageMetadata),
|
||||||
/// The requested image failed to load, so a placeholder was loaded instead.
|
/// The requested image failed to load, so a placeholder was loaded instead.
|
||||||
PlaceholderLoaded(#[ignore_malloc_size_of = "Arc"] Arc<Image>, ServoUrl),
|
PlaceholderLoaded(#[conditional_malloc_size_of] Arc<Image>, ServoUrl),
|
||||||
/// Neither the requested image nor the placeholder could be loaded.
|
/// Neither the requested image nor the placeholder could be loaded.
|
||||||
None,
|
None,
|
||||||
}
|
}
|
||||||
|
@ -115,6 +116,8 @@ pub trait ImageCache: Sync + Send {
|
||||||
where
|
where
|
||||||
Self: Sized;
|
Self: Sized;
|
||||||
|
|
||||||
|
fn memory_report(&self, prefix: &str) -> Report;
|
||||||
|
|
||||||
/// Definitively check whether there is a cached, fully loaded image available.
|
/// Definitively check whether there is a cached, fully loaded image available.
|
||||||
fn get_image(
|
fn get_image(
|
||||||
&self,
|
&self,
|
||||||
|
|
|
@ -130,7 +130,7 @@ pub enum Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [CORS settings attribute](https://html.spec.whatwg.org/multipage/#attr-crossorigin-anonymous)
|
/// [CORS settings attribute](https://html.spec.whatwg.org/multipage/#attr-crossorigin-anonymous)
|
||||||
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
|
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
|
||||||
pub enum CorsSettings {
|
pub enum CorsSettings {
|
||||||
Anonymous,
|
Anonymous,
|
||||||
UseCredentials,
|
UseCredentials,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue