C/C++ 宏

宏的一些用法:
1、#和##
#符号把一个符号直接转换为字符串,例如:
#define STRING(x) #x
const char *str = STRING( test_string ); str的内容就是”test_string”,也就是说#会把其后的符号直接加上双引号。
符号会连接两个符号,从而产生新的符号(词法层次),例如:
#define SIGN( x ) INT_##x
int SIGN( 1 ); 宏被展开后将成为:int INT_1;
2、变参宏 可以使你自定义类似的宏
#define LOG( format, … ) printf( format, __VA_ARGS__ )
LOG( “%s %d”, str, count );     __VA_ARGS__是系统预定义宏,被自动替换为参数列表。
3、宏调用自己
为了防止宏无限递归展开,当宏展开遇到自己时,就停止展开。
#define TEST(X) (X+TEST(X))
则TEST(1) 最终会展开成1+TEST(1)

CUDA Pro Tip: Always Set the Current Device to Avoid Multithreading Bugs

We often say that to reach high performance on GPUs you should expose as much parallelism in your code as possible, and we don’t mean just parallelism within one GPU, but also across multiple GPUs and CPUs. It’s common for high-performance software to parallelize across multiple GPUs by assigning one or more CPU threads to each GPU. In this post I’ll cover a common but subtle bug and a simple rule that will help you avoid it within your own software (spoiler alert: it’s in the title!).
Let’s review how to select which GPU to execute CUDA calls on. The CUDA runtime API is state-based, and threads executecudaSetDevice()to set the current GPU.

cudaError_t cudaSetDevice(int device)

After this call all CUDA API commands go to the current set device untilcudaSetDevice()is called again with a different device ID. The CUDA runtime API is thread-safe, which means it maintains per-thread state about the current device. This is very important as it allows threads to concurrently submit work to different devices, but forgetting to set the current device in each thread can lead to subtle and hard-to-find bugs like the following example.

cudaSetDevice(1);
cudaMalloc(&a,bytes);
#pragma omp parallel
 {
       kernel<<<blocks,threads>>>(a);
 }

While at first glance this code may seem bug free, it is incorrect. The problem here is that we have set device 1 current on the OpenMP master thread but then used OpenMP to spawn more threads which will use the default device (device 0) because they never callcudaSetDevice(). This code would actually launch multiple kernels that run on device 0 but access memory allocated on device 1. This will cause either invalid memory access errors or (in the case where peer-to-peer access is enabled) it will be limited by low PCIe memory bandwidth to the arraya.
Here is a correct implementation of the code, where every thread sets the correct device.

cudaSetDevice(1);
 cudaMalloc(&a,bytes);
 #pragma omp parallel
 {
         cudaSetDevice(1);
         kernel<<<blocks,threads>>>(a);
 }

If it’s not obvious from the title of this post, there’s a simple rule to follow to avoid bugs like this…

Always Set the Device in New Host Threads

Make it a habit to callcudaSetDevice()wherever your code could potentially spawn new host threads. The following example has a potential bug depending on whether the OpenMP library chooses to spawn new threads or reuse old ones.

cudaSetDevice(1);
cudaMalloc(&a,bytes);
#pragma omp parallel
 {
       cudaSetDevice(1);
        kernel<<<blocks,threads>>>(a);
}
#pragma omp parallel
 {
        kernel<<<blocks,threads>>>(a);
}

In this example, threads in the secondomp parallelregion don’t set the current device so there is no guarantee that it is set for each thread. This problem is not restricted to OpenMP; it can easily happen with any threading library, and in any CUDA-accelerated Language.
To save yourself from a variety of multithreading bugs, remember: always callcudaSetDevice()first when you spawn a new host thread.

∥∀

 

About Justin Luitjens

Justin Luitjens

Justin Luitjens is a member of the Developer Technology team at NVIDIA where he works on accelerating applications on GPUs. He holds a Ph.D in Scientific Computing from the University of Utah.

http://devblogs.nvidia.com/parallelforall/cuda-pro-tip-always-set-current-device-avoid-multithreading-bugs/

c++构造函数详解

