LINUX.ORG.RU

Помогите с макросами

 ,


0

1

Я хочу упростить задачу формирования функции-обертки, которая в случае компиляции с поддержкой CUDA должна сначала попробовать запустить код на GPU, если не выйдет (или если скомпилировано без CUDA) — на CPU.

Для этого в файле wrapper.c я определил макросы

#ifdef CUDA_FOUND
#define SET_F(FN)  int FN{ 			\
	if(!Only_CPU) if(CU ## FN) return 1;	\
	if(CPU ## FN) return 1;			\
	return 0;				\
}
#else
#define SET_F(FN)  int FN{ 			\
	if(CPU ## FN) return 1;			\
	return 0;				\
}
#endif // CUDA_FOUND

А в файле wrapper.h (который включается в т.ч. и файлами CUDA.cu и CPU.c, содержащими функции для выполнения на GPU или на CPU) вот такие макросы:

#ifndef EXTERN
	#define EXTERN extern
#endif // EXTERN

#ifdef CPU_C // file included from CPU.c
	#define BOTH(FN) EXTERN int CPU ## FN;
#elif defined CUDA_CU //file included from CUDA.cu
	#define BOTH(FN) EXTERN int CU ## FN;
#elif defined WRAPPER_C // wrapper.c needs names of both wariants
	#ifndef CUDA_FOUND
		#define BOTH(FN) EXTERN int CPU ## FN;
	#else
		#define BOTH(FN) EXTERN int CU ## FN; EXTERN int CPU ## FN;
	#endif // CUDA_FOUND
#else // file included from something else - just define a function
	#define BOTH(FN) EXTERN int FN;
#endif

По идее, все должно работать.

Для теста сделал простую функцию fillrandarr(size_t sz, char *arr) (в дальнейшем это будет реальная функция):

CPU.c:
int CPUfillrandarr(size_t sz, char *arr __attribute__((unused))){
	printf("CPU!!!: sz = %zd\n", sz);
	return 1;
}

CUDA.cu:
EXTERN int CUfillrandarr(size_t sz, char *arr){
	printf("GPU: sz = %zd\n", sz);
	return 1;
}
и добавил соответствующие макросы в файлы:
wrapper.h:
BOTH(fillrandarr(size_t sz, char *arr))

wrapper.c:
SET_F(fillrandarr(size_t sz, char *arr))


Но, увы, компиляция не происходит: препроцессор ругается:

wrapper.c: В функции <<fillrandarr>>:
 ошибка: неявная декларация функции <<CUfillrandarr>> 
 ошибка: expected expression before <<size_t>>
 ошибка: expected expression before <<size_t>>
 ошибка: слишком мало аргументов в вызове функции <<CPUfillrandarr>>
wrapper.h:46:1: замечание: declared here
 ошибка: параметр <<sz>> не используется
 ошибка: параметр <<arr>> не используется

Чертовщина какая-то!

☆☆☆☆☆

Последнее исправление: Eddy_Em (всего исправлений: 1)

Решето Решено!!! Правда, через задницу.

Я-то, дурак, и не подумал, что функция будет вызываться так же, как в макросе определена. Потому-то и ошибки были. Как отделить тип переменных от их имен при помощи макросов я не придумал, поэтому сделал так:

#ifdef CUDA_FOUND
#define SET_F(FNARGS, FNSTRIP) int FNARGS{  	\
	if(!Only_CPU) if(CU ## FNSTRIP) return 1;\
	if(CPU ## FNSTRIP) return 1;		\
	return 0;				\
}
#else
#define SET_F(FNARGS, FNSTRIP) int FNARGS{	\
	if(CPU ## FNSTRIP) return 1;		\
	return 0;				\
}
#endif // CUDA_FOUND

SET_F(fillrandarr(size_t sz, char *arr), fillrandarr(sz, arr))

Все работает:

mkHartmann 
Make a test for allocation at least 100MB memory
В вычислениях по возможности будет использоваться GPU

Device: GeForce GT 240, totalMem=1073020928, memPerBlk=16384,
warpSZ=32, TPB=512, TBDim=512x512x64
GridSz=65535x65535x1, MemovrLap=1, GPUs=12
canMAPhostMEM=1
compute capability 1.2.

cudaMalloc(char, 229630848)
ПАМЯТЬ: свободная = 875MB, суммарная = 1023MB
Тест выделения 100МБ памяти пройден успешно


Все тесты пройдены успешно

GPU: sz = 20

Завтра буду писать инициализацию модели.

Eddy_Em ☆☆☆☆☆
() автор топика

А если просто воспользоваться препроцессором:

int fillrandarr(size_t sz, char *arr){ if(CPUfillrandarr(size_t sz, char *arr)) return 1; return 0; }

Это С или C++?

io ★★
()
Ответ на: комментарий от io

Ты не понял, зачем я эти макросы писал. У меня будет уйма функций, поэтому чтобы облегчить себе задачу, я такую фигню и придумал.

Т.е., например, если я добавлю функцию calculatenormals(float *x, float *y, float *z), то сделаю просто:

wrapper.c:
SET_F(calculatenormals(float *x, float *y, float *z), calculatenormals(x, y, z))

wrapper.h:
BOTH(calculatenormals(float *x, float *y, float *z))

CPU.c:
int CPUcalculatenormals(float *x, float *y, float *z){
    // код
}

CUDA.cu:

int CUcalculatenormals(float *x, float *y, float *z){
    // код
}

Естественно, это С. Я других языков не знаю и знать не хочу.

Eddy_Em ☆☆☆☆☆
() автор топика
Последнее исправление: Eddy_Em (всего исправлений: 2)
Ответ на: комментарий от Eddy_Em

Да все я понял. Есть такая штука - шутка юмора. Препроцессор все едино никто не отменял.

io ★★
()

Дополнение

вот я подумал: а вдруг можно как-то это покрасивше сделать? Кто знает ответ, поделитесь, пожалуйста, на stackoverflow.

Eddy_Em ☆☆☆☆☆
() автор топика
Ответ на: Дополнение от Eddy_Em

Это легко сделать, если у тебя будут обобщённые аргументы у функций или хотя бы ограниченный набор характерных аргументов.

vtVitus ★★★★★
()
Ответ на: комментарий от Eddy_Em

можно конструктор сделать

#include "stdio.h"

#define magic_flag 1

#define SET_COMMON_F(FN) inline int FN(void ** args, size_t sz) {          \
        return (magic_flag) ? CU ## FN(args, sz) : CPU ## FN(args, sz);  \
}

#define SET_F1(FN, T) inline int FN(T t) {                               \
        return (magic_flag) ? CU ## FN( t ) : CPU ## FN( t );            \
}

