Bug 1368837 - media.cache.resource-index controls the MediaResourceIndex cache size - r=cpearce
authorGerald Squelart <gsquelart@mozilla.com>
Tue, 30 May 2017 21:43:28 +1200
changeset 361722 b3dff4b28a95446bdae8c91e15ec1a5b5ab5b6c7
parent 361721 b9d2d284df0d2224049140932cf0e4d9fe2f702d
child 361723 cc5eaeefd44520822edf91a49355d354d6f38ad5
push id31939
push usercbook@mozilla.com
push dateThu, 01 Jun 2017 11:49:28 +0000
treeherdermozilla-central@d96110d76619 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscpearce
bugs1368837
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1368837 - media.cache.resource-index controls the MediaResourceIndex cache size - r=cpearce 8KB by default, otherwise using the next power of two from the given media.cache.resource-index (but staying within 32B-128KB). '0' means we don't want to use caching. MozReview-Commit-ID: 8LmS15Ft2MA
dom/media/MediaPrefs.h
dom/media/MediaResource.cpp
dom/media/MediaResource.h
modules/libpref/init/all.js
--- a/dom/media/MediaPrefs.h
+++ b/dom/media/MediaPrefs.h
@@ -81,16 +81,19 @@ private:
     {
       AssertMainThread();
       PrefAddVarCache(&mValue, aPreference, mValue);
     }
   };
 
   // This is where DECL_MEDIA_PREF for each of the preferences should go.
 
+  // Cache sizes.
+  DECL_MEDIA_PREF("media.cache.resource-index",               MediaResourceIndexCache, uint32_t, 8192);
+
   // AudioSink
   DECL_MEDIA_PREF("accessibility.monoaudio.enable",           MonoAudio, bool, false);
   DECL_MEDIA_PREF("media.resampling.enabled",                 AudioSinkResampling, bool, false);
   DECL_MEDIA_PREF("media.resampling.rate",                    AudioSinkResampleRate, uint32_t, 48000);
 #if defined(XP_WIN) || defined(XP_DARWIN) || defined(MOZ_PULSEAUDIO)
   // libcubeb backend implement .get_preferred_channel_layout
   DECL_MEDIA_PREF("media.forcestereo.enabled",                AudioSinkForceStereo, bool, false);
 #else
