[转载]RArray and Resource Mangement

 

RArray and Resource Mangement

发布: 2008-3-07 21:42 | 作者: eula | 来源: DevDiv 移动开发社区

 

src: http://www.devdiv.net/viewthread-2398

 

f you are not using RArray, you should be!
It offers a substantial performance improvement over the CArray.. implementations for almost all the cases I can think of.

    * It is fast - RArray is coded in ARM assembly in release versions of the code.
    * It does not use asserts internally and also does not leave, it just returns error codes, so the use of trap statements in runtime significant areas of the code can be minimized or reduced.
    * It is lightweight
    * It is a wrapper over a more complex implementation of a generic array.
    * There are templated version of TInt and TUint built into EUSER so it does not bloat your executable.

Most of RArray is fairly simple to understand and works the way any generic array implementation works.

The biggest problem I do have with RArray is that the search and insert functions do not allow a variable to be passed to them so you are required to use a global or some other hack to get a state variable over into the comparison function.

The areas I am going look at are how RArray manages resources and how RArray can be applied effectively used to improve performance.

Before we can continue there are some questions that need to be answered:
2008-3-07 21:42:23
How does RArray allocates memory?
If you play around with RArray you will soon find out that it allocates memory via the User::Alloc functions and these allocations are always a multiple of 4 bytes.

What RArray Constructors are there?
There is a default constructor, a constructor that specifies by how much the array will be expanded when it runs out of spare slots, and most importantly a constructor that maps onto a memory locations- but more of that later.

What is not present is a copy constructor, so what will happen then is that the C++ compiler will create a default constructor that is a bitwise copy of the item it is being copied from.

By default RArray does not have any items, however if one item is added, it preallocates by default 8 items, well okay KGranularity items so it can avoid memory fragmentation when adding items by avoiding a reallocate and copy each time an item is added.

Resource Management:
For the purposes of the example we will create a structure called TVector which has two integers in it.
THIS STRUCTURE IS A MUTIPLE OF 4 BYTES WHICH IS MANDATORY FOR RARRAY ELEMENTS!

struct TVector
{
  TInt iSpeed;
  TInt iDirection;
};
typedef RArray<TVector> RVectorArray; // Note that we keep the R prefix on our typedef

(1) static const TVector data[] = { {1,1}, {2,2}, {3,3} };
(2) const RVectorArray array(sizeof(TVector), CONST_CAST(TVector*,&data[0]), sizeof(data) / sizeof(data[0]));
(3) RVectorArray array1;
(4) array1 = array;
(5) TInt count = array.Count();

Each of the lines is numbered and documented as follows:
(1) we create a static const memory block. This could also be a block of memory from somewhere else such as a scanline or bitmap row.
(2) We now create an RArray that maps that memory into the RArray, note that the array is const so we can't expand it by accident.

It is important to read the documentation on this as it is very clear that this constructor does not allocate any memory nor take ownership of the memory, it just provides a mapping for developers to use the RArray with blocks of memory that needs to be manipulated.

(3) We now create a default RArray which has not memory allocated and it does not have any elements.

(4) Finally we create a clone of the array. It is vital to note that the compiler has underneath generated a default copy constructor and created a bitwise clone of data into array.

(5) Finally get the number of items in array after we have cloned it. What is the value of count?
Answer: See below
2008-3-07 21:42:40
IMPORTANT NOTES:
(2) This just creates a mapping of the memory structure into the RArray is does not force the RArray to allocate any memory, hence there is nothing that needs to be put onto the cleanup stack. This is a very important point that a lot of people get wrong.
(4) This creates a clone of the array but ownership of the items in the data array is not taken
(5) Since array is a clone of data, count is 3
(6) When the function goes out of scope the arrays are destroyed, but we do not call reset. This is very important, because the arrays do not own any memory they MUST NOT be freed, so calling Reset is a big no-no! Likewise putting it on the cleanup stack is also a big mistake and will cause crashes.

Resource Management Part 2:
As I mentioned earlier, the underyling memory allocator for RArray is User::Alloc, so providing we allocate memory via this mechanism, we can move memory blobs from our structure into an RArray and then use the RArray.

(1) TVector* data2 = REINTERPRET_CAST(TVector*, User::AllocL(sizeof(data))); // actually sizeof(data) / (sizeof(data) / sizeof(data[0]))
(2) Mem::Copy(data2, data, sizeof(data));
(3) RVectorArray array2(sizeof(TVector), data2, sizeof(data) / (sizeof(data) / sizeof(data[0])));
(4) CleanupClosePushL(array2);
(5) const TVector element = {4,4};
(6) array2.Append(element);
(7) count = array2.Count();
(8) CleanupStack::PopAndDestroy(&array2);

