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:
Josh Matthews 2025-04-16 09:11:51 -04:00 committed by GitHub
parent f16f625c9b
commit afe98e9e1e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 49 additions and 7 deletions

View file

@ -2,8 +2,10 @@
* 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/. */
use std::collections::HashMap;
use std::cell::{LazyCell, RefCell};
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::{mem, thread};
@ -11,6 +13,8 @@ use compositing_traits::{CrossProcessCompositorApi, SerializableImageData};
use imsz::imsz_from_reader;
use ipc_channel::ipc::IpcSharedMemory;
use log::{debug, warn};
use malloc_size_of::{MallocSizeOf as MallocSizeOfTrait, MallocSizeOfOps};
use malloc_size_of_derive::MallocSizeOf;
use net_traits::image_cache::{
ImageCache, ImageCacheResult, ImageOrMetadataAvailable, ImageResponder, ImageResponse,
PendingImageId, UsePlaceholder,
@ -18,6 +22,8 @@ use net_traits::image_cache::{
use net_traits::request::CorsSettings;
use net_traits::{FetchMetadata, FetchResponseMsg, FilteredMetadata, NetworkError};
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_url::{ImmutableOrigin, ServoUrl};
use webrender_api::units::DeviceIntSize;
@ -98,6 +104,7 @@ type ImageKey = (ServoUrl, ImmutableOrigin, Option<CorsSettings>);
// Represents all the currently pending loads/decodings. For
// performance reasons, loads are indexed by a dedicated load key.
#[derive(MallocSizeOf)]
struct AllPendingLoads {
// The loads, indexed by a load key. Used during most operations,
// for performance reasons.
@ -180,6 +187,7 @@ enum CacheResult<'a> {
/// Images that fail to load (due to network or decode
/// failure) are still stored here, so that they aren't
/// fetched again.
#[derive(MallocSizeOf)]
struct CompletedLoad {
image_response: ImageResponse,
id: PendingImageId,
@ -197,9 +205,10 @@ struct DecoderMsg {
image: Option<Image>,
}
#[derive(MallocSizeOf)]
enum ImageBytes {
InProgress(Vec<u8>),
Complete(Arc<Vec<u8>>),
Complete(#[conditional_malloc_size_of] Arc<Vec<u8>>),
}
impl ImageBytes {
@ -234,6 +243,7 @@ impl ImageBytes {
// A key used to communicate during loading.
type LoadKey = PendingImageId;
#[derive(MallocSizeOf)]
struct LoadKeyGenerator {
counter: u64,
}
@ -257,6 +267,7 @@ enum LoadResult {
/// Represents an image that is either being loaded
/// by the resource thread, or decoded by a worker thread.
#[derive(MallocSizeOf)]
struct PendingLoad {
/// The bytes loaded so far. Reset to an empty vector once loading
/// is complete and the buffer has been transmitted to the decoder.
@ -315,6 +326,7 @@ impl PendingLoad {
// ======================================================================
// Image cache implementation.
// ======================================================================
#[derive(MallocSizeOf)]
struct ImageCacheStore {
// Images that are loading over network, or decoding.
pending_loads: AllPendingLoads,
@ -323,12 +335,14 @@ struct ImageCacheStore {
completed_loads: HashMap<ImageKey, CompletedLoad>,
// The placeholder image used when an image fails to load
#[conditional_malloc_size_of]
placeholder_image: Arc<Image>,
// The URL used for the placeholder image
placeholder_url: ServoUrl,
// Cross-process compositor API instance.
#[ignore_malloc_size_of = "Channel from another crate"]
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(
&self,
url: ServoUrl,
@ -641,3 +671,7 @@ impl ImageCacheImpl {
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()))
});