转载自 http://ticktick.blog.51cto.com/823160/194307

 c++构造函数的知识在各种c++教材上已有介绍,不过初学者往往不太注意观察和总结其中各种构造函数的特点和用法,故在此我根据自己的c++编程经验总结了一下c++中各种构造函数的特点,并附上例子,希望对初学者有所帮助。

                                        c++类的构造函数详解                        

一、 构造函数是干什么的

class Counter
{

public:
         // 类Counter的构造函数
         // 特点:以类名作为函数名,无返回类型
         Counter()
         {
                m_value = 0;
         }
        
private:
     
         // 数据成员
         int m_value;
}

       该类对象被创建时,编译系统对象分配内存空间,并自动调用该构造函数->由构造函数完成成员的初始化工作

eg:    Counter c1;
        编译系统为对象c1的每个数据成员(m_value)分配内存空间,并调用构造函数Counter( )自动地初始化对象c1的m_value值设置为0

故:

        构造函数的作用:初始化对象的数据成员。

二、 构造函数的种类

class Complex
{        

private :
        double    m_real;
        double    m_imag;

public:

        //    无参数构造函数
        // 如果创建一个类你没有写任何构造函数,则系统会自动生成默认的无参构造函数,函数为空,什么都不做
        // 只要你写了一个下面的某一种构造函数,系统就不会再自动生成这样一个默认的构造函数,如果希望有一个这样的无参构造函数,则需要自己显示地写出来
        Complex(void)
        {
             m_real = 0.0;
             m_imag = 0.0;
        }
       
        //    一般构造函数(也称重载构造函数)
        // 一般构造函数可以有各种参数形式,一个类可以有多个一般构造函数,前提是参数的个数或者类型不同(基于c++的重载函数原理)
        // 例如:你还可以写一个 Complex( int num)的构造函数出来
        // 创建对象时根据传入的参数不同调用不同的构造函数
        Complex(double real, double imag)
        {
             m_real = real;
             m_imag = imag;        
         }
       
        //    复制构造函数(也称为拷贝构造函数)
        //    复制构造函数参数为类对象本身的引用,用于根据一个已存在的对象复制出一个新的该类的对象,一般在函数中会将已存在对象的数据成员的值复制一份到新创建的对象中
        //    若没有显示的写复制构造函数,则系统会默认创建一个复制构造函数,但当类中有指针成员时,由系统默认创建该复制构造函数会存在风险,具体原因请查询 有关 “浅拷贝” 、“深拷贝”的文章论述
        Complex(const Complex & c)
        {
                // 将对象c中的数据成员值复制过来
                m_real = c.m_real;
                m_img    = c.m_img;
        }            
   
        // 类型转换构造函数,根据一个指定的类型的对象创建一个本类的对象
        // 例如:下面将根据一个double类型的对象创建了一个Complex对象
        Complex::Complex(double r)
        {
                m_real = r;
                m_imag = 0.0;
        }

        // 等号运算符重载
        // 注意,这个类似复制构造函数,将=右边的本类对象的值复制给等号左边的对象,它不属于构造函数,等号左右两边的对象必须已经被创建
        // 若没有显示的写=运算符重载,则系统也会创建一个默认的=运算符重载,只做一些基本的拷贝工作
        Complex &operator=( const Complex &rhs )
        {
                // 首先检测等号右边的是否就是左边的对象本,若是本对象本身,则直接返回
                if ( this == &rhs )
                {
                        return *this;
                }
               
                // 复制等号右边的成员到左边的对象中
                this->m_real = rhs.m_real;
                this->m_imag = rhs.m_imag;
               
               // 把等号左边的对象再次传出
               // 目的是为了支持连等 eg:    a=b=c 系统首先运行 b=c
               // 然后运行 a= ( b=c的返回值,这里应该是复制c值后的b对象)    
                return *this;
        }

};

下面使用上面定义的类对象来说明各个构造函数的用法:

