dimanche 1 mars 2020

How does an iterator works internally in C++?

I am unable to understand how iterator works exactly in C++. As I read in some blogs that iterator does not contain address. If it does not contain address then how exactly are we incrementing it like it++ or ++it.

Lets take an example of a vector iterator.

vector<int> ::iterator it;
for(it=vec.begin(); it!=vec.end(); it++)
{
    // do something
}

if iterator is not a pointer containing an address then what exactly is it++ doing. What is it storing. How is it working internally. How is iterator exactly pointing to the elements of a vector?

When to std::move from *this in ref qualified method?

I am playing around with immutable objects after reading the excellent Functional Programming in C++ book. Assume I have a class for an immutable vector like this:

template<typename T>
class immutable_vector
{
public:
   immutable_vector() {}

immutable_vector<T> push_back(T t) const &
{
    immutable_vector result(*this);
    result.data.push_back(t);
    return result;
}

//What is the best way to implement this? (see below)
immutable_vector<T> push_back(T t) &&;

private:
   std::vector<T> data;

};

I know that this is a dumb way of implementing the immutable vector, but this is not the point of the question. My question is on how to implement the rvalue qualified member function push_back.

Variant 1

immutable_vector<T> push_back(T t) &&
{
    data.push_back(t);
    return std::move(*this);
}

Variant 2

immutable_vector<T> push_back(T t) &&
{
    immutable_vector<T> result(std::move(*this));
    result.data.push_back(t);
    return result;
}

This is the way the author of the book does it.

My Question

Is one way better than the other or are they completely equivalent?

My guess was that they would be equivalent, because in both cases the move constructor is called once. I would also assume that in Variant 2 copy elision would take place so that no additional constructors would be called. Am I missing something?

Is it thread safe to use new in meyer's singleton implementation?

We know that Meyers Singleton is thread safe since c++11.

I'm using this singleton pattern.

struct singleton_t
{

  static
  singleton_t &
  instance()
  {
    static singleton_t s;
    return s;
  } // instance

  singleton_t(const singleton_t &) = delete;
  singleton_t & operator = (const singleton_t &) = delete;

private:

  singleton_t() {}
  ~singleton_t() {}

}; // struct singleton_t

singleton_t is not a pointer or reference so the polymorphism doesn't apply.

Now I want to do it something like this:

struct singleton_t
{

  static
  singleton_t *
  instance(singleton_t* p = nullptr)
  {
    static singleton_t *s = nullptr == p ? new singleton_t : p;
    return s;
  } // instance

  singleton_t(const singleton_t &) = delete;
  singleton_t & operator = (const singleton_t &) = delete;

private:

  singleton_t() {}
  ~singleton_t() {}

}; // struct singleton_t

In this way, I singleton_t can point to instance of its subclass so the polymorphism can be achieved. Or is there a better approach?

C++ Error: no matching function for call to 'toupper'

The following code raises the no matching function for call to 'toupper' error (C++ 11):

string s("some string");
if (s.begin() != s.end())
{
    auto it = s.begin();
    it=toupper(it);
}

This code is from C++ primer, fifth edition, chapter 3, about "introducing iterators".

Do you know why this error?

Passing an array class as an argument in c++

I want to pass an array as an argument in a function but I cannot find correct method.I am not talking about normal array (int arr[2]={1,3};). I am talking about array class available from c++ 11. example code:

#include<iostream>
#include<array>
using namespace std;
class test
{
   void function(array<int>myarr)
   {
     .......some code..........
   }
};

How do I pass array like this in function as array class takes two arguments: array<data type,size>name_of_array but while passing it as a argument I do not want to specify the size.

Why does not AVX further improve the performance compared with SSE2?

I am new to the field of SSE2 and AVX. I write the following code to test the performance of both SSE2 and AVX.

#include <cmath>
#include <iostream>
#include <chrono>
#include <emmintrin.h>
#include <immintrin.h>

void normal_res(float* __restrict__ a, float* __restrict__ b, float* __restrict__ c, unsigned long N) {
    for (unsigned long n = 0; n < N; n++) {
        c[n] = sqrt(a[n]) + sqrt(b[n]);
    }
}

void normal(float* a, float* b, float* c, unsigned long N) {
    for (unsigned long n = 0; n < N; n++) {
        c[n] = sqrt(a[n]) + sqrt(b[n]);
    }
}

