Added C BLAS1 test

This commit is contained in:
Philippe Tillet
2015-06-25 23:12:26 -07:00
parent b32de3ac76
commit b0cd25ac4b
8 changed files with 167 additions and 98 deletions

View File

@@ -23,18 +23,22 @@ namespace isaac
driver::Program& init(controller<expressions_tuple> const &); driver::Program& init(controller<expressions_tuple> const &);
public: public:
model(expression_type, numeric_type, predictors::random_forest const &, std::vector< tools::shared_ptr<base> > const &, driver::CommandQueue &); model(expression_type, numeric_type, predictors::random_forest const &, std::vector< tools::shared_ptr<base> > const &, driver::CommandQueue const &);
model(expression_type, numeric_type, base const &, driver::CommandQueue &); model(expression_type, numeric_type, base const &, driver::CommandQueue const &);
void execute(controller<expressions_tuple> const &); void execute(controller<expressions_tuple> const &);
templates_container const & templates() const; templates_container const & templates() const;
void test() const
{ std::cout << queue_.device().backend() << std::endl;}
private: private:
templates_container templates_; templates_container templates_;
template_pointer fallback_; template_pointer fallback_;
tools::shared_ptr<predictors::random_forest> predictor_; tools::shared_ptr<predictors::random_forest> predictor_;
std::map<std::vector<int_t>, int> hardcoded_; std::map<std::vector<int_t>, int> hardcoded_;
std::map<driver::Context, std::map<std::string, std::shared_ptr<driver::Program> > > programs_; std::map<driver::Context, std::map<std::string, std::shared_ptr<driver::Program> > > programs_;
driver::CommandQueue & queue_; driver::CommandQueue queue_;
}; };
typedef std::map<std::pair<expression_type, numeric_type>, tools::shared_ptr<model> > model_map_t; typedef std::map<std::pair<expression_type, numeric_type>, tools::shared_ptr<model> > model_map_t;

View File

@@ -238,7 +238,7 @@ struct execution_options_type
{ {
if(queue_) if(queue_)
return *queue_; return *queue_;
return (driver::CommandQueue &)driver::queues[context][queue_id_]; return driver::queues[context][queue_id_];
} }
std::list<driver::Event>* events; std::list<driver::Event>* events;

View File

@@ -134,9 +134,11 @@ std::string Device::extensions() const
switch(backend_) switch(backend_)
{ {
#ifdef ISAAC_WITH_CUDA #ifdef ISAAC_WITH_CUDA
case CUDA: return ""; case CUDA:
return "";
#endif #endif
case OPENCL: return h_.cl->getInfo<CL_DEVICE_EXTENSIONS>(); case OPENCL:
return h_.cl->getInfo<CL_DEVICE_EXTENSIONS>();
default: throw; default: throw;
} }
} }

View File

@@ -86,12 +86,12 @@ driver::Program& model::init(controller<expressions_tuple> const & expressions)
return *program; return *program;
} }
model::model(expression_type etype, numeric_type dtype, predictors::random_forest const & predictor, std::vector< tools::shared_ptr<base> > const & templates, driver::CommandQueue & queue) : model::model(expression_type etype, numeric_type dtype, predictors::random_forest const & predictor, std::vector< tools::shared_ptr<base> > const & templates, driver::CommandQueue const & queue) :
templates_(templates), fallback_(fallbacks[std::make_pair(etype, dtype)]), predictor_(new predictors::random_forest(predictor)), queue_(queue) templates_(templates), fallback_(fallbacks[std::make_pair(etype, dtype)]), predictor_(new predictors::random_forest(predictor)), queue_(queue)
{} {}
model::model(expression_type etype, numeric_type dtype, base const & tp, driver::CommandQueue & queue) : templates_(1,tp.clone()), fallback_(fallbacks[std::make_pair(etype, dtype)]), queue_(queue) model::model(expression_type etype, numeric_type dtype, base const & tp, driver::CommandQueue const & queue) : templates_(1,tp.clone()), fallback_(fallbacks[std::make_pair(etype, dtype)]), queue_(queue)
{} {}
void model::execute(controller<expressions_tuple> const & expr) void model::execute(controller<expressions_tuple> const & expr)

View File