Each of the lines is nummered and documented as follows:
(1) We allocate a block of memory using User::Alloc that is size of the 'data' structure (3 * sizeof(TVector))
This data is not put onto the cleanup stack, though of course we could put it onto the cleanup stack here and then remove it from the cleanup stack on line (4) and add the RArray back to the cleanup stack.
(2) We now copy the data from the const memory structure to our newly allocated memory blob
(3) We now create an RArray that maps the memory we just allocated to the internals of an RArray.
(4) Because we allocated the memory somewhere else, I decided that the RArray should now take ownership of the array, so that if any of the following lines should leave the memory will get correctly cleaned up by the RArray.
(5) we declare a new element
(6) This element is now added to our array that owns the memory. Where the array is full, the array will be expanded to cater for the new element, which may result in a memory reallocation, hence BE VERY CAREFUL where ownership of memory blocks is made.
(7) What should count now report? Answer: 4
(8) As we transfered ownship of the memory block to the array2 RArray, the cleanup stack may now delete it. If we did not put it onto the cleanup stack, we can just call Reset which has the same effect.

IMPORTANT NOTES:

(4) Is very important as you want to move ownership of the memory block to the RArray as soon as possible, especially if you want to add or remove items from the RArray.
(6) We should really check the return value from the append as it could have returned a negative value which would indicate that the item failed to expand. I have seen a number of cases where the error code is never checked and so unexpected things start happening.
(8) We do not neccessarily need to put the array2 as a parmeter to the PopAndDestroy but it provides an additional check that the item at the top of the stack is the item we expected it to be.

Some other things about RArray and RPointerArray

    * This is an advanced example of how to use resource management and RArray. Its not for beginners.
    * In real life I have used to read a block of integers (5000) from disk as a TPtrC8 and then cast it and move it to the RArray where the RArray takes ownership of the data. Which knocked about 10 seconds off the load time.
    * The arrays only take items that are 4 bytes or a multiple of 4 bytes, so if your object is say 9 bytes then the RArray will internally allocate 12 bytes for each item, hence take care when creating structures. Likewise, if your structure has different size items in it, check at compile time that the size is what you expect it to be. I have encountered situations where the sizeof has reported a different to what I had expected becuase of alignment issues in structure fields.
    * To get the address of the block of memory allocated to the RArray, take the address of element zero.
      i.e. TVector* ptr = &(array[0]);
    * RPointerArray does not have an implementation that destroys all the pointer objects. Normally you would do this via ResetAndDestroy. For some reason Symbian decided to put the implementation of this in mmfcontrollerpluginresolver.h. The function is called CleanupResetAndDestroyPushL
    * One source of problems with RArray is when appending a large number of items to the array in one go.
      For example lets say:
      (1) RArray<TInt> array;
      (2) for (TInt i=0; i < 1000; i++)
      (3)    array.Append(i);

      Each time (3) is called when the array hits capacity, it will need to be expanded by its default granularity (8) which means that there will be a large number (124 reallocations) during this example.
      In OS 9.x Symbian has added a Reserve function which preallocates a specified number of items in the RArray so that when adding items, the array does not need to be expanded which reduces memory fragmentation.
      To simulate this in OS7 and OS8, we can reuse the tech above to allocate and transfer ownership.

      For example:
      (1) RArray<TVector> temp(sizeof(TVector), REINTERPRET_CAST(TVector*, User::AllocL(1000 * sizeof(TVector))), 1000);
      (2) for (TInt i= 1000-1; i >= 0; i--)
      (3)    temp.Remove(i);
      (4) RArray<TVector> array3 = temp;

      Important Notes
      (1) We allocate 1000 items and put it into the RArray.
      (2,3) We then remove the items starting from the back which prevents horrific memory copying.
      (4) Finally assign the new array to the existing array. Note that if the existing array (array3) already has items then you will need to call Reset on it before assigning the items, otherwise the memory in the array will be orphaned and you will get a memory leak.
2008-3-07 21:42:54
In Conclusion:
I hope this has provided an example of how to use resources in RArray where speed is critical. Needless to say, make sure that if you use this technique you keep it localized to one function so that the maintainer can know exactly what you are doing as these techniques need to be used to extreme care and only where there is time critical code.

Next week I want to discuss how I ported SyExpat to OS 9 which I have been promising to deliver for ages now. Its coded, I just hav'nt had a chance to document what I changed and why.

I only just posted a comment about multidimensional RArrays so I might publish some additional notes about these this week as well.

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值