Bug 1190258: P2. Don't use a FallibleTArray as backend for MediaRawData. r=njn
authorJean-Yves Avenard <jyavenard@mozilla.com>
Thu, 06 Aug 2015 11:17:45 +1000
changeset 288168 4562ee818a4c5cd03ab51c3a58c89ac88ea0817b
parent 288167 c32cd64adb3745e4bfb5c44739c2417e1ff845fc
child 288169 01ae068187d8e3dad0015e43e1cd7f46c5398b2b
push id5067
push userraliiev@mozilla.com
push dateMon, 21 Sep 2015 14:04:52 +0000
treeherdermozilla-beta@14221ffe5b2f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnjn
bugs1190258
milestone42.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 1190258: P2. Don't use a FallibleTArray as backend for MediaRawData. r=njn MediaRawData is often kept to store small data that is unlikely to change over the lifetime of the MediaRawData. The use of nsTArray as memory backend comes at a cost of much greater memory allocated than required. Additionally, a MediaRawData was configured to allocate a minimum size of 4kiB. While this made sense for our past use of MediaRawData, the new MSE keeps big arrays of them to keep audio and video samples. Combined with the nsTArray overheads, our actual memory use was 22 times greater than thought. A source buffer allow 100MiB worth of samples, resulting in an actual memory usage of 2.2GiB.
dom/media/MediaData.cpp
dom/media/MediaData.h
--- a/dom/media/MediaData.cpp
+++ b/dom/media/MediaData.cpp
@@ -480,44 +480,40 @@ VideoData::Create(const VideoInfo& aInfo
   return v.forget();
 }
 #endif  // MOZ_OMX_DECODER
 
 // Alignment value - 1. 0 means that data isn't aligned.
 // For 32-bytes aligned, use 31U.
 #define RAW_DATA_ALIGNMENT 31U
 
-#define RAW_DATA_DEFAULT_SIZE 4096
-
 MediaRawData::MediaRawData()
   : MediaData(RAW_DATA, 0)
   , mCrypto(mCryptoInternal)
   , mData(nullptr)
   , mSize(0)
-  , mBuffer(new MediaByteBuffer())
-  , mPadding(0)
+  , mBuffer(nullptr)
+  , mCapacity(0)
 {
-  unused << mBuffer->SetCapacity(RAW_DATA_DEFAULT_SIZE, fallible);
 }
 
 MediaRawData::MediaRawData(const uint8_t* aData, size_t aSize)
   : MediaData(RAW_DATA, 0)
   , mCrypto(mCryptoInternal)
   , mData(nullptr)
   , mSize(0)
-  , mBuffer(new MediaByteBuffer())
-  , mPadding(0)
+  , mBuffer(nullptr)
+  , mCapacity(0)
 {
   if (!EnsureCapacity(aSize)) {
     return;
   }
 
   // We ensure sufficient capacity above so this shouldn't fail.
-  MOZ_ALWAYS_TRUE(mBuffer->AppendElements(aData, aSize, fallible));
-  MOZ_ALWAYS_TRUE(mBuffer->AppendElements(RAW_DATA_ALIGNMENT, fallible));
+  memcpy(mData, aData, aSize);
   mSize = aSize;
 }
 
 already_AddRefed<MediaRawData>
 MediaRawData::Clone() const
 {
   nsRefPtr<MediaRawData> s = new MediaRawData;
   s->mTimecode = mTimecode;
@@ -528,78 +524,73 @@ MediaRawData::Clone() const
   s->mExtraData = mExtraData;
   s->mCryptoInternal = mCryptoInternal;
   s->mTrackInfo = mTrackInfo;
   if (mSize) {
     if (!s->EnsureCapacity(mSize)) {
       return nullptr;
     }
 
-    // We ensure sufficient capacity above so this shouldn't fail.
-    MOZ_ALWAYS_TRUE(s->mBuffer->AppendElements(mData, mSize, fallible));
-    MOZ_ALWAYS_TRUE(s->mBuffer->AppendElements(RAW_DATA_ALIGNMENT, fallible));
+    memcpy(s->mData, mData, mSize);
     s->mSize = mSize;
   }
   return s.forget();
 }
 