@@ -45,107 +45,136 @@ extern "C"
//***************** //*****************
//BLAS1 //BLAS1
//***************** //*****************
clblasStatus clblasSaxpy(size_t N, cl_float alpha,
const cl_mem mx, size_t offx, int incx, //AXPY
cl_mem my, size_t offy, int incy, #define MAKE_AXPY(TYPE_CHAR, TYPE_ISAAC, TYPE_CL) \
cl_uint numCommandQueues, cl_command_queue *commandQueues, clblasStatus clblas ## TYPE_CHAR ## axpy(size_t N, TYPE_CL alpha, \
cl_uint numEventsInWaitList, const cl_event *eventWaitList, const cl_mem mx, size_t offx, int incx, \
cl_event *events) cl_mem my, size_t offy, int incy, \
{ cl_uint numCommandQueues, cl_command_queue *commandQueues, \
is::array x(N, is::FLOAT_TYPE, cl::Buffer(mx), offx, incx); cl_uint numEventsInWaitList, const cl_event *eventWaitList, \
clRetainMemObject(mx); cl_event *events) \
is::array y(N, is::FLOAT_TYPE, cl::Buffer(my), offy, incy); { \
clRetainMemObject(my); is::array x(N, TYPE_ISAAC, cl::Buffer(mx), offx, incx); \
execute(is::detail::assign(y, x + alpha*y), y.context(), numCommandQueues, commandQueues, numEventsInWaitList, eventWaitList, events); clRetainMemObject(mx); \
return clblasSuccess; is::array y(N, TYPE_ISAAC, cl::Buffer(my), offy, incy); \
clRetainMemObject(my); \
execute(is::detail::assign(y, x + alpha*y), y.context(), numCommandQueues, commandQueues, numEventsInWaitList, eventWaitList, events); \
return clblasSuccess; \
} }
clblasStatus clblasSscal(size_t N, cl_float alpha, MAKE_AXPY(S, is::FLOAT_TYPE, cl_float)
cl_mem mx, size_t offx, int incx, MAKE_AXPY(D, is::DOUBLE_TYPE, cl_double)
cl_uint numCommandQueues, cl_command_queue *commandQueues,
cl_uint numEventsInWaitList, const cl_event *eventWaitList, cl_event *events) //SCAL
{ #define MAKE_SCAL(TYPE_CHAR, TYPE_ISAAC, TYPE_CL) \
is::array x(N, is::FLOAT_TYPE, cl::Buffer(mx), offx, incx); clblasStatus clblas ## TYPE_CHAR ## scal(size_t N, TYPE_CL alpha,\
clRetainMemObject(mx); cl_mem mx, size_t offx, int incx,\
execute(is::detail::assign(x, alpha*x), x.context(), numCommandQueues, commandQueues, numEventsInWaitList, eventWaitList, events); cl_uint numCommandQueues, cl_command_queue *commandQueues,\
return clblasSuccess; cl_uint numEventsInWaitList, const cl_event *eventWaitList, cl_event *events)\
{\
is::array x(N, TYPE_ISAAC, cl::Buffer(mx), offx, incx);\
clRetainMemObject(mx);\
execute(is::detail::assign(x, alpha*x), x.context(), numCommandQueues, commandQueues, numEventsInWaitList, eventWaitList, events);\
return clblasSuccess;\
} }
clblasStatus clblasScopy(size_t N, MAKE_SCAL(S, is::FLOAT_TYPE, cl_float)
const cl_mem mx, size_t offx, int incx, MAKE_SCAL(D, is::DOUBLE_TYPE, cl_double)
cl_mem my, size_t offy, int incy,
cl_uint numCommandQueues, cl_command_queue *commandQueues, //COPY
cl_uint numEventsInWaitList, const cl_event *eventWaitList, cl_event *events) #define MAKE_COPY(TYPE_CHAR, TYPE_ISAAC, TYPE_CL)\
{ clblasStatus clblas ## TYPE_CHAR ## copy(size_t N,\
const is::array x(N, is::FLOAT_TYPE, cl::Buffer(mx), offx, incx); const cl_mem mx, size_t offx, int incx,\
clRetainMemObject(mx); cl_mem my, size_t offy, int incy,\
is::array y(N, is::FLOAT_TYPE, cl::Buffer(my), offy, incy); cl_uint numCommandQueues, cl_command_queue *commandQueues,\
clRetainMemObject(my); cl_uint numEventsInWaitList, const cl_event *eventWaitList, cl_event *events)\
execute(is::detail::assign(y, x), y.context(), numCommandQueues, commandQueues, numEventsInWaitList, eventWaitList, events); {\
return clblasSuccess; const is::array x(N, TYPE_ISAAC, cl::Buffer(mx), offx, incx);\
clRetainMemObject(mx);\
is::array y(N, TYPE_ISAAC, cl::Buffer(my), offy, incy);\
clRetainMemObject(my);\
execute(is::detail::assign(y, x), y.context(), numCommandQueues, commandQueues, numEventsInWaitList, eventWaitList, events);\
return clblasSuccess;\
} }
clblasStatus clblasSdot(size_t N, cl_mem dotProduct, size_t offDP, MAKE_COPY(S, is::FLOAT_TYPE, cl_float)
const cl_mem mx, size_t offx, int incx, MAKE_COPY(D, is::DOUBLE_TYPE, cl_double)
const cl_mem my, size_t offy, int incy,
cl_mem /*scratchBuff*/, cl_uint numCommandQueues, //DOT
cl_command_queue *commandQueues, cl_uint numEventsInWaitList, #define MAKE_DOT(TYPE_CHAR, TYPE_ISAAC, TYPE_CL) \
const cl_event *eventWaitList, cl_event *events) clblasStatus clblas ## TYPE_CHAR ## dot(size_t N, cl_mem dotProduct, size_t offDP, \
{ const cl_mem mx, size_t offx, int incx, \
is::array x(N, is::FLOAT_TYPE, cl::Buffer(mx), offx, incx); const cl_mem my, size_t offy, int incy, \
clRetainMemObject(mx); cl_mem /*scratchBuff*/, cl_uint numCommandQueues, \
is::array y(N, is::FLOAT_TYPE, cl::Buffer(my), offy, incy); cl_command_queue *commandQueues, cl_uint numEventsInWaitList, \
clRetainMemObject(my); const cl_event *eventWaitList, cl_event *events) \
is::scalar s(is::FLOAT_TYPE, cl::Buffer(dotProduct), offDP); { \
clRetainMemObject(dotProduct); is::array x(N, TYPE_ISAAC, cl::Buffer(mx), offx, incx); \
execute(is::detail::assign(s, dot(x,y)), s.context(), numCommandQueues, commandQueues, numEventsInWaitList, eventWaitList, events); clRetainMemObject(mx); \
return clblasSuccess; is::array y(N, TYPE_ISAAC, cl::Buffer(my), offy, incy); \
clRetainMemObject(my); \
is::scalar s(TYPE_ISAAC, cl::Buffer(dotProduct), offDP); \
clRetainMemObject(dotProduct); \
execute(is::detail::assign(s, dot(x,y)), s.context(), numCommandQueues, commandQueues, numEventsInWaitList, eventWaitList, events); \
return clblasSuccess; \
} }
clblasStatus clblasSasum(size_t N, cl_mem asum, size_t offAsum, MAKE_DOT(S, is::FLOAT_TYPE, cl_float)
const cl_mem mx, size_t offx, int incx, MAKE_DOT(D, is::DOUBLE_TYPE, cl_double)
cl_mem /*scratchBuff*/, cl_uint numCommandQueues, cl_command_queue *commandQueues,
cl_uint numEventsInWaitList, const cl_event *eventWaitList, cl_event *events) //ASUM
{ #define MAKE_ASUM(TYPE_CHAR, TYPE_ISAAC, TYPE_CL) \
is::array x(N, is::FLOAT_TYPE, cl::Buffer(mx), offx, incx); clblasStatus clblas ## TYPE_CHAR ## asum(size_t N, cl_mem asum, size_t offAsum, \
clRetainMemObject(mx); const cl_mem mx, size_t offx, int incx,\
is::scalar s(is::FLOAT_TYPE, cl::Buffer(asum), offAsum); cl_mem /*scratchBuff*/, cl_uint numCommandQueues, cl_command_queue *commandQueues,\
clRetainMemObject(asum); cl_uint numEventsInWaitList, const cl_event *eventWaitList, cl_event *events)\
execute(is::detail::assign(s, sum(abs(x))), s.context(), numCommandQueues, commandQueues, numEventsInWaitList, eventWaitList, events); {\
return clblasSuccess; is::array x(N, TYPE_ISAAC, cl::Buffer(mx), offx, incx);\
clRetainMemObject(mx);\
is::scalar s(TYPE_ISAAC, cl::Buffer(asum), offAsum);\
clRetainMemObject(asum);\
execute(is::detail::assign(s, sum(abs(x))), s.context(), numCommandQueues, commandQueues, numEventsInWaitList, eventWaitList, events);\
return clblasSuccess;\
} }
MAKE_ASUM(S, is::FLOAT_TYPE, cl_float)
MAKE_ASUM(D, is::DOUBLE_TYPE, cl_double)
//***************** //*****************
//BLAS2 //BLAS2
//***************** //*****************
clblasStatus clblasSgemv(clblasOrder order, clblasTranspose transA, #define MAKE_GEMV(TYPE_CHAR, TYPE_ISAAC, TYPE_CL) \
size_t M, size_t N, clblasStatus clblas ## TYPE_CHAR ## gemv(clblasOrder order, clblasTranspose transA,\
cl_float alpha, const cl_mem mA, size_t offA, size_t lda, size_t M, size_t N,\
const cl_mem mx, size_t offx, int incx, TYPE_CL alpha, const cl_mem mA, size_t offA, size_t lda,\
cl_float beta, cl_mem my, size_t offy, int incy, const cl_mem mx, size_t offx, int incx,\
cl_uint numCommandQueues, cl_command_queue *commandQueues, TYPE_CL beta, cl_mem my, size_t offy, int incy,\
cl_uint numEventsInWaitList, const cl_event *eventWaitList, cl_event *events) cl_uint numCommandQueues, cl_command_queue *commandQueues,\
{ cl_uint numEventsInWaitList, const cl_event *eventWaitList, cl_event *events)\
//A {\
is::int_t As1 = M, As2 = N; is::int_t As1 = M, As2 = N;\
if(transA==clblasTrans) std::swap(As1, As2); if(transA==clblasTrans) std::swap(As1, As2);\
is::array A(As1, As2, is::FLOAT_TYPE, cl::Buffer(mA), offA, lda); is::array A(As1, As2, TYPE_ISAAC, cl::Buffer(mA), offA, lda);\
clRetainMemObject(mA); clRetainMemObject(mA);\
//x \
is::array x(N, is::FLOAT_TYPE, cl::Buffer(mx), offx, incx); is::array x(N, TYPE_ISAAC, cl::Buffer(mx), offx, incx);\
clRetainMemObject(mx); clRetainMemObject(mx);\
//y \
is::array y(N, is::FLOAT_TYPE, cl::Buffer(my), offy, incy); is::array y(N, TYPE_ISAAC, cl::Buffer(my), offy, incy);\
clRetainMemObject(my); clRetainMemObject(my);\
//Operation \
is::driver::Context const & context = A.context(); is::driver::Context const & context = A.context();\
if(transA==clblasTrans) if((transA==clblasTrans) ^ (order==clblasRowMajor))\
execute(is::detail::assign(y, alpha*dot(A.T(), x) + beta*y), context, numCommandQueues, commandQueues, numEventsInWaitList, eventWaitList, events); execute(is::detail::assign(y, alpha*dot(A.T(), x) + beta*y), context, numCommandQueues, commandQueues, numEventsInWaitList, eventWaitList, events);\
else else\
execute(is::detail::assign(y, alpha*dot(A, x) + beta*y), context, numCommandQueues, commandQueues, numEventsInWaitList, eventWaitList, events); execute(is::detail::assign(y, alpha*dot(A, x) + beta*y), context, numCommandQueues, commandQueues, numEventsInWaitList, eventWaitList, events);\
return clblasSuccess; return clblasSuccess;\
} }
MAKE_GEMV(S, is::FLOAT_TYPE, cl_float)
MAKE_GEMV(D, is::DOUBLE_TYPE, cl_double)
//***************** //*****************
//BLAS3 //BLAS3
//***************** //*****************