void main()
{
        // 调用了无参构造函数,数据成员初值被赋为0.0
        Complex c1,c2;

        // 调用一般构造函数,数据成员初值被赋为指定值
        Complex c3(1.0,2.5);
        // 也可以使用下面的形式
        Complex c3 = Complex(1.0,2.5);
       
        //    把c3的数据成员的值赋值给c1
        //    由于c1已经事先被创建,故此处不会调用任何构造函数
        //    只会调用 = 号运算符重载函数
        c1 = c3;
       
        //    调用类型转换构造函数
        //    系统首先调用类型转换构造函数,将5.2创建为一个本类的临时对象,然后调用等号运算符重载,将该临时对象赋值给c1
        c2 = 5.2;
       
       // 调用拷贝构造函数( 有下面两种调用方式)
        Complex c5(c2);
        Complex c4 = c2;  // 注意和 = 运算符重载区分,这里等号左边的对象不是事先已经创建,故需要调用拷贝构造函数,参数为c2

        
       
}

三、思考与测验

1. 仔细观察复制构造函数

        Complex(const Complex & c)
        {
                // 将对象c中的数据成员值复制过来
                m_real = c.m_real;
                m_img = c.m_img;
        }    
       
为什么函数中可以直接访问对象c的私有成员?

2. 挑战题,了解引用与传值的区别

  Complex test1(const Complex& c)
  {
          return c;
  }
 
  Complex test2(const Complex c)
  {
         return c;
   }
  
   Complex test3()
   {
          static Complex c(1.0,5.0);
          return c;
   }
 
  Complex& test4()
  {
         static Complex c(1.0,5.0);
         return c;
  }
 
  void main()
  {
        Complex a,b;
   
        // 下面函数执行过程中各会调用几次构造函数,调用的是什么构造函数?
   
       test1(a);
       test2(a);
    
       b = test3();
       b = test4();
    
       test2(1.2);
       // 下面这条语句会出错吗?
       test1(1.2);     //test1( Complex(1.2 )) 呢?
  }
 
四、附录(浅拷贝与深拷贝)
 
       上面提到,如果没有自定义复制构造函数,则系统会创建默认的复制构造函数,但系统创建的默认复制构造函数只会执行“浅拷贝”,即将被拷贝对象的数据成员的值一一赋值给新创建的对象,若该类的数据成员中有指针成员,则会使得新的对象的指针所指向的地址与被拷贝对象的指针所指向的地址相同,delete该指针时则会导致两次重复delete而出错。下面是示例:
 
【浅拷贝与深拷贝】
 
#include <iostream.h>
#include <string.h>
class Person
{
public :
       
        // 构造函数
        Person(char * pN)
        {
              cout << “一般构造函数被调用 !n”;
              m_pName = new char[strlen(pN) + 1];
              //在堆中开辟一个内存块存放pN所指的字符串
              if(m_pName != NULL)
              {
                 //如果m_pName不是空指针,则把形参指针pN所指的字符串复制给它
                   strcpy(m_pName ,pN);
              }
        }        
       
        // 系统创建的默认复制构造函数,只做位模式拷贝
        Person(Person & p)    
        {
                  //使两个字符串指针指向同一地址位置        
                 m_pName = p.m_pName;        
        }
  
        ~Person( )
        {
                delete m_pName;
        }
       
private :

        char * m_pName;
};

void main( )
{
        Person man(“lujun”);
        Person woman(man);
       
        // 结果导致   man 和    woman 的指针都指向了同一个地址
       
        // 函数结束析构时
        // 同一个地址被delete两次
}

// 下面自己设计复制构造函数,实现“深拷贝”,即不让指针指向同一地址,而是重新申请一块内存给新的对象的指针数据成员
Person(Person & chs);
{
         // 用运算符new为新对象的指针数据成员分配空间
         m_pName=new char[strlen(p.m_pName)+ 1];

         if(m_pName)        
         {
                 // 复制内容
                strcpy(m_pName ,chs.m_pName);
         }
     
        // 则新创建的对象的m_pName与原对象chs的m_pName不再指向同一地址了
}

gdb 调试程序:以段错误为例

1、生成执行程序时,加上-g选项,确保生成可执行程序含有调试信息;

2、默认情况下,很多linux发行版是禁止生成core文件的,必须修改这个默认选项:

                 ulimit -c unlimited     //unlimited 表示不限制生成的core文件的大小。

3、运行程序,产生core文件

(gdb)

(gdb) file execuatefile

(gdb)run [参数]

(gdb)core core文件

http://blog.csdn.net/yangtaolyt/article/details/6963508