--- a/dom/media/MediaResource.cpp
+++ b/dom/media/MediaResource.cpp
@@ -1725,16 +1725,20 @@ MediaResourceIndex::ReadAt(int64_t aOffs
 }
 
 nsresult
 MediaResourceIndex::CachedReadAt(int64_t aOffset,
                                  char* aBuffer,
                                  uint32_t aCount,
                                  uint32_t* aBytes)
 {
+  if (mCacheBlockSize == 0) {
+    return UncachedReadAt(aOffset, aBuffer, aCount, aBytes);
+  }
+
   const int oOffset = int(aOffset);
   const unsigned oCount = unsigned(aCount);
   *aBytes = 0;
 
   if (aCount == 0) {
     printf("**** [%p]ReadAt(%u@%d) - aCount==0 -> NS_OK, 0\n",
            this,
            oCount,
@@ -1825,17 +1829,17 @@ MediaResourceIndex::CachedReadAt(int64_t
              unsigned(aCount),
              int(aOffset));
     }
 
     if (aOffset - 1 >= lastBlockOffset) {
       // We were already reading cached data from the last block, we need more
       // from it -> try to top-up, read what we can, and we'll be done.
       MOZ_ASSERT(aOffset == mCachedOffset + mCachedBytes);
-      MOZ_ASSERT(endOffset <= lastBlockOffset + BLOCK_SIZE);
+      MOZ_ASSERT(endOffset <= lastBlockOffset + mCacheBlockSize);
       return CacheOrReadAt(
         oOffset, oCount, "top-up cache", aOffset, aBuffer, aCount, aBytes);
     }
 
     // We were not in the last block (but we may just have crossed the line now)
     MOZ_ASSERT(aOffset <= lastBlockOffset);
     // Continue below...
   } else if (aOffset >= lastBlockOffset) {
@@ -1897,17 +1901,17 @@ MediaResourceIndex::CachedReadAt(int64_t
       oOffset,
       unsigned(read),
       unsigned(aCount),
       int(aOffset));
   }
 
   // We should just have reached the start of the last block.
   MOZ_ASSERT(aOffset == lastBlockOffset);
-  MOZ_ASSERT(aCount <= BLOCK_SIZE);
+  MOZ_ASSERT(aCount <= mCacheBlockSize);
   // Make sure to invalidate the cache first.
   mCachedBytes = 0;
   return CacheOrReadAt(
     oOffset, oCount, "last block", aOffset, aBuffer, aCount, aBytes);
 }
 
 nsresult
 MediaResourceIndex::CacheOrReadAt(int oOffset,
@@ -1916,33 +1920,34 @@ MediaResourceIndex::CacheOrReadAt(int oO
                                   int64_t aOffset,
                                   char* aBuffer,
                                   uint32_t aCount,
                                   uint32_t* aBytes)
 {
   // We should be here because there is more data to read.
   MOZ_ASSERT(aCount > 0);
   // We should be in the last block, so we shouldn't try to read past it.
-  MOZ_ASSERT(IndexInCache(aOffset) + aCount <= BLOCK_SIZE);
+  MOZ_ASSERT(IndexInCache(aOffset) + aCount <= mCacheBlockSize);
 
   const int64_t length = GetLength();
   // If length is unknown (-1), look at resource-cached data.
   // If length is known and equal or greater than requested, also look at
   // resource-cached data.
   // Otherwise, if length is known but same, or less than(!?), requested, don't
   // attempt to access resource-cached data, as we're not expecting it to ever
   // be greater than the length.
   if (length < 0 || length >= aOffset + aCount) {
     // Is there cached data covering at least the requested range?
     const int64_t cachedDataEnd = mResource->GetCachedDataEnd(aOffset);
     if (cachedDataEnd >= aOffset + aCount) {
       // Try to read as much resource-cached data as can fill our local cache.
       const uint32_t cacheIndex = IndexInCache(aOffset);
-      const uint32_t toRead = uint32_t(
-        std::min(cachedDataEnd - aOffset, int64_t(BLOCK_SIZE - cacheIndex)));
+      const uint32_t toRead =
+        uint32_t(std::min(cachedDataEnd - aOffset,
+                          int64_t(mCacheBlockSize - cacheIndex)));
       MOZ_ASSERT(toRead >= aCount);
       nsresult rv =
         mResource->ReadFromCache(&mCachedBlock[cacheIndex], aOffset, toRead);
       if (NS_SUCCEEDED(rv)) {
         // Success means we have read the full `toRead` amount.
         printf("**** [%p]ReadAt(%u@%d) - %s - ReadFromCache(%u@%d) succeeded\n",
                this,
                oCount,
--- a/dom/media/MediaResource.h
+++ b/dom/media/MediaResource.h
@@ -13,20 +13,22 @@
 #include "nsIStreamingProtocolController.h"
 #include "nsIStreamListener.h"
 #include "nsIChannelEventSink.h"
 #include "nsIInterfaceRequestor.h"
 #include "Intervals.h"
 #include "MediaCache.h"
 #include "MediaContainerType.h"
 #include "MediaData.h"
+#include "MediaPrefs.h"
 #include "MediaResourceCallback.h"
 #include "mozilla/Atomics.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/TimeStamp.h"
+#include "mozilla/UniquePtr.h"
 #include "nsThreadUtils.h"
 #include <algorithm>
 
 // For HTTP seeking, if number of bytes needing to be
 // seeked forward is less than this value then a read is
 // done rather than a byte range request.
 //
 // If we assume a 100Mbit connection, and assume reissuing an HTTP seek causes
@@ -755,18 +757,20 @@ private:
  */
 
 class MediaResourceIndex
 {
 public:
   explicit MediaResourceIndex(MediaResource* aResource)
     : mResource(aResource)
     , mOffset(0)
+    , mCacheBlockSize(SelectCacheSize(MediaPrefs::MediaResourceIndexCache()))
     , mCachedOffset(0)
     , mCachedBytes(0)
+    , mCachedBlock(MakeUnique<char[]>(mCacheBlockSize))
   {}
 
   // Read up to aCount bytes from the stream. The buffer must have
   // enough room for at least aCount bytes. Stores the number of
   // actual bytes read in aBytes (0 on end of file).
   // May read less than aCount bytes if the number of
   // available bytes is less than aCount. Always check *aBytes after
   // read, and call again if necessary.
@@ -859,54 +863,72 @@ private:
   nsresult CacheOrReadAt(int oOffset,
                          unsigned oCount,
                          const char* oContext,
                          int64_t aOffset,
                          char* aBuffer,
                          uint32_t aCount,
                          uint32_t* aBytes);
 
+  // Select the next power of 2 (in range 32B-128KB, or 0 -> no cache)
+  static uint32_t SelectCacheSize(uint32_t aHint)
+  {
+    if (aHint == 0) {
+      return 0;
+    }
+    if (aHint <= 32) {
+      return 32;
+    }
+    if (aHint > 64*1024) {
+      return 128*1024;
+    }
+    // 32-bit next power of 2, from:
+    // http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
+    aHint--;
+    aHint |= aHint >> 1;
+    aHint |= aHint >> 2;
+    aHint |= aHint >> 4;
+    aHint |= aHint >> 8;
+    aHint |= aHint >> 16;
+    aHint++;
+    return aHint;
+  }
+
   // Maps a file offset to a mCachedBlock index.
   uint32_t IndexInCache(int64_t aOffsetInFile) const
   {
-    static_assert((BLOCK_SIZE & (BLOCK_SIZE - 1)) == 0,
-                  "BLOCK_SIZE must be power of 2");
-    static_assert(BLOCK_SIZE <= int64_t(UINT32_MAX),
-                  "BLOCK_SIZE must fit in 32 bits");
-    const uint32_t index = uint32_t(aOffsetInFile & (BLOCK_SIZE - 1));
-    MOZ_ASSERT(index == aOffsetInFile % BLOCK_SIZE);
+    const uint32_t index = uint32_t(aOffsetInFile) & (mCacheBlockSize - 1);
+    MOZ_ASSERT(index == aOffsetInFile % mCacheBlockSize);
     return index;
   }
 
   // Starting file offset of the cache block that contains a given file offset.
   int64_t CacheOffsetContaining(int64_t aOffsetInFile) const
   {
-    static_assert((BLOCK_SIZE & (BLOCK_SIZE - 1)) == 0,
-                  "BLOCK_SIZE must be power of 2");
-    const int64_t offset = aOffsetInFile & ~(BLOCK_SIZE - 1);
+    const int64_t offset = aOffsetInFile & ~(int64_t(mCacheBlockSize) - 1);
     MOZ_ASSERT(offset == aOffsetInFile - IndexInCache(aOffsetInFile));
     return offset;
   }
 
   RefPtr<MediaResource> mResource;
   int64_t mOffset;
 
   // Local cache used by ReadAt().
   // mCachedBlock is valid when mCachedBytes != 0, in which case it contains
   // data of length mCachedBytes, starting at offset `mCachedOffset` in the
   // resource, located at index `IndexInCache(mCachedOffset)` in mCachedBlock.
   //
   // resource: |------------------------------------------------------|
-  //                                          <----------> BLOCK_SIZE
+  //                                          <----------> mCacheBlockSize
   //           <---------------------------------> mCachedOffset
   //                                             <--> mCachedBytes
   // mCachedBlock:                            |..----....|
   //  CacheOffsetContaining(mCachedOffset)    <--> IndexInCache(mCachedOffset)
   //           <------------------------------>
-  static constexpr int64_t BLOCK_SIZE = MediaCacheStream::BLOCK_SIZE;
+  const uint32_t mCacheBlockSize;
   int64_t mCachedOffset;
   uint32_t mCachedBytes;
-  char mCachedBlock[BLOCK_SIZE];
+  UniquePtr<char[]> mCachedBlock;
 };
 
 } // namespace mozilla
 
 #endif
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -307,16 +307,20 @@ pref("media.cache_size", 512000);
 // When a network connection is suspended, don't resume it until the
 // amount of buffered data falls below this threshold (in seconds).
 pref("media.cache_resume_threshold", 30);
 // Stop reading ahead when our buffered data is this many seconds ahead
 // of the current playback position. This limit can stop us from using arbitrary
 // amounts of network bandwidth prefetching huge videos.
 pref("media.cache_readahead_limit", 60);
 
+// Cache size hint (in bytes) for each MediaResourceIndex.
+// 0 -> no cache. Will use next power of 2, clamped to 32B-128KB.
+pref("media.cache.resource-index", 8192);
+
 // We'll throttle the download if the download rate is throttle-factor times
 // the estimated playback rate, AND we satisfy the cache readahead_limit
 // above. The estimated playback rate is time_duration/length_in_bytes.
 // This means we'll only throttle the download if there's no concern that
 // throttling would cause us to stop and buffer.
 pref("media.throttle-factor", 2);
 // By default, we'll throttle media download once we've reached the the
 // readahead_limit if the download is fast. This pref toggles the "and the