#define SET_F2(FN, T1, T2) inline int FN(T1 t1, T2 t2) {                  \
        return (magic_flag) ? CU ## FN( t1, t2 ) : CPU ## FN( t1, t2 );   \
}

int CUcommon_function(void ** t, size_t r) {
        printf("CUcommon_function %p, %d\n", t, r);
}

int CPUcommon_function(void ** t, size_t r) {
        printf("CPUcommon_function %p, %d\n", t, r);
}

int CUf1_function(int f) {
        printf("CUf1_function %d\n", f);
}

int CPUf1_function(int f) {
        printf("CPUf1_function %d\n", f);
}

int CUf2_function(int f, void *d) {
        printf("CUf2_function %d, %p\n", f, d);
}

int CPUf2_function(int f, void *d) {
        printf("CPUf2_function %d, %p\n", f, d);
}

SET_COMMON_F(common_function)

SET_F1(f1_function, int)

SET_F2(f2_function, int , void *)

int main(char ** args, int size) {
        common_function(NULL, 0);
        f1_function(0);
        f2_function(0, NULL);
        return 0;
}

vtVitus ★★★★★
()
Ответ на: комментарий от Eddy_Em

Какой привередливый :) Тут либо конструктор (выше) либо вызов методов через define, другого вроде не дано

