i tested the example in book <C++ Concurrency in Action> charpter 7 lock_free_queue and mutex_queue by 2 threads push and 2 threads pop ,the result is using mutex is faster than lock_free queue.here is the lock_free queue code.
so anyone can explain this result? on x86 cpu, the head and tail respectively of lock_free_queue is mutex free,so my first thought is this lock_free_queue will faster than mutex_queue .
here is the mutex queue code:
template<typename T>
class LockFreeQueue
{
public:
struct Node;
struct CountedNodePtr{
int external_count;
Node* ptr;
CountedNodePtr()noexcept:external_count(0),ptr(nullptr){}
};
std::atomic<CountedNodePtr> head;
std::atomic<CountedNodePtr> tail;
struct NodeCounter{
uint32_t internal_count:30;
uint32_t external_counters:2;
};
struct Node{
std::atomic<T*> data;
std::atomic<NodeCounter> count;
CountedNodePtr next;
Node()noexcept{
NodeCounter new_count;
new_count.external_counters = 2;
new_count.internal_count = 0;
count.store(new_count);
data.store(nullptr);
}
void ReleaseRef(){
NodeCounter old_counter = count.load(std::memory_order_relaxed);
NodeCounter new_counter;
do{
new_counter = old_counter;
--new_counter.internal_count;
}while(!count.compare_exchange_strong(old_counter, new_counter,
std::memory_order_acquire,std::memory_order_relaxed));
if(!new_counter.internal_count && !new_counter.external_counters){
delete this;
}
}
};
static void IncreaseExternalCount(std::atomic<CountedNodePtr>& counter,
CountedNodePtr& old_counter){
CountedNodePtr new_counter;
do{
new_counter = old_counter;
++new_counter.external_count;
}while(!counter.compare_exchange_strong(old_counter, new_counter,
std::memory_order_acquire,std::memory_order_relaxed));
old_counter.external_count = new_counter.external_count;
}
static void FreeExternalCounter(CountedNodePtr& old_node_ptr){
Node* const ptr = old_node_ptr.ptr;
int const count_increase = old_node_ptr.external_count-2;
NodeCounter old_counter = ptr->count.load(std::memory_order_relaxed);
NodeCounter new_counter;
do{
new_counter = old_counter;
--new_counter.external_counters;
new_counter.internal_count+=count_increase;
}while(!ptr->count.compare_exchange_strong(old_counter, new_counter,
std::memory_order_acquire,std::memory_order_relaxed));
if(!new_counter.internal_count && !new_counter.external_counters){
delete ptr;
}
}
public:
LockFreeQueue(){
CountedNodePtr ptr ;
ptr.external_count =1;
ptr.ptr = new Node;
head.store(ptr);
tail.store(head.load());
};
~LockFreeQueue(){
while(Node* const old_head = head.load().ptr){
head.store(old_head->next);
delete old_head;
}
}
void Push(T new_value){
std::unique_ptr<T> new_data = std::make_unique<T>(new_value);
CountedNodePtr new_next;
new_next.ptr = new Node;
new_next.external_count = 1;
CountedNodePtr old_tail = tail.load();
for(;;){
IncreaseExternalCount(tail,old_tail);
T* old_data = nullptr;
if(old_tail.ptr->data.compare_exchange_strong(old_data,
new_data.get())){
old_tail.ptr->next = new_next;
old_tail = tail.exchange(new_next);
FreeExternalCounter(old_tail);
new_data.release();
break;
}
old_tail.ptr->ReleaseRef();
}
}
std::unique_ptr<T> Pop(){
CountedNodePtr old_head = head.load(std::memory_order_relaxed);
for(;;){
IncreaseExternalCount(head,old_head);
Node* const ptr = old_head.ptr;
if(ptr==tail.load().ptr){
ptr->ReleaseRef();
return std::unique_ptr<T>();
}
if(head.compare_exchange_strong(old_head, ptr->next)){
T* const res = ptr->data.exchange(nullptr);
FreeExternalCounter(old_head);
return std::unique_ptr<T>(res);
}
ptr->ReleaseRef();
}
}
};
// here is the mutex_queue
template <typename T>
class ThreadSafeQueue {
public:
ThreadSafeQueue() {}
ThreadSafeQueue& operator=(const ThreadSafeQueue& other) = delete;
ThreadSafeQueue(const ThreadSafeQueue& other) = delete;
~ThreadSafeQueue() { BreakAllWait(); }
void Enqueue(const T& element) {
std::lock_guard<std::mutex> lock(mutex_);
queue_.emplace(element);
// cv_.notify_one();
}
bool Dequeue(T* element) {
std::lock_guard<std::mutex> lock(mutex_);
if (queue_.empty()) {
return false;
}
*element = std::move(queue_.front());
queue_.pop();
return true;
}
// bool WaitDequeue(T* element) {
// std::unique_lock<std::mutex> lock(mutex_);
// cv_.wait(lock, [this]() { return break_all_wait_ || !queue_.empty(); });
// if (break_all_wait_) {
// return false;
// }
// *element = std::move(queue_.front());
// queue_.pop();
// return true;
// }
typename std::queue<T>::size_type Size() {
std::lock_guard<std::mutex> lock(mutex_);
return queue_.size();
}
bool Empty() {
std::lock_guard<std::mutex> lock(mutex_);
return queue_.empty();
}
void BreakAllWait() {
break_all_wait_ = true;
// cv_.notify_all();
}
private:
volatile bool break_all_wait_ = false;
std::mutex mutex_;
std::queue<T> queue_;
struct Node{
int data;
Node * next;
Node(const int& d):data(d),next(NULL){}
};
// std::condition_variable cv_;
};
// test code added here
#include <iostream>
#include "lock_free_queue.h"
#include "thread_safe_queue.h"
#include "unbounded_queue.h"
#include <thread>
#include <chrono>
#include <thread>
#include <atomic>
#include <vector>
#include <mutex>
#include <utility>
#include <iomanip>
#include <ucontext.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stddef.h>
#include <string.h>
#include <stdbool.h>
#include <stdint.h>
//#include <stdatomic.h>
#include <pthread.h>
#include <time.h>
#ifndef MDC_TEST
using namespace rudder::base;
using namespace apollo::cyber::base;
using namespace std;
struct Student
{
int a;
double b;
float c;
};
typedef int swr_ele_t; // 元素
int cycles;
typedef struct swr_queue
{ // 队列结构
std::atomic<size_t> head;
std::atomic<size_t> tail;
swr_ele_t *eles;
size_t size;
pthread_mutex_t lock;
} swr_queue_t;
LockFreeQueue<swr_ele_t> lock_queue_test[7];
ThreadSafeQueue<swr_ele_t> mutex_queue_test[7];
UnboundedQueue<swr_ele_t> unbounde_queue_test[7];
bool LockPush(const swr_ele_t *ele,int i)
{
lock_queue_test[i].Push(*ele);
return true;
};
bool LockPop(swr_ele_t *ele,int i)
{
auto tmp = lock_queue_test[i].Pop();
if(tmp == nullptr)
{
return false;
}
else
{
*ele = *tmp;
// std::cout<<"tmp"<<*tmp<<std::endl;
return true;
}
};
bool MutexPush(const swr_ele_t *ele,int i)
{
mutex_queue_test[i].Enqueue(*ele);
return true;
};
bool MutexPop(swr_ele_t *ele,int i)
{
swr_ele_t tmp;
if(mutex_queue_test[i].Dequeue(&tmp))
{
*ele = tmp;
return true;
}else{
return false;
}
};
bool UnboundedPush(const swr_ele_t *ele,int i)
{
unbounde_queue_test[i].Enqueue(*ele);
return true;
};
bool UnboundedPop(swr_ele_t *ele,int i)
{
swr_ele_t tmp;
if(unbounde_queue_test[i].Dequeue(&tmp))
{
*ele = tmp;
return true;
}else{
return false;
}
};
/***************************测试程序部分******************************/
typedef bool (*push_fn_t)(const swr_ele_t *,int);
typedef bool (*pop_fn_t)(swr_ele_t *,int);
// 线程参数
typedef struct thead_arg
{
//swr_queue_t *queue;
char type;
push_fn_t push;
pop_fn_t pop;
double push_time;
double pop_time;
int i;
} thead_arg_t;
// 计时器: 毫秒
double time_diff(struct timespec *start, struct timespec *end)
{
long sec = end->tv_sec - start->tv_sec;
long nsec = end->tv_nsec - start->tv_nsec;
return sec * 1000.0 + nsec / 1000000.0;
}
void *thread_func(void *a)
{
thead_arg_t *arg = (thead_arg_t *)a;
//swr_queue_t *queue = arg->queue;
struct timespec time1, time2;
clock_gettime(CLOCK_THREAD_CPUTIME_ID, &time1);
if (arg->type == 'W')
{ // Producer
swr_ele_t e = cycles;
while (true)
{
if (arg->push(&e,arg->i))
{
if ((--e) < 0)
break;
}
}
}
else
{ // Consumer
while (true)
{
swr_ele_t e;
// cout<<"arg->pop"<<std::endl;
if (arg->pop(&e,arg->i))
{
// cout<<"pop"<<e <<std::endl;
// if (e % 1000000 == 0) printf("pop ele: %d\n", e);
if (e == 0)
break;
}
}
}
clock_gettime(CLOCK_THREAD_CPUTIME_ID, &time2);
if(arg->type == 'W')
{
arg->push_time = time_diff(&time1, &time2);
}
else
{
arg->pop_time = time_diff(&time1, &time2);
}
printf("%c %d Time=%fms\n", arg->type, arg->i,time_diff(&time1, &time2));
// printf("%f ", arg->type, time_diff(&time1, &time2));
return NULL;
}
int main(int argc, char const *argv[])
{
// std::cout<<"argv"<< argv[1]<<std::endl;;
std::cout<<"core_num is : "<<std::thread::hardware_concurrency()<<std::endl;
std::cout<<"lock free head" <<lock_queue_test[0].head.is_lock_free()<<std::endl;
std::cout<<"lock free tail" <<lock_queue_test[0].tail.is_lock_free()<<std::endl;
int num = 0;
while(num<7)
{
cycles = 10000;
struct test_case
{
push_fn_t push;
pop_fn_t pop;
const char *desc;
} cases[] = {
//{LockPush, LockPop, "LockPush, LockPop"},
{MutexPush, MutexPop, "MutexPush,MutexPop"},
// {UnboundedPush, UnboundedPop, "UnboundedPush,UnboundedPop"},
};
for (unsigned int k = 0; k < sizeof(cases) / sizeof(cases[0]); ++k)
{
printf("----------------------------------------------------------------------------\n");
printf("%s\n", cases[k].desc);
swr_queue_t queue;
const int NUM = 6;
pthread_t tids[NUM];
thead_arg_t args[NUM];
int i;//0 1 2 3
// for (i = 0; i < NUM; ++i)
// {
// args[i].i = num;
// // args[i].queue = &queue;
// args[i].type = (i+1)%2 == 1 ? 'W' : 'R';
// args[i].push = cases[k].push;
// args[i].pop = cases[k].pop;
//// printf("pthread_create start");
// pthread_create(&tids[i], NULL, thread_func, args + i);
// }
for (i = 0; i < 3; ++i)
{
args[i].i = num;
// args[i].queue = &queue;
args[i].type = 'W';
args[i].push = cases[k].push;
args[i].pop = cases[k].pop;
// printf("pthread_create start");
pthread_create(&tids[i], NULL, thread_func, args + i);
// pthread_join(tids[i], NULL);
// printf("pthread_create %d",args[i].i);
}
for (i = 0; i < 3; ++i)
{
pthread_join(tids[i], NULL);
}
for (i = 3; i < 6; ++i)
{
args[i].i = num;
// args[i].queue = &queue;
args[i].type = 'R';
args[i].push = cases[k].push;
args[i].pop = cases[k].pop;
// printf("pthread_create start");
pthread_create(&tids[i], NULL, thread_func, args + i);
// pthread_join(tids[i], NULL);
// printf("pthread_create %d",args[i].i);
}
for (i = 3; i < 6; ++i)
{
pthread_join(tids[i], NULL);
}
printf("\n");
// printf("total time=%fms\n", args[0].push_time + args[1].pop_time);
// printf("%f\n", args[0].push_time + args[1].pop_time);
}
num++;
}
return 0;
}
#endif /* MDC_TEST */
Related
I am trying to come up with a generic buffer (or heap container - not sure how to call it) for generic objects. So I could have a large number of them in contiguous memory.
The header I did works, but I was wondering if you guys could help me understand what am I doing wrong if anything, or any gotchas/bugs I am missing here. Or any other re-factoring I could do to make it better.
buffer.h
#ifndef _ENGINE_BUFFER
#define _ENGINE_BUFFER
#include <cstdlib>
#include <cstdio>
#include <typeinfo>
#include <cstring>
// Defining buffer struct:
template <typename T>
struct Buffer {
unsigned int _size;
unsigned int _number_of_objects;
unsigned int _head;
unsigned int _next;
unsigned int * _backlog;
int _backlog_head;
T* _buffer_address;
};
// Creating buffer:
template <typename T>
Buffer<T>* createBuffer(int size) {
Buffer<T>* _buffer = (Buffer<Sprite>*) calloc(1, sizeof(Buffer<T>));
if (_buffer) {
*(_buffer) = Buffer<T>();
_buffer->_size = size;
_buffer->_number_of_objects = 0;
_buffer->_head = 0;
_buffer->_next = 0;
_buffer->_backlog = (unsigned int*) calloc(size, sizeof(int));
_buffer->_backlog_head = -1;
_buffer->_buffer_address = (T*) calloc(size, sizeof(T));
if(_buffer->_buffer_address) {
return _buffer;
}
}
return (Buffer<T>*) nullptr;
}
// Adding object to buffer:
template <typename T>
int addObjectToBuffer(Buffer<T>* _buffer, const T &_obj) {
int _head = 0;
if (_buffer->_backlog_head > -1) {
_buffer->_buffer_address[_buffer->_backlog[_buffer->_backlog_head]] = _obj;
_head = _buffer->_backlog_head;
_buffer->_backlog_head--;
_buffer->_number_of_objects++;
} else {
if ( (int) (_buffer->_number_of_objects + 1) >= _buffer->_size){
T* _new_address = (T*) calloc((_buffer->_number_of_objects + 1) * 2, sizeof(T));
memcpy(_new_address, _buffer->_buffer_address, _buffer->_number_of_objects * sizeof(T));
free(_buffer->_buffer_address);
_buffer->_buffer_address = _new_address;
_buffer->_size = (_buffer->_number_of_objects + 1) * 2;
for (int i = _buffer->_head; i < _buffer->_size; i++){
_buffer->_buffer_address[i] = 0;
}
}
_buffer->_buffer_address[_buffer->_head] = _obj;
_buffer->_number_of_objects++;
_head = _buffer->_head;
_buffer->_head++;
}
return _head;
}
// Getting number of objects in buffer:
template <typename T>
unsigned int bufferGetSize(Buffer<T>* _buffer) {
return (int) _buffer->_number_of_objects;
}
// Getting next object in buffer:
template <typename T>
T* bufferGetNext(Buffer<T>* _buffer) {
if (_buffer->_backlog_head > -1){
for (int i = 0; i <= _buffer->_backlog_head; i++ ) {
if (_buffer->_backlog[i] == _buffer->_next) _buffer->_next++;;
}
}
unsigned int _next = _buffer->_next;
if (_next < _buffer->_head) {
_buffer->_next++;
return &_buffer->_buffer_address[_next];
} else {
_buffer->_next = 0;
bufferGetNext(_buffer);
}
}
// Reset iterator head:
template <typename T>
void bufferResetHead(Buffer<T>* _buffer){
_buffer->_next = 0;
}
// Deleting object from buffer:
template <typename T>
void deleteObjectFromBuffer(Buffer<T>* _buffer, unsigned int _obj_index) {
if (_obj_index >= 0 && _obj_index <= _buffer->_head) {
bool _obj_exists = false;
for (int i = _buffer->_backlog_head; i >= 0; i-- ) {
if (_buffer->_backlog[i] == _obj_index){
_obj_exists = true;
fprintf(stderr, "\n[ Warning: object_deleted_twice | buffer: %#010x | object: %#010x | index: \"%d\" ]\n",
_buffer, &_buffer->_buffer_address[_obj_index], _obj_index);
}
}
if (!_obj_exists) {
_buffer->_backlog_head++;
_buffer->_backlog[_buffer->_backlog_head] = _obj_index;
_buffer->_number_of_objects--;
}
} else {
fprintf(stderr, "\n[ Warning: index_out_of_range | buffer: %#010x | index: \"%d\" ]\n",
_buffer, _obj_index);
}
}
// Deleting buffer:
template <typename T>
void deleteBuffer(Buffer<T>* _buffer) {
free(_buffer->_buffer_address);
free(_buffer);
}
// Print buffer memory:
template <typename T>
void printBufferMemory(Buffer<T>* _buffer, unsigned int _number_of_columns, bool _print_address) {
int _column_count = 0;
printf("\n");
printf("[ Number of objects in buffer: %#02d | Actual buffer size: %#02d ]\n", _buffer->_number_of_objects, _buffer->_size);
for (int i = 1; i < 11 * _number_of_columns; i++) {
printf("=");
}
printf("\n");
for (int i = 0; i < _buffer->_size; i++) {
if (_column_count > _number_of_columns - 1) {
printf("\n");
_column_count = 0;
}
if (_print_address) {
printf("%#010x ", &_buffer->_buffer_address[i]);
} else {
printf("%#010d ", *(&_buffer->_buffer_address[i]));
}
_column_count++;
}
printf("\n");
for (int i = 1; i < 11 * _number_of_columns; i++) {
printf("=");
}
printf("\n");
}
#endif // _ENGINE_BUFFER
sprite.h
#ifndef _SPRITE
#define _SPRITE
struct Sprite {
int value = 0;
Sprite(int n){
value = n;
}
};
#endif // _SPRITE
main.cpp
#include <cstdio>
#include <cstdlib>
#include "sprite.h"
#include "buffer.h"
using namespace std;
int main()
{
int buffer_size = 512;
int object_number = 512;
Buffer<Sprite>* engine_buffer = createBuffer<Sprite>(buffer_size);
for (int i = 0; i < object_number; i++) {
addObjectToBuffer(engine_buffer, Sprite(i + 100));
}
for (int i = 0; i < bufferGetSize(engine_buffer); i++) {
printf("Value of Sprite %d is: %d\n", i + 1, bufferGetNext(engine_buffer)->value);
}
printBufferMemory(engine_buffer, 10, false);
deleteBuffer(engine_buffer);
return(0);
}
I'm developing a container template class. This code needs to interface with existing C code and needs to stay binary compatible, so I can not use i.e. std::vector or similar.
The problem that I have is that it needs to support different allocation strategies, and I don't know how to provide the allocator as a template argument. I created an SSCCE to illustrate how far I got (which of course doesn't compile, because if it would, I wouldn't need to ask this question :)).
#include <iostream>
#include <cstring>
#include <type_traits>
typedef unsigned int uint_t;
typedef signed int int_t;
template <typename T, typename S, typename _allocator = _virtual>
class Container
{
public:
Container(S nItems = 0, S nMaxItems = 0, T *pArray = NULL)
{
mItems = nItems;
mMaxItems = nMaxItems;
mArray = pArray;
}
void adjustMalloc(uint_t nClusterSize)
{
if(mItems == mMaxItems)
{
mArray = (T *)realloc(mArray, (mMaxItems+nClusterSize)*sizeof(T));
mMaxItems += nClusterSize;
}
}
void adjustAligned(uint_t nClusterSize)
{
if(mItems == mMaxItems)
{
mArray = (T *)_aligned_realloc(mArray, (mMaxItems+nClusterSize)*sizeof(T), 16);
mMaxItems += nClusterSize;
}
}
void adjustVirtual(uint_t nClusterSize)
{
if(mItems == mMaxItems)
{
mArray = VirtualAlloc(mArray, (mMaxItems+nClusterSize)*sizeof(T), MEM_RESERVE, PAGE_NOACCESS);
mMaxItems += nClusterSize;
}
}
void adjust(uint_t nClusterSize)
{
if (std::is_same<_allocator>::value == _virtual)
adjustVirtual(nClusterSize);
else if(std::is_same<_allocator>::value == _aligned)
adjustAligned(nClusterSize);
else if(std::is_same<_allocator>::value == _malloc)
adjustMalloc(nClusterSize);
else
{
// Cause a static compiler error, how?
}
}
bool add(T *pItem)
{
if(find(pItem) == NULL)
{
adjust(100);
mItems++;
return true; // added
}
return false;
}
T *find(T *pItem)
{
T *p = mArray;
for(S i = 0; i < mItems; i++, p++)
{
if(*p == *pItem)
return p;
}
return NULL;
}
private:
S mItems;
S mMaxItems;
T *mArray;
};
class Record
{
public:
bool operator==(const Record &oRecord)
{
if(Id != oRecord.Id)
return false;
if(strcmp(Name, oRecord.Name) != 0)
return false;
return true;
}
int Id;
char Name[10+1];
};
int main(int argc, char *argv[])
{
Record rec;
rec.Id = 0;
strcpy(rec.Name, "Test");
Container<Record, uint_t> records; // Default using malloc
records.add(&rec);
if(records.find(&rec) == NULL)
std::cerr << "Not found" << std::endl;
Container<Record, uint_t, _virtual> vrecords; // VirtualAlloc allocator used.
vrecords.add(&rec);
if(records.find(&rec) == NULL)
std::cerr << "Not found" << std::endl;
return 0;
}
I'm using Visual Studio 2010 so it's not 100% C++11.
The VirtualAlloc is provided just as (another) example and will not work as it is shown here.
I found a solution for my problem. However, I get warnings
warning C4127: conditional expression is constant
in the adjust() method for the if(std::is_same... and I was wondering if this is normal or if I can get rid of it, other than disabling it.
#include "stdafx.h"
#include "windows.h"
#include <iostream>
#include <cstring>
#include <type_traits>
#pragma warning (push)
//#pragma warning (disable : 4127)
typedef unsigned int uint_t;
typedef signed int int_t;
typedef struct { const static bool _virtual_allocator = true; } _virtual_type;
typedef struct { const static bool _aligned_allocator = true; } _aligned_type;
typedef struct { const static bool _malloc_allocator = true; } _malloc_type;
template <typename T, typename S, typename _allocator = _aligned_type>
class Container
{
public:
Container(S nItems = 0, S nMaxItems = 0, T *pArray = NULL)
{
mItems = nItems;
mMaxItems = nMaxItems;
mArray = pArray;
}
void adjustMalloc(uint_t nClusterSize)
{
if(mItems == mMaxItems)
{
mArray = (T *)realloc(mArray, (mMaxItems+nClusterSize)*sizeof(T));
mMaxItems += nClusterSize;
}
}
void adjustAligned(uint_t nClusterSize)
{
if(mItems == mMaxItems)
{
mArray = (T *)_aligned_realloc(mArray, (mMaxItems+nClusterSize)*sizeof(T), 16);
mMaxItems += nClusterSize;
}
}
void adjustVirtual(uint_t nClusterSize)
{
if(mItems == mMaxItems)
{
mArray = (T *)VirtualAlloc((LPVOID)mArray, (mMaxItems+nClusterSize)*sizeof(T), MEM_RESERVE, PAGE_NOACCESS);
mMaxItems += nClusterSize;
}
}
void adjust(uint_t nClusterSize)
{
if (std::is_same<_allocator, _virtual_type>::value)
adjustVirtual(nClusterSize);
else if(std::is_same<_allocator, _aligned_type>::value)
adjustAligned(nClusterSize);
else if(std::is_same<_allocator, _malloc_type>::value)
adjustMalloc(nClusterSize);
else
{
// Cause a static compiler error, how?
}
}
bool add(T *pItem)
{
if(find(pItem) == NULL)
{
adjust(100);
mItems++;
return true; // added
}
return false;
}
T *find(T *pItem)
{
T *p = mArray;
for(S i = 0; i < mItems; i++, p++)
{
if(*p == *pItem)
return p;
}
return NULL;
}
private:
S mItems;
S mMaxItems;
T *mArray;
};
#pragma warning (pop)
class Record
{
public:
bool operator==(const Record &oRecord)
{
if(Id != oRecord.Id)
return false;
if(strcmp(Name, oRecord.Name) != 0)
return false;
return true;
}
int Id;
char Name[10+1];
};
int main(int argc, char *argv[])
{
Record rec;
rec.Id = 0;
strcpy(rec.Name, "Test");
Container<Record, uint_t> mrecords;
mrecords.add(&rec);
if(mrecords.find(&rec) == NULL)
std::cerr << "Malloc Not found" << std::endl;
Container<Record, uint_t, _aligned_type> arecords;
arecords.add(&rec);
if(arecords.find(&rec) == NULL)
std::cerr << "Aligned Not found" << std::endl;
Container<Record, uint_t, _virtual_type> vrecords;
vrecords.add(&rec);
if(vrecords.find(&rec) == NULL)
std::cerr << "Virtual Not found" << std::endl;
return 0;
}
What algorithm can be used to calculate post dominators in a control flow graph?
Currently I calculate the dominators by using the iterative bit vector algorithm:
#include <iostream>
#include <list>
#include <stack>
#include <vector>
#include <memory>
#include <map>
class BasicBlock
{
public:
BasicBlock(std::string name) : mName(name) { }
void AllocateDominatorsAndSetToTrue(const size_t nBlocks)
{
mDominators.resize(nBlocks);
for (size_t i = 0; i < nBlocks; i++)
{
mDominators[i] = true;
}
}
void SetAllDominatorsTo(bool value)
{
for (size_t i = 0; i < mDominators.size(); i++)
{
mDominators[i] = value;
}
}
std::string mName;
int mId = 0;
// Links to blocks before this one
std::vector<BasicBlock*> mPredecessors;
// Links to blocks after this one
std::vector<BasicBlock*> mSucessors;
std::vector<bool> mDominators;
std::vector<BasicBlock*> mImmediateDominator;
};
class ControlFlowGraph
{
public:
void AddBasicBlock(std::string name)
{
mBasicBlocks.emplace_back(std::make_unique<BasicBlock>(name));
}
void AddSucessor(std::string block, std::string target)
{
FindBlock(block)->mSucessors.emplace_back(FindBlock(target));
}
void AddPredecessor(std::string block, std::string target)
{
FindBlock(block)->mPredecessors.emplace_back(FindBlock(target));
}
BasicBlock* FindBlock(std::string name)
{
for (const auto& block : mBasicBlocks)
{
if (block->mName == name)
{
return block.get();
}
}
return nullptr;
}
void CalculateDominators()
{
const size_t nBlocks = mBasicBlocks.size();
int i = 0;
for (std::unique_ptr<BasicBlock>& block : mBasicBlocks)
{
block->mId = i++;
block->AllocateDominatorsAndSetToTrue(nBlocks);
}
BasicBlock* block = mBasicBlocks[0].get();
block->SetAllDominatorsTo(false);
block->mDominators[block->mId] = true; // block always dominates itself
bool changed = false;
do
{
changed = false;
for (std::unique_ptr<BasicBlock>& b : mBasicBlocks)
{
if (b == mBasicBlocks[0]) // Is it the entry node?
{
continue;
}
for (BasicBlock* pred : b->mPredecessors)
{
auto T = b->mDominators;
for (size_t i = 0; i < nBlocks; i++)
{
if (b->mDominators[i] && pred->mDominators[i])
{
b->mDominators[i] = true;
}
else
{
b->mDominators[i] = false;
}
}
b->mDominators[b->mId] = true; // block always dominates itself
if (b->mDominators != T)
{
changed = true;
}
}
}
}
while (changed);
}
void CalculateImmediateDominators()
{
// ??
}
std::vector<std::unique_ptr<BasicBlock>> mBasicBlocks;
};
int main()
{
ControlFlowGraph graph;
graph.AddBasicBlock("1");
graph.AddBasicBlock("2");
graph.AddBasicBlock("3");
graph.AddBasicBlock("4");
graph.AddBasicBlock("5");
graph.AddBasicBlock("6");
graph.AddSucessor("1", "2");
graph.AddSucessor("2", "3");
graph.AddSucessor("2", "4");
graph.AddSucessor("3", "2");
graph.AddSucessor("4", "5");
graph.AddSucessor("4", "6");
graph.AddSucessor("5", "4");
graph.AddSucessor("6", "2");
graph.AddPredecessor("2", "1");
graph.AddPredecessor("3", "2");
graph.AddPredecessor("4", "2");
graph.AddPredecessor("5", "4");
graph.AddPredecessor("6", "4");
graph.CalculateDominators();
graph.CalculateImmediateDominators();
return 0;
}
This main program should ask the user to put in some numbers and store them into a dynamic array. The array should then be outputted its contents in a straight line, no end line commands, with a comma in between. I can't figure out how to start the program.
If you guys can help me find a way to do this, I would be eternally thankful!
Here is ListType.h:
#ifndef LISTTYPE_H_INCLUDED
#define LISTTYPE_H_INCLUDED
#include <iostream>
class ListType {
public:
ListType(size_t=10);
virtual ~ListType();
virtual bool insert(int)=0;
virtual bool erase();
virtual bool erase(int)=0;
virtual bool find(int) const=0;
size_t size() const;
bool empty() const;
bool full() const;
void output(std::ostream& out) const;
friend std::ostream& operator << (std::ostream&, const ListType&);
protected:
int *items;
size_t capacity;
size_t count;
};
#endif // LISTTYPE_H_INCLUDED
here is UListType.h:
#ifndef ULISTTYPE_H_INCLUDED
#define ULISTTYPE_H_INCLUDED
#include <iostream>
class UListType: public ListType {
public:
UListType(size_t=10);
bool insert(int);
bool erase(int);
bool find(int) const;
};
#endif // ULISTTYPE_H_INCLUDED
here is OListType.h:
#ifndef OLISTTYPE_H_INCLUDED
#define OLISTTYPE_H_INCLUDED
#include <iostream>
class OListType: public ListType {
public:
OListType(size_t=10);
bool insert(int);
bool erase(int);
bool find(int) const;
};
#endif // OLISTTYPE_H_INCLUDED
here is ListType.cpp:
#include "ListType.h"
ListType::ListType (size_t a) {
capacity = a;
count = 0;
items = new int [capacity];
}
ListType::~ListType() {
delete [] items;
}
bool ListType::erase() {
count = 0;
return 0;
}
size_t ListType::size() const {
return (count);
}
bool ListType::empty() const {
return (count == 0);
}
bool ListType::full() const {
return (count == capacity);
}
void ListType::output(std::ostream& out) const {
for (int i = 0; i < count; i++) {
if (i > 0) {
out << ", ";
}
out << items[i];
}
}
std::ostream& operator << (std::ostream& out, const ListType& my_list) {
my_list.output(out);
return out;
}
here is UListType.cpp
#include "ListType.h"
#include "UListType.h"
UListType::UListType (size_t c): ListType(c) {}
bool UListType::insert(int item) {
if (full()) {
int *newitems;
capacity *=2;
newitems = new int[capacity];
for (size_t i =0; i < count; ++i){
newitems[i] = items[i];
}
delete [] items;
items = newitems;
}
items[count++] = item;
return true;
}
bool UListType::erase(int item) {
bool result = false;
size_t i=0;
while ( i < count && items [i] != item) {
++i;
}
if (i < count) {
items[i] = items[-- count];
result = true;
}
return result;
}
bool UListType::find(int item) const {
size_t i = 0;
while (i < count && items [i] != item) {
++i;
}
return i;
}
here is OListType.cpp
#include "ListType.h"
#include "OListType.h"
OListType::OListType(size_t c): ListType(c) {}
bool OListType::insert(int item) {
size_t i = count;
if (full()) {
int *newitems;
capacity *=2;
newitems = new int[capacity];
while (i > 0 && items[i-1] > item){
newitems[i] = items[i];
}
delete [] items;
items = newitems;
}
items[count++] = item;
return true;
}
bool OListType::erase(int item) {
bool found=false;
size_t i=0, j= count-1, mid;
while (i <= j && !(found)){
mid = (i + j)/2;
if (item < items [mid])
j = mid - 1;
else if (item > items [mid])
i = mid + 1;
found = items [mid] == item;
}
if (found) {
for (i = mid; i < count - 1; ++i) {
items [i] = items [i +1];
}
--count;
}
return found;
}
bool OListType::find (int item) const {
bool found=false;
size_t i=0, j= count-1, mid;
while (i <= j && !(found)){
mid = (i + j)/2;
if (item < items [mid])
j = mid - 1;
else if (item > items [mid])
i = mid + 1;
found = items [mid] == item;
}
return found;
}
#include "ListType.h"
#include "UListType.h"
#include <iostream>
using std::cout;
using std::endl;
using std::cin;
int main()
{
UListType UL;
cout << "How many numbers do you want to put it?" << endl;
int n;
cin >> n;
cout << "All right, enter " << n << " numbers:" << endl;
int x;
for(int k=0; k<n; ++k)
{
cin >> x;
// do something with x
}
return(0);
}
You already have everything you need. Try the following
#include <iostream>
#include "OListType.h"
using namespace std;
int main()
{
OListType list;
int n;
do
{
cout << "Add a number [Y/n]?";
char a;
cin >> a;
if (a != 'n')
{
cin >> n;
list.insert(n);
}
else
{
list.output(cout);
break;
}
}while (1);
return 0;
}
So my Enqueue and Dequeue functions are below. How do I take what I have and make it thread safe? I thought about using a mutex from Windows.h, but I'd like to not limit my program to Windows-only, if possible.
void Queue::Enqueue(int num){
//increase recorded size
size++;
//stick in num
numbers[nextSpace] = num;
//find the next available space
nextSpace = (++nextSpace) % maxSize;
}
int Queue::Dequeue(){
int temp;
temp = items[curSpace];
curSpace = (++curSpace) % maxSize;
size--;
return temp;
}
You can refer this code (with pthreads):
#include<pthread.h>
#define DEFAULT_SIZE 100
class circularQueue{
private:
int *m_queue;
int p_head;
int p_tail;
int m_cap;
pthread_mutex_t mp = PTHREAD_MUTEX_INITIALIZER;
public:
circularQueue(int size)
{
/*in case invalid input*/
if(size<0)
size = DEFAULT_SIZE ;
m_queue = new int[size];
p_head = 0;
p_tail = -1;
m_cap = 0;
pthread_mutex_init(&mp,NULL);
}
bool enqueue(int x)
{
bool res= false;
p_thread_mutex_lock(&mp);
/*queue is full*/
if(m_cap == size)
{
res = false;
}
else
{
m_queue[(++p_tail)%size)] = x;
++m_cap;
res = true;
}
p_thread_mutex_unlock(&mp);
return res;
}
int dequeue()
{
int res=0;
pthread_mutex_lock(&mp);
/*empty queue*/
if(m_cap == 0)
{
throw("empty queue!");
pthread_mutex_unlock(&mp);
}
else{
res = m_queue[p_head];
p_head = (p_head+1)%size;
}
pthread_mutex_unlock(&mp);
return res;
}
~virtual circularQueue()
{
delete[] m_queue;
m_queue = NULL;
pthread_mutex_destroy(&mp);
}
}