patterncppModerate
Custom vector that uses less memory than std::vector
Viewed 0 times
stdthanusescustomlessthatmemoryvector
Problem
I'm using vectors to create matrix and the vector itself is consuming a large space. So I implemented a version that would use less memory and, if possible, be as fast or faster than
How can I improve the performance and is there anything that should be changed?
```
#ifndef __DATAARRAY_H__
#define __DATAARRAY_H__
#include
#include "core/Exceptions/ArgumentException.h"
template class Sizer { };
namespace Core
{
#pragma pack(1)
template
class DataArray
{
public:
DataArray() :m_iDataSize(0), m_aData(nullptr), m_iDataCapacity(0){
}
DataArray(MaxSizeType size) :m_iDataSize(0){
_Reserve(size);
}
DataArray(const DataArray& dArray)
{
_Reserve(dArray.m_iDataCapacity);
_CopyData(dArray);
}
void reserve(MaxSizeType size){
}
void resize(MaxSizeType size){
if (size = size())
throw std::invalid_argument("Not enought memory");
return m_aData[id];
}
DataType& operator=(const DataArray& dArray)
{
if (this == &dArray)
return *this;
_Delete_m_aData();
_Reserve(dArray.m_iDataCapacity);
_CopyData(dArray);
return *this;
}
MaxSizeType size(){
return m_iDataSize;
}
MaxSizeType Capacity(){
return m_iDataCapacity;
}
~DataArray(){
_Delete_m_aData();
}
protected:
inline void _Reserve(MaxSizeType size){
if (size > m_iDataCapacity){
DataType new_Data = (DataType)std::realloc(m_aData, sizeof(DataType)*size);
if (new_Data != NULL){
m_aData = new_Data;
std::vector.How can I improve the performance and is there anything that should be changed?
```
#ifndef __DATAARRAY_H__
#define __DATAARRAY_H__
#include
#include "core/Exceptions/ArgumentException.h"
template class Sizer { };
namespace Core
{
#pragma pack(1)
template
class DataArray
{
public:
DataArray() :m_iDataSize(0), m_aData(nullptr), m_iDataCapacity(0){
}
DataArray(MaxSizeType size) :m_iDataSize(0){
_Reserve(size);
}
DataArray(const DataArray& dArray)
{
_Reserve(dArray.m_iDataCapacity);
_CopyData(dArray);
}
void reserve(MaxSizeType size){
}
void resize(MaxSizeType size){
if (size = size())
throw std::invalid_argument("Not enought memory");
return m_aData[id];
}
DataType& operator=(const DataArray& dArray)
{
if (this == &dArray)
return *this;
_Delete_m_aData();
_Reserve(dArray.m_iDataCapacity);
_CopyData(dArray);
return *this;
}
MaxSizeType size(){
return m_iDataSize;
}
MaxSizeType Capacity(){
return m_iDataCapacity;
}
~DataArray(){
_Delete_m_aData();
}
protected:
inline void _Reserve(MaxSizeType size){
if (size > m_iDataCapacity){
DataType new_Data = (DataType)std::realloc(m_aData, sizeof(DataType)*size);
if (new_Data != NULL){
m_aData = new_Data;
Solution
Size Requirements
You talk about wanting to "use less memory" than
Performance
There's two glaring issues that jump out at me as far as performance goes. I suspect this would be substantially slower than
First one is on
First, you're taking your
The next issue comes from indexing:
First, the exception is misleading. A better exception type would be
But the main problem is this kills performance because on every lookup, we have a branch. Every time. And branches are slow. That's why on
If you want bounds checking, you can provide a separate member function called
Other Issues
You're missing a lot of other nice features that
-
This cast is unnecessary, since
You talk about wanting to "use less memory" than
std::vector. But std::vector doesn't use that much memory. It's three pointers. I guess if you set your MaxSizeType to int instead of size_t, you can have a vector of 16 bytes instead of 24, but I would really have to see what you're doing that that difference should matter.Performance
There's two glaring issues that jump out at me as far as performance goes. I suspect this would be substantially slower than
std::vector.First one is on
push_back:void push_back(DataType element){
if (m_iDataCapacity <= m_iDataSize)
{
_Reserve(m_iDataCapacity + 1);
}
new (m_aData + m_iDataSize)DataType(element);
m_iDataSize++;
}First, you're taking your
DataType by value. That's incurring an extra copy, which you don't want to do. Secondly, your resize policy is increasing capacity by one. This means that your push_back() is expected \$O(n)\$. There's an easy explanation for this too: once we reach our capacity, we will have to _Reserve() on every subsequent push_back(), which means we're copying \$n\$ elements every time. The right thing to do is to increase capacity by a constant factor.The next issue comes from indexing:
DataType& operator[](MaxSizeType id)
{
if (id = size())
throw std::invalid_argument("Not enought memory");
return m_aData[id];
}First, the exception is misleading. A better exception type would be
std::out_of_range, and a message indicating this - since the issue isn't that we ran out of memory. That's an error that we would throw if allocation fails (in _Reserve, you do the same thing - there you should throw std::bad_alloc).But the main problem is this kills performance because on every lookup, we have a branch. Every time. And branches are slow. That's why on
std::vector, operator[] is defined simply as:reference
operator[](size_type __n)
{
return *(this->_M_impl._M_start + __n);
}If you want bounds checking, you can provide a separate member function called
at(). Other Issues
You're missing a lot of other nice features that
std::vector provides:- Missing iterators,
begin(), andend().
- Missing
constoverloads of member functions
-
This cast is unnecessary, since
pData is already a DataType.((DataType*)pData)->~DataType();Code Snippets
void push_back(DataType element){
if (m_iDataCapacity <= m_iDataSize)
{
_Reserve(m_iDataCapacity + 1);
}
new (m_aData + m_iDataSize)DataType(element);
m_iDataSize++;
}DataType& operator[](MaxSizeType id)
{
if (id < 0 || id >= size())
throw std::invalid_argument("Not enought memory");
return m_aData[id];
}reference
operator[](size_type __n)
{
return *(this->_M_impl._M_start + __n);
}((DataType*)pData)->~DataType();Context
StackExchange Code Review Q#111114, answer score: 10
Revisions (0)
No revisions yet.