Commit c7c00301 authored by Brandon Wylie's avatar Brandon Wylie Committed by Commit Bot

[IC] Performance improvements and bug fixes

A summary:
- A bunch of work was being done on the UI thread. Now it's being
done off-thread.
- Network requests were dispatched all at once, slowing everything
down. Now they're dispatched one at a time so the first thumbnail
is loaded in quickly.
- One weak ptr was being passed across threads, resulting in a crash.
This is fixed now.

Bug: 864634
Change-Id: I8dc3797a66c0ccc048b9d9613333df7450ec50c1
Reviewed-on: https://chromium-review.googlesource.com/c/1306634
Commit-Queue: Brandon Wylie <wylieb@chromium.org>
Reviewed-by: default avatarFilip Gorski <fgorski@chromium.org>
Reviewed-by: default avatarSky Malice <skym@chromium.org>
Cr-Commit-Position: refs/heads/master@{#604474}
parent 0b5ca9b6
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include "base/sha1.h" #include "base/sha1.h"
#include "base/task/post_task.h" #include "base/task/post_task.h"
#include "base/task/task_traits.h" #include "base/task/task_traits.h"
#include "base/task_runner_util.h"
#include "base/threading/sequenced_task_runner_handle.h" #include "base/threading/sequenced_task_runner_handle.h"
#include "base/time/clock.h" #include "base/time/clock.h"
#include "base/time/time.h" #include "base/time/time.h"
...@@ -37,16 +38,17 @@ constexpr int kCacheResizeWhenFull = 48 * 1024 * 1024; // 48mb. ...@@ -37,16 +38,17 @@ constexpr int kCacheResizeWhenFull = 48 * 1024 * 1024; // 48mb.
constexpr int kCacheItemsTimeToLiveDays = 7; constexpr int kCacheItemsTimeToLiveDays = 7;
constexpr int kImageCacheEvictionIntervalHours = 24; constexpr int kImageCacheEvictionIntervalHours = 24;
std::string HashUrlToKey(const std::string& input) {
return base32::Base32Encode(base::SHA1HashString(input));
}
void OnStartupEvictionQueued() {} void OnStartupEvictionQueued() {}
} // namespace } // namespace
namespace image_fetcher { namespace image_fetcher {
// static
std::string ImageCache::HashUrlToKey(const std::string& input) {
return base32::Base32Encode(base::SHA1HashString(input));
}
// static // static
void ImageCache::RegisterProfilePrefs(PrefRegistrySimple* registry) { void ImageCache::RegisterProfilePrefs(PrefRegistrySimple* registry) {
registry->RegisterTimePref(kPrefLastStartupEviction, base::Time()); registry->RegisterTimePref(kPrefLastStartupEviction, base::Time());
...@@ -102,8 +104,7 @@ void ImageCache::QueueOrStartRequest(base::OnceClosure request) { ...@@ -102,8 +104,7 @@ void ImageCache::QueueOrStartRequest(base::OnceClosure request) {
return; return;
} }
// Post task for fairness with tasks that may be queued. std::move(request).Run();
task_runner_->PostTask(FROM_HERE, std::move(request));
} }
void ImageCache::MaybeStartInitialization() { void ImageCache::MaybeStartInitialization() {
...@@ -129,7 +130,7 @@ void ImageCache::OnDependencyInitialized() { ...@@ -129,7 +130,7 @@ void ImageCache::OnDependencyInitialized() {
// Everything is initialized, take care of the queued requests. // Everything is initialized, take care of the queued requests.
for (base::OnceClosure& request : queued_requests_) { for (base::OnceClosure& request : queued_requests_) {
task_runner_->PostTask(FROM_HERE, std::move(request)); std::move(request).Run();
} }
queued_requests_.clear(); queued_requests_.clear();
...@@ -147,7 +148,7 @@ void ImageCache::OnDependencyInitialized() { ...@@ -147,7 +148,7 @@ void ImageCache::OnDependencyInitialized() {
} }
void ImageCache::SaveImageImpl(const std::string& url, std::string image_data) { void ImageCache::SaveImageImpl(const std::string& url, std::string image_data) {
std::string key = HashUrlToKey(url); std::string key = ImageCache::HashUrlToKey(url);
// If the cache is full, evict some stuff. // If the cache is full, evict some stuff.
RunEvictionWhenFull(); RunEvictionWhenFull();
...@@ -160,7 +161,7 @@ void ImageCache::SaveImageImpl(const std::string& url, std::string image_data) { ...@@ -160,7 +161,7 @@ void ImageCache::SaveImageImpl(const std::string& url, std::string image_data) {
void ImageCache::LoadImageImpl(bool read_only, void ImageCache::LoadImageImpl(bool read_only,
const std::string& url, const std::string& url,
ImageDataCallback callback) { ImageDataCallback callback) {
std::string key = HashUrlToKey(url); std::string key = ImageCache::HashUrlToKey(url);
data_store_->LoadImage(key, std::move(callback)); data_store_->LoadImage(key, std::move(callback));
if (!read_only) { if (!read_only) {
...@@ -169,7 +170,7 @@ void ImageCache::LoadImageImpl(bool read_only, ...@@ -169,7 +170,7 @@ void ImageCache::LoadImageImpl(bool read_only,
} }
void ImageCache::DeleteImageImpl(const std::string& url) { void ImageCache::DeleteImageImpl(const std::string& url) {
std::string key = HashUrlToKey(url); std::string key = ImageCache::HashUrlToKey(url);
data_store_->DeleteImage(key); data_store_->DeleteImage(key);
metadata_store_->DeleteImageMetadata(key); metadata_store_->DeleteImageMetadata(key);
......
...@@ -31,6 +31,7 @@ class ImageMetadataStore; ...@@ -31,6 +31,7 @@ class ImageMetadataStore;
// ImageMetadataStore. // ImageMetadataStore.
class ImageCache : public base::RefCounted<ImageCache> { class ImageCache : public base::RefCounted<ImageCache> {
public: public:
static std::string HashUrlToKey(const std::string& input);
static void RegisterProfilePrefs(PrefRegistrySimple* registry); static void RegisterProfilePrefs(PrefRegistrySimple* registry);
ImageCache(std::unique_ptr<ImageDataStore> data_storage, ImageCache(std::unique_ptr<ImageDataStore> data_storage,
......
...@@ -165,6 +165,11 @@ class ImageCacheTest : public testing::Test { ...@@ -165,6 +165,11 @@ class ImageCacheTest : public testing::Test {
DISALLOW_COPY_AND_ASSIGN(ImageCacheTest); DISALLOW_COPY_AND_ASSIGN(ImageCacheTest);
}; };
TEST_F(ImageCacheTest, HashUrlToKeyTest) {
ASSERT_EQ(ImageCache::HashUrlToKey("foo"), ImageCache::HashUrlToKey("foo"));
ASSERT_NE(ImageCache::HashUrlToKey("foo"), ImageCache::HashUrlToKey("bar"));
}
TEST_F(ImageCacheTest, SanityTest) { TEST_F(ImageCacheTest, SanityTest) {
CreateImageCache(); CreateImageCache();
InitializeImageCache(); InitializeImageCache();
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include "base/bind.h" #include "base/bind.h"
#include "base/metrics/histogram_macros.h" #include "base/metrics/histogram_macros.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/timer/elapsed_timer.h" #include "base/timer/elapsed_timer.h"
#include "components/image_fetcher/core/cache/cached_image_fetcher_metrics_reporter.h" #include "components/image_fetcher/core/cache/cached_image_fetcher_metrics_reporter.h"
#include "components/image_fetcher/core/cache/image_cache.h" #include "components/image_fetcher/core/cache/image_cache.h"
...@@ -41,17 +42,14 @@ void ImageCallbackIfPresent(ImageFetcherCallback image_callback, ...@@ -41,17 +42,14 @@ void ImageCallbackIfPresent(ImageFetcherCallback image_callback,
std::move(image_callback).Run(id, image, metadata); std::move(image_callback).Run(id, image, metadata);
} }
bool EncodeSkBitmapToPNG(const SkBitmap& bitmap, std::string EncodeSkBitmapToPNG(SkBitmap bitmap) {
std::vector<unsigned char>* dest) { std::vector<unsigned char> encoded_data;
if (!bitmap.readyToDraw() || bitmap.isNull()) { bool result = gfx::PNGCodec::Encode(
return false;
}
return gfx::PNGCodec::Encode(
static_cast<const unsigned char*>(bitmap.getPixels()), static_cast<const unsigned char*>(bitmap.getPixels()),
gfx::PNGCodec::FORMAT_RGBA, gfx::Size(bitmap.width(), bitmap.height()), gfx::PNGCodec::FORMAT_RGBA, gfx::Size(bitmap.width(), bitmap.height()),
static_cast<int>(bitmap.rowBytes()), /* discard_transparency */ false, static_cast<int>(bitmap.rowBytes()), /* discard_transparency */ false,
std::vector<gfx::PNGCodec::Comment>(), dest); std::vector<gfx::PNGCodec::Comment>(), &encoded_data);
return result ? std::string(encoded_data.begin(), encoded_data.end()) : "";
} }
} // namespace } // namespace
...@@ -76,7 +74,6 @@ void CachedImageFetcher::SetDataUseServiceName( ...@@ -76,7 +74,6 @@ void CachedImageFetcher::SetDataUseServiceName(
} }
void CachedImageFetcher::SetDesiredImageFrameSize(const gfx::Size& size) { void CachedImageFetcher::SetDesiredImageFrameSize(const gfx::Size& size) {
image_fetcher_->SetDesiredImageFrameSize(size);
desired_image_frame_size_ = size; desired_image_frame_size_ = size;
} }
...@@ -99,9 +96,10 @@ void CachedImageFetcher::FetchImageAndData( ...@@ -99,9 +96,10 @@ void CachedImageFetcher::FetchImageAndData(
image_cache_->LoadImage( image_cache_->LoadImage(
read_only_, image_url.spec(), read_only_, image_url.spec(),
base::BindOnce(&CachedImageFetcher::OnImageFetchedFromCache, base::BindOnce(&CachedImageFetcher::OnImageFetchedFromCache,
weak_ptr_factory_.GetWeakPtr(), base::Time::Now(), id, weak_ptr_factory_.GetWeakPtr(), base::Time::Now(),
image_url, std::move(data_callback), desired_image_frame_size_, id, image_url,
std::move(image_callback), traffic_annotation)); std::move(data_callback), std::move(image_callback),
traffic_annotation));
CachedImageFetcherMetricsReporter::ReportEvent( CachedImageFetcherMetricsReporter::ReportEvent(
CachedImageFetcherEvent::kImageRequest); CachedImageFetcherEvent::kImageRequest);
...@@ -109,6 +107,7 @@ void CachedImageFetcher::FetchImageAndData( ...@@ -109,6 +107,7 @@ void CachedImageFetcher::FetchImageAndData(
void CachedImageFetcher::OnImageFetchedFromCache( void CachedImageFetcher::OnImageFetchedFromCache(
base::Time start_time, base::Time start_time,
const gfx::Size& size,
const std::string& id, const std::string& id,
const GURL& image_url, const GURL& image_url,
ImageDataFetcherCallback data_callback, ImageDataFetcherCallback data_callback,
...@@ -117,9 +116,9 @@ void CachedImageFetcher::OnImageFetchedFromCache( ...@@ -117,9 +116,9 @@ void CachedImageFetcher::OnImageFetchedFromCache(
std::string image_data) { std::string image_data) {
if (image_data.empty()) { if (image_data.empty()) {
// Fetching from the DB failed, start a network fetch. // Fetching from the DB failed, start a network fetch.
FetchImageFromNetwork(/* cache_hit */ false, start_time, id, image_url, EnqueueFetchImageFromNetwork(/* cache_hit */ false, start_time, size, id,
std::move(data_callback), std::move(image_callback), image_url, std::move(data_callback),
traffic_annotation); std::move(image_callback), traffic_annotation);
CachedImageFetcherMetricsReporter::ReportEvent( CachedImageFetcherMetricsReporter::ReportEvent(
CachedImageFetcherEvent::kCacheMiss); CachedImageFetcherEvent::kCacheMiss);
...@@ -128,10 +127,11 @@ void CachedImageFetcher::OnImageFetchedFromCache( ...@@ -128,10 +127,11 @@ void CachedImageFetcher::OnImageFetchedFromCache(
RequestMetadata()); RequestMetadata());
// TODO(wylieb): On Android, do this in-process. // TODO(wylieb): On Android, do this in-process.
GetImageDecoder()->DecodeImage( GetImageDecoder()->DecodeImage(
image_data, desired_image_frame_size_, image_data, size,
base::BindRepeating(&CachedImageFetcher::OnImageDecodedFromCache, base::BindRepeating(&CachedImageFetcher::OnImageDecodedFromCache,
weak_ptr_factory_.GetWeakPtr(), start_time, id, weak_ptr_factory_.GetWeakPtr(), start_time, size,
image_url, base::Passed(std::move(data_callback)), id, image_url,
base::Passed(std::move(data_callback)),
base::Passed(std::move(image_callback)), base::Passed(std::move(image_callback)),
traffic_annotation, image_data)); traffic_annotation, image_data));
CachedImageFetcherMetricsReporter::ReportEvent( CachedImageFetcherMetricsReporter::ReportEvent(
...@@ -141,6 +141,7 @@ void CachedImageFetcher::OnImageFetchedFromCache( ...@@ -141,6 +141,7 @@ void CachedImageFetcher::OnImageFetchedFromCache(
void CachedImageFetcher::OnImageDecodedFromCache( void CachedImageFetcher::OnImageDecodedFromCache(
base::Time start_time, base::Time start_time,
const gfx::Size& size,
const std::string& id, const std::string& id,
const GURL& image_url, const GURL& image_url,
ImageDataFetcherCallback data_callback, ImageDataFetcherCallback data_callback,
...@@ -150,9 +151,9 @@ void CachedImageFetcher::OnImageDecodedFromCache( ...@@ -150,9 +151,9 @@ void CachedImageFetcher::OnImageDecodedFromCache(
const gfx::Image& image) { const gfx::Image& image) {
if (image.IsEmpty()) { if (image.IsEmpty()) {
// Upon failure, fetch from the network. // Upon failure, fetch from the network.
FetchImageFromNetwork(/* cache_hit */ true, start_time, id, image_url, EnqueueFetchImageFromNetwork(/* cache_hit */ true, start_time, size, id,
std::move(data_callback), std::move(image_callback), image_url, std::move(data_callback),
traffic_annotation); std::move(image_callback), traffic_annotation);
CachedImageFetcherMetricsReporter::ReportEvent( CachedImageFetcherMetricsReporter::ReportEvent(
CachedImageFetcherEvent::kCacheDecodingError); CachedImageFetcherEvent::kCacheDecodingError);
...@@ -163,9 +164,27 @@ void CachedImageFetcher::OnImageDecodedFromCache( ...@@ -163,9 +164,27 @@ void CachedImageFetcher::OnImageDecodedFromCache(
} }
} }
void CachedImageFetcher::EnqueueFetchImageFromNetwork(
bool cache_hit,
base::Time start_time,
const gfx::Size& size,
const std::string& id,
const GURL& image_url,
ImageDataFetcherCallback data_callback,
ImageFetcherCallback image_callback,
const net::NetworkTrafficAnnotationTag& traffic_annotation) {
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(&CachedImageFetcher::FetchImageFromNetwork,
weak_ptr_factory_.GetWeakPtr(), cache_hit, start_time,
size, id, image_url, std::move(data_callback),
std::move(image_callback), traffic_annotation));
}
void CachedImageFetcher::FetchImageFromNetwork( void CachedImageFetcher::FetchImageFromNetwork(
bool cache_hit, bool cache_hit,
base::Time start_time, base::Time start_time,
const gfx::Size& size,
const std::string& id, const std::string& id,
const GURL& image_url, const GURL& image_url,
ImageDataFetcherCallback data_callback, ImageDataFetcherCallback data_callback,
...@@ -173,6 +192,7 @@ void CachedImageFetcher::FetchImageFromNetwork( ...@@ -173,6 +192,7 @@ void CachedImageFetcher::FetchImageFromNetwork(
const net::NetworkTrafficAnnotationTag& traffic_annotation) { const net::NetworkTrafficAnnotationTag& traffic_annotation) {
// Fetch image data and the image itself. The image data will be stored in // Fetch image data and the image itself. The image data will be stored in
// the image cache, and the image will be returned to the caller. // the image cache, and the image will be returned to the caller.
image_fetcher_->SetDesiredImageFrameSize(size);
image_fetcher_->FetchImageAndData( image_fetcher_->FetchImageAndData(
id, image_url, id, image_url,
base::BindOnce(&CachedImageFetcher::DecodeDataForCaching, base::BindOnce(&CachedImageFetcher::DecodeDataForCaching,
...@@ -221,17 +241,30 @@ void CachedImageFetcher::DecodeDataForCaching( ...@@ -221,17 +241,30 @@ void CachedImageFetcher::DecodeDataForCaching(
DataCallbackIfPresent(std::move(data_callback), image_data, request_metadata); DataCallbackIfPresent(std::move(data_callback), image_data, request_metadata);
GetImageDecoder()->DecodeImage( GetImageDecoder()->DecodeImage(
image_data, /* Decoding for cache shouldn't specify size */ gfx::Size(), image_data, /* Decoding for cache shouldn't specify size */ gfx::Size(),
base::BindRepeating(&CachedImageFetcher::EncodeDataAndCache, base::BindRepeating(&CachedImageFetcher::StartEncodingDataAndCache,
weak_ptr_factory_.GetWeakPtr(), image_url)); weak_ptr_factory_.GetWeakPtr(), image_url));
} }
void CachedImageFetcher::EncodeDataAndCache(const GURL& image_url, void CachedImageFetcher::StartEncodingDataAndCache(const GURL& image_url,
const gfx::Image& image) { const gfx::Image& image) {
std::vector<unsigned char> encoded_data; SkBitmap bitmap = image.IsEmpty() ? SkBitmap() : *image.ToSkBitmap();
// If the image is empty, or there's a problem encoding the image, don't save // If the bitmap is null or otherwise not ready, skip encoding.
// it. if (!bitmap.readyToDraw() || bitmap.isNull()) {
if (image.IsEmpty() || StoreEncodedData(image_url, "");
!EncodeSkBitmapToPNG(*image.ToSkBitmap(), &encoded_data)) { return;
}
// Post a task to another thread to encode the image data downloaded.
base::PostTaskAndReplyWithResult(
FROM_HERE, base::BindOnce(&EncodeSkBitmapToPNG, std::move(bitmap)),
base::BindOnce(&CachedImageFetcher::StoreEncodedData,
weak_ptr_factory_.GetWeakPtr(), image_url));
}
void CachedImageFetcher::StoreEncodedData(const GURL& image_url,
std::string image_data) {
// If the image is empty, delete the image.
if (image_data.empty()) {
CachedImageFetcherMetricsReporter::ReportEvent( CachedImageFetcherMetricsReporter::ReportEvent(
CachedImageFetcherEvent::kTranscodingError); CachedImageFetcherEvent::kTranscodingError);
image_cache_->DeleteImage(image_url.spec()); image_cache_->DeleteImage(image_url.spec());
...@@ -239,8 +272,7 @@ void CachedImageFetcher::EncodeDataAndCache(const GURL& image_url, ...@@ -239,8 +272,7 @@ void CachedImageFetcher::EncodeDataAndCache(const GURL& image_url,
} }
if (!read_only_) { if (!read_only_) {
image_cache_->SaveImage(image_url.spec(), std::string(encoded_data.begin(), image_cache_->SaveImage(image_url.spec(), std::move(image_data));
encoded_data.end()));
} }
} }
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include "base/containers/flat_map.h" #include "base/containers/flat_map.h"
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
#include "base/task/post_task.h"
#include "base/timer/timer.h" #include "base/timer/timer.h"
#include "components/image_fetcher/core/cache/cached_image_fetcher_metrics_reporter.h" #include "components/image_fetcher/core/cache/cached_image_fetcher_metrics_reporter.h"
#include "components/image_fetcher/core/image_decoder.h" #include "components/image_fetcher/core/image_decoder.h"
...@@ -59,6 +60,7 @@ class CachedImageFetcher : public ImageFetcher { ...@@ -59,6 +60,7 @@ class CachedImageFetcher : public ImageFetcher {
// Cache // Cache
void OnImageFetchedFromCache( void OnImageFetchedFromCache(
base::Time start_time, base::Time start_time,
const gfx::Size& size,
const std::string& id, const std::string& id,
const GURL& image_url, const GURL& image_url,
ImageDataFetcherCallback image_data_callback, ImageDataFetcherCallback image_data_callback,
...@@ -67,6 +69,7 @@ class CachedImageFetcher : public ImageFetcher { ...@@ -67,6 +69,7 @@ class CachedImageFetcher : public ImageFetcher {
std::string image_data); std::string image_data);
void OnImageDecodedFromCache( void OnImageDecodedFromCache(
base::Time start_time, base::Time start_time,
const gfx::Size& size,
const std::string& id, const std::string& id,
const GURL& image_url, const GURL& image_url,
ImageDataFetcherCallback image_data_callback, ImageDataFetcherCallback image_data_callback,
...@@ -76,9 +79,19 @@ class CachedImageFetcher : public ImageFetcher { ...@@ -76,9 +79,19 @@ class CachedImageFetcher : public ImageFetcher {
const gfx::Image& image); const gfx::Image& image);
// Network // Network
void EnqueueFetchImageFromNetwork(
bool cache_hit,
base::Time start_time,
const gfx::Size& size,
const std::string& id,
const GURL& image_url,
ImageDataFetcherCallback image_data_callback,
ImageFetcherCallback image_callback,
const net::NetworkTrafficAnnotationTag& traffic_annotation);
void FetchImageFromNetwork( void FetchImageFromNetwork(
bool cache_hit, bool cache_hit,
base::Time start_time, base::Time start_time,
const gfx::Size& size,
const std::string& id, const std::string& id,
const GURL& image_url, const GURL& image_url,
ImageDataFetcherCallback image_data_callback, ImageDataFetcherCallback image_data_callback,
...@@ -95,7 +108,9 @@ class CachedImageFetcher : public ImageFetcher { ...@@ -95,7 +108,9 @@ class CachedImageFetcher : public ImageFetcher {
const GURL& image_url, const GURL& image_url,
const std::string& image_data, const std::string& image_data,
const RequestMetadata& request_metadata); const RequestMetadata& request_metadata);
void EncodeDataAndCache(const GURL& image_url, const gfx::Image& image); void StartEncodingDataAndCache(const GURL& image_url,
const gfx::Image& image);
void StoreEncodedData(const GURL& image_url, std::string image_data);
// Whether the ImageChache is allowed to be modified in any way from requests // Whether the ImageChache is allowed to be modified in any way from requests
// made by this CachedImageFetcher. This includes updating last used times, // made by this CachedImageFetcher. This includes updating last used times,
...@@ -106,7 +121,7 @@ class CachedImageFetcher : public ImageFetcher { ...@@ -106,7 +121,7 @@ class CachedImageFetcher : public ImageFetcher {
scoped_refptr<ImageCache> image_cache_; scoped_refptr<ImageCache> image_cache_;
// When true, read the cache as write-only. Used for when users are incognito. // When true, operations won't affect the longeivity of valid cache items.
bool read_only_; bool read_only_;
gfx::Size desired_image_frame_size_; gfx::Size desired_image_frame_size_;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment