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

Optimize lazy-loading of IMG elements in Image Prioritizer #1261

Merged
merged 9 commits into from
Jun 3, 2024
Prev Previous commit
Next Next commit
Introduce get_all_element_max_intersection_ratios method to cache O(n…
…^3) operation
westonruter committed Jun 3, 2024
commit 58df83934b72da170a22a094f0f8b88984b04953
Original file line number Diff line number Diff line change
@@ -73,28 +73,15 @@ public function __invoke( OD_HTML_Tag_Walker $walker ): bool {
}

// TODO: Also if the element isLCPCandidate it should never by lazy-loaded.
$is_visible = false;
$is_found = false;
foreach ( $this->url_metrics_group_collection as $group ) {
foreach ( $group as $url_metric ) {
foreach ( $url_metric->get_elements() as $element ) {
if ( $xpath === $element['xpath'] ) {
$is_found = true;
if ( $element['intersectionRatio'] > 0 ) {
$is_visible = true;
break 3; // TODO: O(n^3) my!
}
}
}
}
}
$element_max_intersection_ratio = $this->url_metrics_group_collection->get_element_max_intersection_ratio( $xpath );

// If the element was not found, we don't know if it was visible for not, so don't do anything.
if ( ! $is_found ) {
if ( is_null( $element_max_intersection_ratio ) ) {
$walker->set_attribute( 'data-ip-unknown-tag', true ); // Mostly useful for debugging why an IMG isn't optimized.
} else {
// Otherwise, make sure visible elements omit the loading attribute, and hidden elements include loading=lazy.
$loading = (string) $walker->get_attribute( 'loading' );
$is_visible = $element_max_intersection_ratio > 0.0;
$loading = (string) $walker->get_attribute( 'loading' );
if ( $is_visible && 'lazy' === $loading ) {
$walker->set_attribute( 'data-od-removed-loading', $loading );
$walker->remove_attribute( 'loading' );
Original file line number Diff line number Diff line change
@@ -78,7 +78,8 @@ final class OD_URL_Metrics_Group_Collection implements Countable, IteratorAggreg
* is_every_group_populated?: bool,
* is_every_group_complete?: bool,
* get_groups_by_lcp_element?: array<string, OD_URL_Metrics_Group[]>,
* get_common_lcp_element?: ElementData|null
* get_common_lcp_element?: ElementData|null,
* get_all_element_max_intersection_ratios?: array<string, float>
* }
*/
private $result_cache = array();
@@ -370,6 +371,51 @@ public function get_common_lcp_element(): ?array {
return $result;
}

/**
* Gets the max intersection ratios of all elements across all groups and their captured URL metrics.
*
* @return array<string, float> Keys are XPaths and values are the intersection ratios.
*/
public function get_all_element_max_intersection_ratios(): array {
if ( array_key_exists( __FUNCTION__, $this->result_cache ) ) {
return $this->result_cache[ __FUNCTION__ ];
}

$result = ( function () {
$element_max_intersection_ratios = array();

/*
* O(n^3) my! Yes. This is why the result is cached. This being said, the number of groups should be 4 (one
* more than the default number of breakpoints) and the number of URL metrics for each group should be 3
* (the default sample size). Therefore, given the number (n) of visited elements on the page this will only
* end up running n*4*3 times.
*/
foreach ( $this->groups as $group ) {
foreach ( $group as $url_metric ) {
foreach ( $url_metric->get_elements() as $element ) {
$element_max_intersection_ratios[ $element['xpath'] ] = array_key_exists( $element['xpath'], $element_max_intersection_ratios )
? max( $element_max_intersection_ratios[ $element['xpath'] ], $element['intersectionRatio'] )
: $element['intersectionRatio'];
}
}
}
return $element_max_intersection_ratios;
} )();

$this->result_cache[ __FUNCTION__ ] = $result;
return $result;
}

/**
* Gets the max intersection ratio of an element across all groups and their captured URL metrics.
*
* @param string $xpath XPath for the element.
* @return float|null Max intersection ratio of null if tag is unknown (not captured).
*/
public function get_element_max_intersection_ratio( string $xpath ): ?float {
return $this->get_all_element_max_intersection_ratios()[ $xpath ] ?? null;
}

westonruter marked this conversation as resolved.
Show resolved Hide resolved
/**
* Gets URL metrics from all groups flattened into one list.
*