void sse(float* a, float* b, float* c, unsigned long N) {
    __m128* a_ptr = (__m128*)a;
    __m128* b_ptr = (__m128*)b;

    for (unsigned long n = 0; n < N; n+=4, a_ptr++, b_ptr++) {
        __m128 asqrt = _mm_sqrt_ps(*a_ptr);
        __m128 bsqrt = _mm_sqrt_ps(*b_ptr);
        __m128 add_result = _mm_add_ps(asqrt, bsqrt);
        _mm_store_ps(&c[n], add_result);
    }
}

void avx(float* a, float* b, float* c, unsigned long N) {
    __m256* a_ptr = (__m256*)a;
    __m256* b_ptr = (__m256*)b;

    for (unsigned long n = 0; n < N; n+=8, a_ptr++, b_ptr++) {
        __m256 asqrt = _mm256_sqrt_ps(*a_ptr);
        __m256 bsqrt = _mm256_sqrt_ps(*b_ptr);
        __m256 add_result = _mm256_add_ps(asqrt, bsqrt);
        _mm256_store_ps(&c[n], add_result);
    }
}

int main(int argc, char** argv) {
    unsigned long N = 1 << 30;

    auto *a = static_cast<float*>(aligned_alloc(128, N*sizeof(float)));
    auto *b = static_cast<float*>(aligned_alloc(128, N*sizeof(float)));
    auto *c = static_cast<float*>(aligned_alloc(128, N*sizeof(float)));

    std::chrono::time_point<std::chrono::system_clock> start, end;
    for (unsigned long i = 0; i < N; ++i) {                                                                                                                                                                                   
        a[i] = 3141592.65358;           
        b[i] = 1234567.65358;                                                                                                                                                                            
    }

    start = std::chrono::system_clock::now();   
    for (int i = 0; i < 5; i++)                                                                                                                                                                              
        normal(a, b, c, N);                                                                                                                                                                                                                                                                                                                                                                                                            
    end = std::chrono::system_clock::now();
    std::chrono::duration<double> elapsed_seconds = end - start;
    std::cout << "normal elapsed time: " << elapsed_seconds.count() / 5 << std::endl;

    start = std::chrono::system_clock::now();     
    for (int i = 0; i < 5; i++)                                                                                                                                                                                                                                                                                                                                                                                         
        normal_res(a, b, c, N);    
    end = std::chrono::system_clock::now();
    elapsed_seconds = end - start;
    std::cout << "normal restrict elapsed time: " << elapsed_seconds.count() / 5 << std::endl;                                                                                                                                                                                 

    start = std::chrono::system_clock::now();
    for (int i = 0; i < 5; i++)                                                                                                                                                                                                                                                                                                                                                                                              
        sse(a, b, c, N);    
    end = std::chrono::system_clock::now();
    elapsed_seconds = end - start;
    std::cout << "sse elapsed time: " << elapsed_seconds.count() / 5 << std::endl;   

    start = std::chrono::system_clock::now();
    for (int i = 0; i < 5; i++)                                                                                                                                                                                                                                                                                                                                                                                              
        avx(a, b, c, N);    
    end = std::chrono::system_clock::now();
    elapsed_seconds = end - start;
    std::cout << "avx elapsed time: " << elapsed_seconds.count() / 5 << std::endl;   
    return 0;            
}

I compile my program by using g++ complier as the following.

g++ -msse -msse2 -mavx -mavx512f -O2

The results are as the following. It seems that there is no further improvement when I use more advanced 256 bits vectors.

normal elapsed time: 10.5311
normal restrict elapsed time: 8.00338
sse elapsed time: 0.995806
avx elapsed time: 0.973302

I have two questions.

  1. Why does not AVX give me further improvement? Is it because the memory bandwidth?
  2. According to my experiment, the SSE2 perform 10 times faster than the naive version. Why is that? I expect the SSE2 can only be 4 times faster based on its 128 bits vectors with respect to single precision floating points. Thanks a lot.

samedi 29 février 2020

Execution order of map[K] = V [duplicate]

Give the following C++ code.

std::map<std::pair<int, int>, int> map;
int test = 0;
map[std::make_pair(test, 2)] = ++test;

The contents of map becomes to (1,2) -> 1. Why not (0, 2) -> 1? Can anyone explain the order of execution in this scenario?