C++ auto-expanding vector class template

Discussion of chess software programming and technical issues.

Moderators: hgm, Rebel, chrisw

User avatar
sje
Posts: 4675
Joined: Mon Mar 13, 2006 7:43 pm

C++ auto-expanding vector class template

Post by sje »

C++ auto-expanding vector class template

The C++ template library has a std::vector template which allows for the easy definition of a class which has an auto-expanding vector of some parameter class.

See: http://www.cplusplus.com/reference/vect ... ?kw=vector

The above standard template class has a big bunch of member methods. Maybe more than what are needed for a particular parameter class instantiation. To avoid clutter and overhead, I use my own auto-expanding Vector template class for a number of parameter classes. These classes are for ply-indexed or iteration-indexed items where a fixed length vector would be inappropriate because Symbolic has no fixed MAXPLY or MAXITERATION constant.

For those with an interest, the header:

Code: Select all

typedef unsigned int ui;
#define ZOL&#40;v, limit&#41; for &#40;ui v = 0; v < &#40;limit&#41;; v++)

// Auto expanding vector template

template <class T>
class Vector
&#123;
public&#58;
  Vector&#40;void&#41; &#123;Allocate&#40;1&#41;; Reset&#40;);&#125;
  ~Vector&#40;void&#41; &#123;delete &#91;&#93; storage;&#125;

  void Reset&#40;void&#41; &#123;ZOL&#40;index, count&#41; storage&#91;index&#93;.Reset&#40;);&#125;
  T& operator&#91;&#93;&#40;const ui index&#41; &#123;Ensure&#40;index + 1&#41;; return storage&#91;index&#93;;&#125;

private&#58;
  void Allocate&#40;const ui newcount&#41; &#123;count = newcount; storage = new T&#91;count&#93;;&#125;
  void Ensure&#40;const ui mincount&#41; &#123;while &#40;count < mincount&#41; Double&#40;);&#125;
  void Double&#40;void&#41;
  &#123;
    const ui oldcount = count;
    const T *oldstorage = storage;

    Allocate&#40;count * 2&#41;;
    ZOL&#40;index, oldcount&#41;
    &#123;
      storage&#91;index&#93; = oldstorage&#91;index&#93;;
      storage&#91;index + oldcount&#93;.Reset&#40;);
    &#125;;
    delete &#91;&#93; oldstorage;
  &#125;

  ui  count;
  T  *storage;
&#125;;
The above template assumes that the parameter class has a Reset() method which will perform any needed resetting operations on an element.

The implementation limits unused memory to a maximum of just less than twice the maximum subscript and the number of allocations/copies to (log2 N)^2 where N is the maximum subscript.
User avatar
stegemma
Posts: 859
Joined: Mon Aug 10, 2009 10:05 pm
Location: Italy
Full name: Stefano Gemma

Re: C++ auto-expanding vector class template

Post by stegemma »

sje wrote:C++ auto-expanding vector class template

The C++ template library has a std::vector template which allows for the easy definition of a class which has an auto-expanding vector of some parameter class.

See: http://www.cplusplus.com/reference/vect ... ?kw=vector

The above standard template class has a big bunch of member methods. Maybe more than what are needed for a particular parameter class instantiation. To avoid clutter and overhead, I use my own auto-expanding Vector template class for a number of parameter classes. These classes are for ply-indexed or iteration-indexed items where a fixed length vector would be inappropriate because Symbolic has no fixed MAXPLY or MAXITERATION constant.

For those with an interest, the header:

Code: Select all

typedef unsigned int ui;
#define ZOL&#40;v, limit&#41; for &#40;ui v = 0; v < &#40;limit&#41;; v++)

// Auto expanding vector template

template <class T>
class Vector
&#123;
public&#58;
  Vector&#40;void&#41; &#123;Allocate&#40;1&#41;; Reset&#40;);&#125;
  ~Vector&#40;void&#41; &#123;delete &#91;&#93; storage;&#125;

  void Reset&#40;void&#41; &#123;ZOL&#40;index, count&#41; storage&#91;index&#93;.Reset&#40;);&#125;
  T& operator&#91;&#93;&#40;const ui index&#41; &#123;Ensure&#40;index + 1&#41;; return storage&#91;index&#93;;&#125;

private&#58;
  void Allocate&#40;const ui newcount&#41; &#123;count = newcount; storage = new T&#91;count&#93;;&#125;
  void Ensure&#40;const ui mincount&#41; &#123;while &#40;count < mincount&#41; Double&#40;);&#125;
  void Double&#40;void&#41;
  &#123;
    const ui oldcount = count;
    const T *oldstorage = storage;

    Allocate&#40;count * 2&#41;;
    ZOL&#40;index, oldcount&#41;
    &#123;
      storage&#91;index&#93; = oldstorage&#91;index&#93;;
      storage&#91;index + oldcount&#93;.Reset&#40;);
    &#125;;
    delete &#91;&#93; oldstorage;
  &#125;

  ui  count;
  T  *storage;
&#125;;
The above template assumes that the parameter class has a Reset() method which will perform any needed resetting operations on an element.

The implementation limits unused memory to a maximum of just less than twice the maximum subscript and the number of allocations/copies to (log2 N)^2 where N is the maximum subscript.
I use a similar approach in my clsCollection class but I add a fixed number of elements, when I resize the array. The first count is 10, not 1, because I suppose that nobody would use a dynamic array if it needs less than 10 elements ;)

In my clsCollection class, I use an array of pointers to elements, so that resizing is nothing more than moving existing pointers in the new array. The class seems to be very efficient but it is not simple, because it is a 20 years continuos work for commercial softwares.

I've used two different class, in fact: one for indexed by string collection and one without indexes.

En passant... the engines array mentioned in genetical growing thread is actually a clsCollection, indexed by engine names.

I have a simpler class where elements are directly the items, I call this class clsC and it is usable only for base objects (those with copy statement), like integers, strings and so on. I use this class to store FENs in the last release of the PlayGame of Satana, to test for repeated positions.

All this just to say that maybe it would be useful to have different version of your dynamic array, tailored on the kind of objects they should contains (of course all of them derived by a base dynamic array class).
Author of Drago, Raffaela, Freccia, Satana, Sabrina.
http://www.linformatica.com
Rein Halbersma
Posts: 741
Joined: Tue May 22, 2007 11:13 am

Re: C++ auto-expanding vector class template

Post by Rein Halbersma »

sje wrote:C++ auto-expanding vector class template

The C++ template library has a std::vector template which allows for the easy definition of a class which has an auto-expanding vector of some parameter class.

See: http://www.cplusplus.com/reference/vect ... ?kw=vector

The above standard template class has a big bunch of member methods. Maybe more than what are needed for a particular parameter class instantiation. To avoid clutter and overhead, I use my own auto-expanding Vector template class for a number of parameter classes.
This is really unnecessary. There is no clutter or overhead from std::vector because C++ compilers are not allowed to generate object code for class template member functions that are not called.

So if all you do is call constructor, destructor, push_back(), pop_back() and reference elements through operator[], only those member functions will be instantiated. All the other members will not be present in your object code.

It's not even a link-time optimization (such as removing dead code) but mandatory behavior. The code simply won't even be generated.
mar
Posts: 2559
Joined: Fri Nov 26, 2010 2:00 pm
Location: Czech Republic
Full name: Martin Sedlak

Re: C++ auto-expanding vector class template

Post by mar »

a couple of comments:

- your indexing operator will generate more code (being potentially slower) than what most other dynamic arrays do (not a good idea to grow inside [])
this is why I prefer to append elemens via a method. but in your case I would probably just add a const version of [] that wouldn't grow and assert on out of range index

- because of the above you can't pass const reference to your Vector and read-access elements

- you cannot clear the array while keeping capacity (but that's probably not a problem in your use case), because you don't store capacity at all (this also means you cannot erase elements without reallocating/copying)

- I don't think it's a good idea to allocate memory for empty array; in your case it also means it has size of 1 instead of 0

- you cannot store elements that don't have Reset method (whatever it does)
User avatar
sje
Posts: 4675
Joined: Mon Mar 13, 2006 7:43 pm

Re: C++ auto-expanding vector class template

Post by sje »

For all of my usage:

1) Instance vectors will always need at least one element.
2) Instance vectors will never need to shrink.
3) There is no need for references as values will always be copied in or out.
4) Every parameter class has a Reset() method, although some are empty so no code is generated.
5) There is no need of push_back() or pop-back() methods.
6) There is no need of a big bunch of other methods including any storage-eating member variables required for their support.
7) There is no dependency on the C++11 environment, which is not supported on some of my machines.
8) Dynamic allocation of a whole vector of entries at one time is much faster than allocating entries individually.
9) There is no need to allocate an expanding array of pointers to entries.
10) There is no need to be able to allocate a variable number of entries in the constructor.
11) There is no need to be able to explicitly insert or delete entries.
12) There is no need for custom iterators.

A definition:
dike (verb) To remove a faulty or unnecessary component with a diagonal cutting tool.
Aphorism: "When in doubt, dike it out."
matthewlai
Posts: 793
Joined: Sun Aug 03, 2014 4:48 am
Location: London, UK

Re: C++ auto-expanding vector class template

Post by matthewlai »

sje wrote:For all of my usage:

1) Instance vectors will always need at least one element.
2) Instance vectors will never need to shrink.
3) There is no need for references as values will always be copied in or out.
4) Every parameter class has a Reset() method, although some are empty so no code is generated.
5) There is no need of push_back() or pop-back() methods.
6) There is no need of a big bunch of other methods including any storage-eating member variables required for their support.
7) There is no dependency on the C++11 environment, which is not supported on some of my machines.
8) Dynamic allocation of a whole vector of entries at one time is much faster than allocating entries individually.
9) There is no need to allocate an expanding array of pointers to entries.
10) There is no need to be able to allocate a variable number of entries in the constructor.
11) There is no need to be able to explicitly insert or delete entries.
12) There is no need for custom iterators.

A definition:
dike (verb) To remove a faulty or unnecessary component with a diagonal cutting tool.
Aphorism: "When in doubt, dike it out."
Basically, what you are saying is, you don't need all those features of a vector, so you made a version with much less features, for no benefit really. As explained already, there is not even a performance or code size benefit.

Except for your "no need" points -
6) There is no need of a big bunch of other methods including any storage-eating member variables required for their support.
If you actually look into std::vector implementation, you'll see that it contains exactly what you have - a size and a buffer.
7) There is no dependency on the C++11 environment, which is not supported on some of my machines.
std::vector works perfectly well in C++03. Though they are even more efficient in C++11 because of move semantics.
8) Dynamic allocation of a whole vector of entries at one time is much faster than allocating entries individually.
That can be done with a vector as well, using the constructor that takes a size argument.
Disclosure: I work for DeepMind on the AlphaZero project, but everything I say here is personal opinion and does not reflect the views of DeepMind / Alphabet.
mar
Posts: 2559
Joined: Fri Nov 26, 2010 2:00 pm
Location: Czech Republic
Full name: Martin Sedlak

Re: C++ auto-expanding vector class template

Post by mar »

I understand that it fits your use case.
I'm not a big fan of STL either, which is why I wrote my own containers (not for a chess engine though). Problem is some implementations use lots of cross-includes that impact compilation speed.

Some of your points seem invalid to me though:
6) there are no methods generated (unless actually used) as Rein pointed out already
My Array (=vector) stores data pointer and 32-bit size and capacity. In 64-bit mode, it would consume 16 bytes (the same as your Vector).
Problem with std::vector is they store 3 pointers instead: data pointer, pointer to end and pointer to allocated end.
But sice they also store allocator, it has to occupy at least 1 byte even if it's empty => so it will eat 32 bytes in 64-bit mode
(this can be solved using inheritance instead of composition: inheriting from empty struct/class doesn't have this problem).

7) it has nothing to do with C++11

8) not sure what exactly you mean, new[] and delete[] construct/destroy objects one by one

9) no implementation allocates pointers to entries, unless you have vector<Entry*>

As for the last comment: I'd always prefer a general purpose solution over a single-purpose solution that is not reusable.
Rein Halbersma
Posts: 741
Joined: Tue May 22, 2007 11:13 am

Re: C++ auto-expanding vector class template

Post by Rein Halbersma »

matthewlai wrote:
8) Dynamic allocation of a whole vector of entries at one time is much faster than allocating entries individually.
That can be done with a vector as well, using the constructor that takes a size argument.
Or better yet

Code: Select all

std&#58;&#58;vector<Entry> v;          // no copy, no allocate
v.reserve&#40;EXPECTED_CAPACITY&#41;;  // allocate in one go
// filling with entries        // copy or emplace one-by-one
whereagles
Posts: 565
Joined: Thu Nov 13, 2014 12:03 pm

Re: C++ auto-expanding vector class template

Post by whereagles »

Rein..? As in.. former PhD student of Eric Bergshoef? :)
User avatar
sje
Posts: 4675
Joined: Mon Mar 13, 2006 7:43 pm

Re: C++ auto-expanding vector class template

Post by sje »

First, instances are NOT the same size:

Code: Select all

    // Template size test

    Analysis analysis0, analysis1;
    Vector<Analysis> v0;
    std&#58;&#58;vector<Analysis> v1&#40;1&#41;;

    WriteStdLog&#40;EncodeIbSize&#40;sizeof&#40;v0&#41;) + "    " + EncodeIbSize&#40;sizeof&#40;v1&#41;) + "\n");
    
    v0&#91;0&#93; = analysis0;
    WriteStdLog&#40;"checkpoint 1\n");
    
    v1&#91;0&#93; = analysis1;
    WriteStdLog&#40;"checkpoint 2\n");
Produces:

Code: Select all

&#91;&#93; test
16 B    24 B
checkpoint 1
checkpoint 2
Unless so-called optional initial element reservation is omitted:

Code: Select all

    // Template size test

    Analysis analysis0, analysis1;
    Vector<Analysis> v0;
    std&#58;&#58;vector<Analysis> v1;

    WriteStdLog&#40;EncodeIbSize&#40;sizeof&#40;v0&#41;) + "    " + EncodeIbSize&#40;sizeof&#40;v1&#41;) + "\n");
    
    v0&#91;0&#93; = analysis0;
    WriteStdLog&#40;"checkpoint 1\n");
    
    v1&#91;0&#93; = analysis1;
    WriteStdLog&#40;"checkpoint 2\n");
Which produces:

Code: Select all

&#91;&#93; test
16 B    24 B
checkpoint 1
Segmentation fault&#58; 11
Not very friendly, is it?