+// EnsureCapacity ensures that the buffer is big enough to hold
+// aSize. It doesn't set the mSize. It's up to the caller to adjust it.
 bool
 MediaRawData::EnsureCapacity(size_t aSize)
 {
-  if (mData && mBuffer->Capacity() >= aSize + RAW_DATA_ALIGNMENT * 2) {
+  const size_t sizeNeeded = aSize + RAW_DATA_ALIGNMENT * 2;
+
+  if (mData && mCapacity >= sizeNeeded) {
     return true;
   }
-  if (!mBuffer->SetCapacity(aSize + RAW_DATA_ALIGNMENT * 2, fallible)) {
+  nsAutoArrayPtr<uint8_t> newBuffer;
+  newBuffer = new (fallible) uint8_t[sizeNeeded];
+  if (!newBuffer) {
     return false;
   }
+
   // Find alignment address.
   const uintptr_t alignmask = RAW_DATA_ALIGNMENT;
-  mData = reinterpret_cast<uint8_t*>(
-    (reinterpret_cast<uintptr_t>(mBuffer->Elements()) + alignmask) & ~alignmask);
-  MOZ_ASSERT(uintptr_t(mData) % (RAW_DATA_ALIGNMENT+1) == 0);
-
-  // Shift old data according to new padding.
-  uint32_t oldpadding = int32_t(mPadding);
-  mPadding = mData - mBuffer->Elements();
-  int32_t shift = int32_t(mPadding) - int32_t(oldpadding);
+  uint8_t* newData = reinterpret_cast<uint8_t*>(
+    (reinterpret_cast<uintptr_t>(newBuffer.get()) + alignmask) & ~alignmask);
+  MOZ_ASSERT(uintptr_t(newData) % (RAW_DATA_ALIGNMENT+1) == 0);
+  memcpy(newData, mData, mSize);
 
-  if (shift == 0) {
-    // Nothing to do.
-  } else if (shift > 0) {
-    // We ensure sufficient capacity above so this shouldn't fail.
-    MOZ_ALWAYS_TRUE(mBuffer->InsertElementsAt(oldpadding, shift, fallible));
-  } else {
-    mBuffer->RemoveElementsAt(mPadding, -shift);
-  }
+  mBuffer = newBuffer.forget();
+  mCapacity = sizeNeeded;
+  mData = newData;
+
   return true;
 }
 
 MediaRawData::~MediaRawData()
 {
 }
 
 size_t
 MediaRawData::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
 {
   size_t size = aMallocSizeOf(this);
-
-  size += mBuffer->ShallowSizeOfIncludingThis(aMallocSizeOf);
+  size += aMallocSizeOf(mBuffer.get());
   return size;
 }
 
 MediaRawDataWriter*
 MediaRawData::CreateWriter()
 {
   return new MediaRawDataWriter(this);
 }
 
 MediaRawDataWriter::MediaRawDataWriter(MediaRawData* aMediaRawData)
   : mCrypto(aMediaRawData->mCryptoInternal)
   , mTarget(aMediaRawData)
-  , mBuffer(aMediaRawData->mBuffer.get())
 {
 }
 
 bool
 MediaRawDataWriter::EnsureSize(size_t aSize)
 {
   if (aSize <= mTarget->mSize) {
     return true;
@@ -612,56 +603,52 @@ MediaRawDataWriter::EnsureSize(size_t aS
 
 bool
 MediaRawDataWriter::SetSize(size_t aSize)
 {
   if (aSize > mTarget->mSize && !EnsureSize(aSize)) {
     return false;
   }
 
-  // Pad our buffer. We ensure sufficient capacity above so this shouldn't fail.
-  MOZ_ALWAYS_TRUE(
-    mBuffer->SetLength(aSize + mTarget->mPadding + RAW_DATA_ALIGNMENT,
-                       fallible));
   mTarget->mSize = aSize;
   return true;
 }
 
 bool
 MediaRawDataWriter::Prepend(const uint8_t* aData, size_t aSize)
 {
   if (!EnsureSize(aSize + mTarget->mSize)) {
     return false;
   }
 
-  // We ensure sufficient capacity above so this shouldn't fail.
-  MOZ_ALWAYS_TRUE(mBuffer->InsertElementsAt(mTarget->mPadding, aData, aSize,
-                                            fallible));
+  // Shift the data to the right by aSize to leave room for the new data.
+  memmove(mTarget->mData + aSize, mTarget->mData, mTarget->mSize);
+  memcpy(mTarget->mData, aData, aSize);
+
   mTarget->mSize += aSize;
   return true;
 }
 
 bool
 MediaRawDataWriter::Replace(const uint8_t* aData, size_t aSize)
 {
+  // If aSize is smaller than our current size, we leave the buffer as is,
+  // only adjusting the reported size.
   if (!EnsureSize(aSize)) {
     return false;
   }
 
-  // We ensure sufficient capacity above so this shouldn't fail.
-  MOZ_ALWAYS_TRUE(mBuffer->ReplaceElementsAt(mTarget->mPadding, mTarget->mSize,
-                                             aData, aSize, fallible));
+  memcpy(mTarget->mData, aData, aSize);
   mTarget->mSize = aSize;
   return true;
 }
 
 void
 MediaRawDataWriter::Clear()
 {
-  mBuffer->RemoveElementsAt(mTarget->mPadding, mTarget->mSize);
   mTarget->mSize = 0;
   mTarget->mData = nullptr;
 }
 
 uint8_t*
 MediaRawDataWriter::Data()
 {
   return mTarget->mData;
--- a/dom/media/MediaData.h
+++ b/dom/media/MediaData.h
@@ -378,34 +378,27 @@ public:
   // Clear the memory buffer. Will set target mData and mSize to 0.
   void Clear();
 
 private:
   friend class MediaRawData;
   explicit MediaRawDataWriter(MediaRawData* aMediaRawData);
   bool EnsureSize(size_t aSize);
   MediaRawData* mTarget;
-  nsRefPtr<MediaByteBuffer> mBuffer;
 };
 
 class MediaRawData : public MediaData {
 public:
   MediaRawData();
   MediaRawData(const uint8_t* aData, size_t mSize);
 
   // Pointer to data or null if not-yet allocated
-  const uint8_t* Data() const
-  {
-    return mData;
-  }
+  const uint8_t* Data() const { return mData; }
   // Size of buffer.
-  size_t Size() const
-  {
-    return mSize;
-  }
+  size_t Size() const { return mSize; }
 
   const CryptoSample& mCrypto;
   nsRefPtr<MediaByteBuffer> mExtraData;
 
   nsRefPtr<SharedTrackInfo> mTrackInfo;
 
   // Return a deep copy or nullptr if out of memory.
   virtual already_AddRefed<MediaRawData> Clone() const;
@@ -422,19 +415,19 @@ private:
   // Ensure that the backend buffer can hold aSize data. Will update mData.
   // Will enforce that the start of allocated data is always 32 bytes
   // aligned and that it has sufficient end padding to allow for 32 bytes block
   // read as required by some data decoders.
   // Returns false if memory couldn't be allocated.
   bool EnsureCapacity(size_t aSize);
   uint8_t* mData;
   size_t mSize;
-  nsRefPtr<MediaByteBuffer> mBuffer;
+  nsAutoArrayPtr<uint8_t> mBuffer;
+  uint32_t mCapacity;
   CryptoSample mCryptoInternal;
-  uint32_t mPadding;
   MediaRawData(const MediaRawData&); // Not implemented
 };
 
   // MediaByteBuffer is a ref counted infallible TArray.
 class MediaByteBuffer : public nsTArray<uint8_t> {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaByteBuffer);
   MediaByteBuffer() = default;
   explicit MediaByteBuffer(size_t aCapacity) : nsTArray<uint8_t>(aCapacity) {}