View File

@@ -6,6 +6,10 @@
typedef isaac::int_t int_t; typedef isaac::int_t int_t;
template<class T> struct BLAS;
template<> struct BLAS<float> { template<class FT, class DT> static FT F(FT SAXPY, DT ) { return SAXPY; } };
template<> struct BLAS<double> { template<class FT, class DT> static DT F(FT , DT DAXPY) { return DAXPY; } };
/*------ Simple Vector ---------*/ /*------ Simple Vector ---------*/
template<class T> template<class T>
class simple_vector_base class simple_vector_base

View File

@@ -3,6 +3,7 @@
#include "common.hpp" #include "common.hpp"
#include "isaac/array.h" #include "isaac/array.h"
#include "isaac/wrap/clBLAS.h"
namespace ad = isaac; namespace ad = isaac;
typedef ad::int_t int_t; typedef ad::int_t int_t;
@@ -14,6 +15,9 @@ void test_reduction(T epsilon, simple_vector_base<T> & cx, simple_vector_base<T
using namespace std; using namespace std;
ad::driver::Context const & ctx = x.context(); ad::driver::Context const & ctx = x.context();
int_t N = cx.size(); int_t N = cx.size();
ad::driver::CommandQueue queue = ad::driver::queues[ctx][0];
cl_command_queue clqueue = (*queue.handle().cl)();
unsigned int failure_count = 0; unsigned int failure_count = 0;
isaac::numeric_type dtype = ad::to_numeric_type<T>::value; isaac::numeric_type dtype = ad::to_numeric_type<T>::value;
@@ -23,7 +27,7 @@ void test_reduction(T epsilon, simple_vector_base<T> & cx, simple_vector_base<T
isaac::scalar ds(dtype, ctx); isaac::scalar ds(dtype, ctx);
#define RUN_TEST(NAME, CPU_REDUCTION, INIT, ASSIGNMENT, GPU_REDUCTION) \ #define RUN_TEST(NAME, CPU_REDUCTION, INIT, ASSIGNMENT, GPU_REDUCTION) \
cout << NAME "..." << flush;\ cout << PREFIX << " " << NAME "..." << flush;\
cs = INIT;\ cs = INIT;\
for(int_t i = 0 ; i < N ; ++i)\ for(int_t i = 0 ; i < N ; ++i)\
CPU_REDUCTION;\ CPU_REDUCTION;\
@@ -38,6 +42,16 @@ void test_reduction(T epsilon, simple_vector_base<T> & cx, simple_vector_base<T
else\ else\
cout << endl; cout << endl;
#define PREFIX "[C]"
RUN_TEST("DOT", cs+=cx[i]*cy[i], 0, cs, BLAS<T>::F(clblasSdot, clblasDdot)(N, (*ds.data().handle().cl)(), 0, (*x.data().handle().cl)(), x.start()[0], x.stride()[0],
(*y.data().handle().cl)(), y.start()[0], y.stride()[0],
0, 1, &clqueue, 0, NULL, NULL));
RUN_TEST("ASUM", cs+=std::fabs(cx[i]), 0, cs, BLAS<T>::F(clblasSasum, clblasDasum)(N, (*ds.data().handle().cl)(), 0, (*x.data().handle().cl)(), x.start()[0], x.stride()[0],
0, 1, &clqueue, 0, NULL, NULL));
#undef PREFIX
#define PREFIX "[C++]"
RUN_TEST("s = x'.y", cs+=cx[i]*cy[i], 0, cs, ds = dot(x,y)); RUN_TEST("s = x'.y", cs+=cx[i]*cy[i], 0, cs, ds = dot(x,y));
RUN_TEST("s = exp(x'.y)", cs += cx[i]*cy[i], 0, std::exp(cs), ds = exp(dot(x,y))); RUN_TEST("s = exp(x'.y)", cs += cx[i]*cy[i], 0, std::exp(cs), ds = exp(dot(x,y)));
RUN_TEST("s = 1 + x'.y", cs += cx[i]*cy[i], 0, 1 + cs, ds = 1 + dot(x,y)); RUN_TEST("s = 1 + x'.y", cs += cx[i]*cy[i], 0, 1 + cs, ds = 1 + dot(x,y));

View File

@@ -2,6 +2,7 @@
#include <iostream> #include <iostream>
#include "common.hpp" #include "common.hpp"
#include "isaac/array.h" #include "isaac/array.h"
#include "isaac/wrap/clBLAS.h"
namespace ad = isaac; namespace ad = isaac;
typedef isaac::int_t int_t; typedef isaac::int_t int_t;
@@ -15,7 +16,8 @@ void test_element_wise_vector(T epsilon, simple_vector_base<T> & cx, simple_vect
int failure_count = 0; int failure_count = 0;
ad::numeric_type dtype = x.dtype(); ad::numeric_type dtype = x.dtype();
ad::driver::Context const & ctx = x.context(); ad::driver::Context const & ctx = x.context();
ad::driver::CommandQueue queue = ad::driver::queues[ctx][0];
cl_command_queue clqueue = (*queue.handle().cl)();
int_t N = cz.size(); int_t N = cz.size();
T aa = 3.12, bb=3.5; T aa = 3.12, bb=3.5;
@@ -26,7 +28,7 @@ void test_element_wise_vector(T epsilon, simple_vector_base<T> & cx, simple_vect
#define CONVERT #define CONVERT
#define RUN_TEST_VECTOR_AXPY(NAME, CPU_LOOP, GPU_EXPR) \ #define RUN_TEST_VECTOR_AXPY(NAME, CPU_LOOP, GPU_EXPR) \
{\ {\
std::cout << NAME "..." << std::flush;\ std::cout << PREFIX << " " << NAME "..." << std::flush;\
for(int_t i = 0 ; i < N ; ++i)\ for(int_t i = 0 ; i < N ; ++i)\
CPU_LOOP;\ CPU_LOOP;\
GPU_EXPR;\ GPU_EXPR;\
@@ -41,8 +43,20 @@ void test_element_wise_vector(T epsilon, simple_vector_base<T> & cx, simple_vect
std::cout << std::endl;\ std::cout << std::endl;\
} }
RUN_TEST_VECTOR_AXPY("z = 0", cz[i] = 0, z = zeros(N, 1, dtype, ctx)) #define PREFIX "[C]"
RUN_TEST_VECTOR_AXPY("AXPY", cy[i] = cx[i] + a*cy[i], BLAS<T>::F(clblasSaxpy, clblasDaxpy)(N, a, (*x.data().handle().cl)(), x.start()[0], x.stride()[0],
(*y.data().handle().cl)(), y.start()[0], y.stride()[0],
1, &clqueue, 0, NULL, NULL));
RUN_TEST_VECTOR_AXPY("COPY", cy[i] = cx[i], BLAS<T>::F(clblasScopy, clblasDcopy)(N, (*x.data().handle().cl)(), x.start()[0], x.stride()[0],
(*y.data().handle().cl)(), y.start()[0], y.stride()[0],
1, &clqueue, 0, NULL, NULL));
RUN_TEST_VECTOR_AXPY("SCAL", cx[i] = a*cx[i], BLAS<T>::F(clblasSscal, clblasDscal)(N, a, (*x.data().handle().cl)(), x.start()[0], x.stride()[0],
1, &clqueue, 0, NULL, NULL));
#undef PREFIX
#define PREFIX "[C++]"
RUN_TEST_VECTOR_AXPY("z = 0", cz[i] = 0, z = zeros(N, 1, dtype, ctx))
RUN_TEST_VECTOR_AXPY("z = x", cz[i] = cx[i], z = x) RUN_TEST_VECTOR_AXPY("z = x", cz[i] = cx[i], z = x)
RUN_TEST_VECTOR_AXPY("z = -x", cz[i] = -cx[i], z = -x) RUN_TEST_VECTOR_AXPY("z = -x", cz[i] = -cx[i], z = -x)
@@ -88,8 +102,10 @@ void test_element_wise_vector(T epsilon, simple_vector_base<T> & cx, simple_vect
RUN_TEST_VECTOR_AXPY("z = x<y", cz[i] = cx[i]<cy[i], z= cast(x<y, dtype)) RUN_TEST_VECTOR_AXPY("z = x<y", cz[i] = cx[i]<cy[i], z= cast(x<y, dtype))
RUN_TEST_VECTOR_AXPY("z = x!=y", cz[i] = cx[i]!=cy[i], z= cast(x!=y, dtype)) RUN_TEST_VECTOR_AXPY("z = x!=y", cz[i] = cx[i]!=cy[i], z= cast(x!=y, dtype))
#undef RUN_TEST_VECTOR_AXPY #undef RUN_TEST_VECTOR_AXPY
if(failure_count > 0) if(failure_count > 0)
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }