0

I'm working on a 1-bit bimodal branch prediction simulator for a class project. I was thinking of using an unordered_map for the table but I need to be able to set the size, so I was thinking using a vector of pairs and table.reserve(tableSize) may be a good way to do this.

However, this leaves me with only linear search of the vector to find table entries which is horribly slow. Does anyone know a way that I can implement a hash function for this application?

For those of you that do not know how a branch predictor table works, the key is the PC address 0x12345678 and the value is T or NT (branch taken or non-taken). I also need a method of collision resolution. If a branch is taken, the bit (bool) is set to true, which will be the prediction for the next branch. If the next branch is NOT taken, the bit is set to false or 0. The purpose of the simulator is to measure correct predictions vs total branches to get the accuracy and compare it to other methods' accuracies. So, the ultimate goal is to use the 2 least significant bytes of the PC (by masking them out) to define the position in the hash table to store the prediction value 0 or 1.

Any help would be greatly appreciated.

On a side note: The maximum table size will be 1024 entries so the time complexity won't have a chance to really scale that high, but any optimization is worth it since I'll be entering this into a competition.


EDIT:

Decided to go with an unordered_map.

This is the solution I've got so far:

Header File

// bimodal 1-bit
class BM1Pred{
    private:
            std::ofstream &outputFile;
            //hash table with bool value mapped to addresses
            // 0 - NOT TAKEN, 1- TAKEN
            unordered_map<long long, bool> table;
            int tableSize;
    public:

            // constructor
            BM1Pred(std::ofstream& file, int size)
            : outputFile(file), tableSize(size) {}

            // deconstructor
            ~BM1Pred()
            {}

            // member functions
            void parseResults(std::string filename);

};
Riptyde4
  • 5,134
  • 8
  • 30
  • 57
  • Did you think of using `std::map`? Seems fitting. – Violet Giraffe Mar 24 '14 at 15:30
  • @VioletGiraffe can I set the size of that? Also, the map must be empty initially. The values will accumulate within it over the course of the program execution. – Riptyde4 Mar 24 '14 at 15:31
  • You can add and remove elements, it's a dynamically-sized container. I don't remember if it has the method for reserving a specific size, but even if it doesn't - just add the required number of elements in a loop and initialize with blank values. – Violet Giraffe Mar 24 '14 at 15:32
  • @VioletGiraffe need it to overwrite one of the map elements 0-1023 instead of adding another one. If I initialize it to blank values won't adding another element increase the table size to 1025....1026...and so on? – Riptyde4 Mar 24 '14 at 15:37
  • Setting the size refers to the setting the size of the table. I need to have seperate objects of my predictor class with table sizes 8,16,32,64,128,256,512,1024. That is, there are X positions in the table that can be filled. – Riptyde4 Mar 24 '14 at 15:40
  • Use the `map` overloaded `[]` operator to assign a new value to an existing key. In fact, using the `[]` operator to insert new elements is also supported. – CPlusPlus OOA and D Mar 24 '14 at 15:41
  • @CPlusPlusOOAandD Is it possible to search the table to see if an entry exists before attempting to reassign one that could potentially not exist resulting in adding another entry and increasing the table size? Apologies, I'm not too familiar with the STL – Riptyde4 Mar 24 '14 at 15:42
  • You won't increase the size of the container. `map[8] = X; map[8] = Y;` will result in a map with exactly one entry, with key `8` and value `Y`. – Bart van Nierop Mar 24 '14 at 15:43
  • 1
    Sure, but the `[]` operator functionality is such that a new key-value will be created if it is not already present. I am posting an answer for your reference. – CPlusPlus OOA and D Mar 24 '14 at 15:44
  • @CPlusPlusOOAandD See that's what I'd like to avoid, so if it's possible to check if that entry is already present before attempting to reassign it then that would solve my problem. Is there a way to check table size as well? – Riptyde4 Mar 24 '14 at 15:45
  • @VioletGiraffe You can't fill an `std::map` with blank values and then update them. It just doesn't work like that. Keys are constant. – iavr Mar 24 '14 at 15:56
  • There are three different capacity related member functions: `empty`, `size`, and `max_size`. To determine the number of elements in a container, call `size`. To search for a key before doing anything else, use `find` or `count`. The `find` method returns an iterator to the key (if present). If not present, the returned iterator value is `mapName.end()`. The `count` method will return zero if the key is not present. – CPlusPlus OOA and D Mar 24 '14 at 15:57
  • @iavr: obviously, but values aren't. Did I misunderstand the question? – Violet Giraffe Mar 24 '14 at 18:50
  • @VioletGiraffe Ok, sorry, I misunderstood. I just think this is pointless, as the whole discussion. I think any associative container will do for the job, nothing special needed. – iavr Mar 24 '14 at 20:37
  • I've posted an edit with my current solution. Let me know what you guys think. – Riptyde4 Mar 24 '14 at 20:42