#include "stdio.h"

#define magic_flag 1

#ifdef magic_flag
        #define CALL_F(FN, ... ) CU ## FN( __VA_ARGS__ )
#else
        #define CALL_F(FN, ... ) CPU ## FN( __VA_ARGS__ )
#endif

int CUcommon_function(void ** t, size_t r) {
        printf("CUcommon_function %p, %d\n", t, r);
        return 1;
}

int CPUcommon_function(void ** t, size_t r) {
        printf("CPUcommon_function %p, %d\n", t, r);
        return 1;
}

int CUf1_function(int f) {
        printf("CUf1_function %d\n", f);
        return 1;
}

int CPUf1_function(int f) {
        printf("CPUf1_function %d\n", f);
        return 1;
}

int CUf2_function(int f, void *d) {
        printf("CUf2_function %d, %p\n", f, d);
        return 0;
}

int CPUf2_function(int f, void *d) {
        printf("CPUf2_function %d, %p\n", f, d);
        return 0;
}

int main(char ** args, int size) {
        if (CALL_F(common_function, NULL, 0)) {
                printf("OK\n");
        }
        CALL_F(f1_function, 0);
        if (!CALL_F(f2_function, 0, NULL)) {
                printf("failed\n");
        }
        return 0;
}

vtVitus ★★★★★
()
Ответ на: комментарий от Eddy_Em

Упростил-таки! На сосфорже натолкнули на хорошую идею.

Получилось так (весь код теперь в заголовочном файле, а в сишном — только общее):

#define Fn1(A,B) A(x1)
#define Df1(A,B) A(B x1)
#define Fn2(A,B,C) A(x1, x2)
#define Df2(A,B,C) A(B x1, C x2)
#define Fn3(A,B,C,D) A(x1, x2, x3)
#define Df3(A,B,C,D) A(B x1, C x2, D x3)
#define Fn4(A,B,C,D,E) A(x1, x2, x3, x4)
#define Df4(A,B,C,D,E) A(B x1, C x2, D x3, E x4)
#define Fn5(A,B,C,D,E,F) A(x1, x2, x3, x4, x5)
#define Df5(A,B,C,D,E,F) A(B x1, C x2, D x3, E x4, F x5)
#define Fn6(A,B,C,D,E,F,G) A(x1, x2, x3, x4, x5, x6)
#define Df6(A,B,C,D,E,F,G) A(B x1, C x2, D x3, E x4, F x5, G x6)
#define Fn7(A,B,C,D,E,F,G,H) A(x1, x2, x3, x4, x5, x6, x7)
#define Df7(A,B,C,D,E,F,G,H) A(B x1, C x2, D x3, E x4, F x5, G x6, H x7)
#define Fn8(A,B,C,D,E,F,G,H,I) A(x1, x2, x3, x4, x5, x6, x7, x8)
#define Df8(A,B,C,D,E,F,G,H,I) A(B x1, C x2, D x3, E x4, F x5, G x6, H x7, I x8)
#define Fn9(A,B,C,D,E,F,G,H,I,J) A(x1, x2, x3, x4, x5, x6, x7, x8, x9)
#define Df9(A,B,C,D,E,F,G,H,I,J) A(B x1, C x2, D x3, E x4, F x5, G x6, H x7, I x8, J x9)
#define Fn10(A,B,C,D,E,F,G,H,I,J,K) A(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10)
#define Df10(A,B,C,D,E,F,G,H,I,J,K) A(B x1, C x2, D x3, E x4, F x5, G x6, H x7, I x8, J x9, K x10)

#define DEF(N, ...) int Df ## N(__VA_ARGS__)
#define CONCAT(A, B) A ## B
#define FN(N, ...) Fn ## N(__VA_ARGS__)
#define DF(N, ...) Df ## N(__VA_ARGS__)
#define XFUNC(T, X) CONCAT(T, X)
#define FUNC(T, ...) XFUNC(T, FN(__VA_ARGS__))
#define DFUNC(T,...) EXTERN int XFUNC(T, DF(__VA_ARGS__))

#ifdef WRAPPER_C
// even when using cuda in case of fail CUDA init use CPU
static int Only_CPU =
#ifdef CUDA_FOUND
	0
#else
	1
#endif
;
#ifdef CUDA_FOUND
#define SET_F(...) DEF(__VA_ARGS__){					\
	if(!Only_CPU) if(FUNC(CU, __VA_ARGS__)) return 1;	\
	if(FUNC(CPU, __VA_ARGS__)) return 1;				\
	return 0;											\
}
#else
#define SET_F(...) DEF(__VA_ARGS__){					\
	if(FUNC(CPU, __VA_ARGS__)) return 1;				\
	return 0;											\
}
#endif // CUDA_FOUND
#else
	#define SET_F(...)
#endif // WRAPPER_C

#ifdef CPU_C // file included from CPU.c
	#define BOTH(...) DFUNC(CPU, __VA_ARGS__);
	//#pragma message "CPUC"
#elif defined CUDA_CU //file included from CUDA.cu
	#define BOTH(...) DFUNC(CU, __VA_ARGS__);
#elif defined WRAPPER_C // wrapper.c needs names of both wariants
	#ifndef CUDA_FOUND
		#define BOTH(...) DFUNC(CPU, __VA_ARGS__);
	#else
		#define BOTH(...) DFUNC(CU, __VA_ARGS__); DFUNC(CPU, __VA_ARGS__);
	#endif // CUDA_FOUND
#else // file included from something else - just define a function
	#define BOTH(...) DFUNC(, __VA_ARGS__);
#endif

#define DFN(...) BOTH(__VA_ARGS__) SET_F(__VA_ARGS__)


DFN(2, fillrandarr, size_t, float *)

DFN(6, bicubic_interp, float *, float *, size_t, size_t, size_t, size_t)

Макрос DFN объявляет (и определяет, если заголовочный файл включен из wrapper.c) функцию (второй аргумент макроса) с количеством аргументов, равным первому аргументу макроса и типами аргументов — остальными аргументами макроса. P.S. Если надо больше 10 аргументов — дописать соответствующие макросы DfXXX и FnXXX

Eddy_Em ☆☆☆☆☆
() автор топика
Последнее исправление: Eddy_Em (всего исправлений: 1)

Интересная штука получается: ГСЧ на куде какой-то очень тормозной. Зато время заполнения массива любого размера (в пределах на сколько памяти хватит) почти константно (хотя по идее из-за копирования памяти должно возрастать) ☺

Eddy_Em ☆☆☆☆☆
() автор топика
Последнее исправление: Eddy_Em (всего исправлений: 1)
#define Fn1(A,B) A(x1)
#define Df1(A,B) A(B x1)
...
#define Fn10(A,B,C,D,E,F,G,H,I,J,K) A(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10)
#define Df10(A,B,C,D,E,F,G,H,I,J,K) A(B x1, C x2, D x3, E x4, F x5, G x6, H x7, I x8, J x9, K x10)

Какой-то это костыль, имхо. Первоначальный вариант был более эстетичный, что ли...

Хотя Вам, конечно, виднее. Да и главное, чтобы удобно использовать было =)

solovey ★★
()
Ответ на: комментарий от solovey

В первоначальном варианте много надо было писать для определения функции:

SET_F(fillrandarr(size_t sz, char *arr), fillrandarr(sz, arr))
Если аргументов много, получается что-то страшное.

А теперь кратко и понятно:

DFN(2, fillrandarr, size_t, float *)

Мне, кстати, на stackoverflow еще вариант подсказали: с использованием буста. Тоже работает.

Eddy_Em ☆☆☆☆☆
() автор топика
Последнее исправление: Eddy_Em (всего исправлений: 1)
Ответ на: комментарий от Eddy_Em

с использованием буста

Только си, только хардкор, разве нет =)

По сабжу - да, в первом случае при 10 аргументах получилось бы совсем нечитаемо...

solovey ★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.