2 Answers2

1

You can still use an std::unordered_map. If you want to reserve a specific size, you can use std::unordered_map::reserve.

But I can't see why std::map wouldn't be an option. Its size changes dynamically. Why do you need to "set its size"?

To answer your additional questions, in both cases map[key] returns a reference to the value that is mapped to a key equivalent to key, performing an insertion if such key does not already exist. So you insert or update an item by

map[key] = value;

in which case, the value stored for key is updated to value if key already exists in the container; otherwise, an insertion is performed. You don't need to check anything yourself.

iavr
  • 7,547
  • 1
  • 18
  • 53
  • it's because the table can only have up to a certain amount of entries. – Riptyde4 Mar 24 '14 at 15:47
  • 1
    @Riptyde4 Sorry, this is so unclear. An associative container does not have a maximum size. If you want to set one, just check size upon insertion and disallow if above a given size; but I can't see where this would be useful. Maybe you're not describing your actual problem here. – iavr Mar 24 '14 at 15:54
  • Can't you remove an item when you add one above the limit? – Veritas Mar 24 '14 at 15:56
  • @Veritas Of course you can, but I still can't see what's the point. I feel we're all involved in a pointless discussion here. – iavr Mar 24 '14 at 16:02
  • @iavr Yeah, I'll just keep a counter for the amount of entries in the table. I could post the project but trust me you really don't want to read through it...lol – Riptyde4 Mar 24 '14 at 17:52
  • @iavr Essentially what's going to happen is i'm going to be taking the PC address 0x12345678 and masking out the lower bytes to determine position in the table. The goal is to measure effectiveness of different ways of branch prediction – Riptyde4 Mar 24 '14 at 17:55
  • @Riptyde4 If you use an STL container you don't need to keep an extra counter - just use `size()` on the container (there are very few exceptions that don't have `size()`). – iavr Mar 24 '14 at 17:55
  • Yeah thats what I meant sorry lol, i'd use size to control how an element insertion is handled (new spot in the map or overwrite) – Riptyde4 Mar 24 '14 at 17:56
  • @Riptyde4 This is still unclear. You sound like you're going to implement you own hash table, so why not use `std::unordered_map`? This **is** a hash table. Anyhow, if you go for own implementation, at least look at `std::hash`. Just keeping the lower bytes may be a poor choice for a hash function. – iavr Mar 24 '14 at 17:58
  • @iavr Well there's different methods of branch prediction and this is how bimodal 1-bit prediction is done, so I'm just writing a simulator employing its methods to measure its performance and compare it to other methods. If a branch is taken, the bit (bool) is set to true, which will be the prediction for the next branch. If the next branch is NOT taken, the bit is set to false or 0. The purpose of the simulator is to measure correct predictions vs total branches to get the accuracy and compare it to other methods' accuracies. I think I'm going to end up using unordered_map. – Riptyde4 Mar 24 '14 at 18:08
  • I'm just trying to associate the boolean value with the 2 least significant bytes of the PC to store the prediction in the tables of set sizes 8,16,32,64,128,256,512 and 1024 bytes. – Riptyde4 Mar 24 '14 at 18:16
0

I recommend to use a map container and the [] operator. Check out this online reference for more map performance details (e.g. red-black tree): Big-O Cheatsheet. Check out std::map and class template std::map for the properties, member function, iterator, etc. details. As far as I know, the map container is implemented as a red-black tree in C++11. I do know that back in the late 90s, it is definitely implemented as a red-black tree. Keep in mind that the ordering will be from low to high by default (e.g. less<Key>).