Does GSL have thread-safety issues when it comes to using function pointers? The attached openmp code integrates f(x)=-(c+x)^{-1} over the range 1<=x<=2 for various values of c using gsl's gsl_integration_qng function. The parallel version runs much slower than the serial version. I suspect that this has to do with the function pointer &fx. Does anyone have prior experience with this problem? Thanks in advance!
#include<cstdlib>
#include <gsl/gsl_integration.h>
#include<cstdio>
#include<omp.h>
double fx(double x,void *p);
double evalintegral(double c);
using namespace std;
int main(int argc, char *argv[])
{
// numerically integrate the function f(x) = -(c+x)^{-1} between 1 and 2
int Ncs = atoi(argv[1]);
int Nreps = atoi(argv[2]);
printf("Ncs=%d, Nreps=%d.\n",Ncs,Nreps);
int i, j;
double tempF;
double *cs = new double[Ncs];
double dc = 1 / (double)(Ncs-1);
for (i = 0; i < Ncs; i++)
{
cs[i] = dc*(double)i;
}
printf("Began integrations.\n");
#pragma omp parallel for default(none)\
shared(Nreps,Ncs,cs)\
private(i,j,tempF)
for (i = 0; i < Nreps; i++)
{
for (j = 0; j < Ncs; j++)
{
tempF = evalintegral(cs[j]);
}
}
delete[] cs;
printf("Finished integrations.\n");
return 0;
}
double fx(double x, void *p)
{
double *c = (double*) p;
return -1 / (*c + x);
}
double evalintegral(double c)
{
double *ptr_c = new double[1];
ptr_c[0] = c;
gsl_function Fquad;
Fquad.params = ptr_c;
Fquad.function = &fx;
size_t quadneval;
double quadres, quaderr;
gsl_integration_qng(&Fquad,1,2,1e-10,1e-6,&quadres,&quaderr,&quadneval);
delete[] ptr_c;
return quadres;
}
I faced the same issue with an GSL integral routine. As #mrx_hk mentioned the problem was reusing the workspace object.
In my case I stored:
gsl_integration_workspace * w_ = gsl_integration_workspace_alloc(1200);
as a class member variable and reused the storage on every integration call, which caused a race condition.
The solution was to create a workspace object for every thread, in my case for every integration call.
Related
I am kind of new to C++ and I was doing a physics simulation in python which was taking forever to finish so I decided to switch to C++, and I don t understand how to make a function which will return a 2D array (or 3D array)
#include <iostream>
#include <cmath>
// #include <complex> //
using namespace std;
double** psiinit(int L, int n, double alpha){
double yj[400][400] = {};
for (int j = 0; j < n; j++)
{
double xi[400] = {};
for (int i = 0; i < n; i++)
{
xi[i] = exp(-(pow((i-(L/4)), 2) + (pow((j-(L/4)), 2)))/alpha) / (sqrt(2)*3.14159*alpha);
};
yj[j] = xi;
};
return yj;
}
int main(){
int L = 10;
int n = 400;
int nt = 200*n;
double alpha = 1;
double m = 1;
double hbar = 1;
double x[n] = {};
double y[n] = {};
double t[nt] = {};
double psi[nt][n][n] = {};
psi[0] = psiinit(L, n, alpha);
cout << psi <<endl;
return 0;
}
I have look for answers but it doesn't seems to be for my kind of problems
Thanks
If you're new to c++ you should read about the concepts of heap and stack, and about stack frames. There are a ton of good resources for that.
In short, when you declare a C-style array (such as yj), it is created in the stack frame of the function, and therefore there are no guarantees about it once you exit the frame, and your program invokes undefined behavior when it references that returned array.
There are 3 options:
Pass the array to the function as an output parameter (very C-style and not recommended).
Wrap the array in a class (like std::array already does for you), in which case it remains on the stack and is copied to the calling frame when returned, but then its size has to be known at compile time.
Allocate the array on the heap and return it, which seems to me to best suit your case. std::vector does that for you:
std::vector<std::vector<double>> psiinit(int L, int n, double alpha){
std::vector<std::vector<double>> yj;
for (int j = 0; j < n; j++)
{
std::vector<double> xi;
for (int i = 0; i < n; i++)
{
const int value = exp(-(pow((i-(L/4)), 2) + (pow((j-(L/4)), 2)))/alpha) / (sqrt(2)*3.14159*alpha);
xi.push_back(value);
}
yj.push_back(xi);
}
return yj;
}
If you're concerned with performance and all of your inner vectors are of a fixed size N, it might be better to use std::vector<std::array<double, N>>.
Either make a wrapper as said above, or use a vector of vectors.
#include <vector>
#include <iostream>
auto get_2d_array()
{
// use std::vector since it will allocate (the large amount of) data on the heap
// construct a vector of 400 vectors with 400 doubles each
std::vector<std::vector<double>> arr(400, std::vector<double>(400));
arr[100][100] = 3.14;
return arr;
}
int main()
{
auto arr = get_2d_array();
std::cout << arr[100][100];
}
Your understanding of arrays, pointers and return values is incomplete. I cannot write you a whole tutorial on the topic but I recommend you read up on this.
In the mean time, I recommend you use std::vector instead of C-style arrays and treat your multidimensional arrays as 1D vectors with proper indexing, e.g. cell = vector[row * cols + col]
Something like this:
#include <cmath>
// using std::exp, M_PI, M_SQRT2
#include <vector>
std::vector<double> psiinit(int L, int n, double alpha) {
std::vector<double> yj(n * n);
double div = M_SQRT2 * M_PI * alpha;
for (int j = 0; j < n; j++)
{
double jval = j - L/4;
jval = jval * jval;
for (int i = 0; i < n; i++)
{
double ival = i - L/4;
ival = ival * ival;
yj[j * n + i] = std::exp(-(ival + jval) / alpha) / div;
}
}
return yj;
}
Addendum: There are also specialized libraries to support matrices better and faster. For example Eigen
https://eigen.tuxfamily.org/dox/GettingStarted.html
heap allocating and returning that pointer will also work...
instead of
double yj[400][400] = {};
do,
double** yj;
yj = new double*[400];
yj[i] = new double[400];
then just,
return yj;
I'm attempting to speed up a chunk of code with multi-threading for a project I've been working on. The error I keep getting is this:
"error: invalid use of non-static member function"
I have attempted using std::ref() on all of my parameters.
I've also looked at making the method I'm running on individual threads static, but I found that that would change too much of the code base.
I found a little on std::bind() it was less than helpful.
I did some research on p-threads and was told that they would be just as difficult to manage. This is my first large multi-threading project, but I have many working samples that I have been trying to implement in a similar fashion.
Any new ideas would be welcome. I am sure I'm doing something off in left field, I don't primarily code in C++.
Updated >>>
I've got it to compile now, but it's not doing much, I was informed that I need to implement a queue to handle the data going in, I'm going to attempt that now, just thought I would update this post. I will update again if/when I run into more problems, or solve it myself. Thank you all for the suggestions. I hope to be able to leave this here as an example for anyone who may run into the same problems in the future.
#include <thread>
#include "someclass.h"
void SomeClass::foo(const BigClass &a, int b, int c, double d, double e, unsigned int f, unsigned int g, unsigned int num_threads, std::vector<Chunk> p_vect)
{
std::vector<std::thread> thread_vect;
unsigned int thread_spread = g / num_threads;
unsigned int new_g = f + thread_spread - 1;
for (unsigned int x = 0; x < num_threads; x++)
{
thread_vect.push_back(std::thread(SomeClass::bar, &a, std::ref(b), std::ref(c), std::ref(d), std::ref(e)));
f += thread_spread;
newg += thread_spread;
}
for (auto &t : thread_vect)
{
t.join();
}
}
Chunk SomeClass::bar(const BigClass &a, int b, int c, double d, double e)
{
BigClass my_a(a);
int i;
for (i = 0; i < (int)moves.size(); i++)
{
BigClass tmp_a(a);
Chunk tmp_p_data = this->baz(tmp_a, b, c + 1, d, e);
if (tmp_p_data.utility < p_data.utility)
{
p_data.utility = tmp_p_data.utility;
p_data.move = moves[i];
p_data.move_ok = true;
if (p_data.utility <= d)
{
break;
}
if (p_data.utility < e)
{
e = p_data.utility;
}
}
}
return p_data;
}
updated >>>
#include <thread>
#include "someclass.h"
void SomeClass::foo(const BigClass &a, int b, int c, double d, double e, unsigned int f, unsigned int g, unsigned int num_threads, std::vector<Chunk> p_vect)
{
std::vector<std::thread> threads;
unsigned int x;
for (x = 0; x < max_threads; x++)
{
threads.push_back(std::thread(&SomeClass::bar, this, std::ref(a), std::ref(b), std::ref(c), std::ref(d), std::ref(e)));
}
for (x = 0; x < max_threads; x++)
{
threads[x].join();
}
}
Chunk SomeClass::bar(const BigClass &a, int b, int c, double d, double e)
{
BigClass my_a(a);
int i;
for (i = 0; i < (int)moves.size(); i++)
{
BigClass tmp_a(a);
Chunk tmp_p_data = this->baz(tmp_a, b, c + 1, d, e);
if (tmp_p_data.utility < p_data.utility)
{
p_data.utility = tmp_p_data.utility;
p_data.move = moves[i];
p_data.move_ok = true;
if (p_data.utility <= d)
{
break;
}
if (p_data.utility < e)
{
e = p_data.utility;
}
}
}
return p_data;
}
I expect to be able to spawn multiple threads on the same method with different inputs at the roots of a large tree of data that I'm traversing.
Currently I'm compiling, but the program never finishes. It does spawn multiple threads, but I'm not currently handling the data correctly as of yet.
Each traversal can take from < 1 second to > 45 seconds depending on pruning. If I end up running > 20 seconds a branch, the project I'm working on will not meet the project requirements.
I am trying to understand the difference between AoS and SoA in practical terms.
I've tried this in C# and it yielded no result, so now I'm trying in C++.
#include <stdlib.h>
#include <chrono>
#include <iostream>
#include <math.h>
const int iterations = 40000000;
class Entity {
public:
float a, b, c;
};
struct Entities {
public:
float a[iterations];
float b[iterations];
float c[iterations];
};
void AoSTest(int iterations, Entity enArr[]);
void SoATest(int iterations, Entities* entities);
int main()
{
Entity* enArr = new Entity[iterations];
Entities* entities = new Entities;
int A = rand() - 50;
int B = rand() - 50;
int C = rand() - 50;
for (int i = 0; i < iterations; i++)
{
enArr[i].a = A;
enArr[i].b = B;
enArr[i].c = C;
entities->a[i] = A;
entities->b[i] = B;
entities->c[i] = C;
}
auto start = std::chrono::high_resolution_clock::now();
AoSTest(iterations, enArr);
//SoATest(iterations, entities);
auto finish = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> elapsed = finish - start;
//std::cout << std::to_string(elapsed.count()) + "time";
std::cout << std::to_string(std::chrono::duration_cast<std::chrono::seconds>(finish - start).count()) + "s";
}
void AoSTest(int iterations, Entity enArr[]) {
for (int i = 0; i < iterations; i++)
{
enArr[i].a = sqrt(enArr[i].a * enArr[i].c);
enArr[i].c = sqrt(enArr[i].c * enArr[i].a);
//std::cout << std::to_string(sqrt(enArr[i].a) + sqrt(enArr[i].b)) + "\n";
}
}
void SoATest(int iterations, Entities* entities) {
for (int i = 0; i < iterations; i++)
{
entities->a[i] = sqrt(entities->a[i] * entities->c[i]);
entities->c[i] = sqrt(entities->c[i] * entities->a[i]);
//std::cout << std::to_string(sqrt(entities->a[i]) + sqrt(entities->b[i])) + "\n";
}
}
My thought was that since data layout, in theory, should be different there should be a performance difference...
I don't understand why some say that there is a lot to gain here if it's so context sensitive as it seems to me thus far.
Is it completely dependent on SIMD or some specific optimization option?
I'm running it in Visual Studio.
I compiled your code with the intel compiler 18.0.1 and optimisation turned on (-O3). I added some return value, just to make sure nothing can be optimised away.
I found that Structure of Arrays (SoA) is approx twice as fast as Array of Structures (AoS). This makes sence, since the quantity B will not be loaded into the cache from the slow memory (RAM) if you use the SoA approch, but it will occupy cache with the AoS approach. Please not that I changed the time resolution to nanoseconds. Otherwise, I always get 0s as output.
#include <stdlib.h>
#include <chrono>
#include <iostream>
#include <math.h>
const int iterations = 40000000;
class Entity {
public:
float a, b, c;
};
struct Entities {
public:
float a[iterations];
float b[iterations];
float c[iterations];
};
int AoSTest(int iterations, Entity enArr[]);
int SoATest(int iterations, Entities* entities);
int main() {
Entity* enArr = new Entity[iterations];
Entities* entities = new Entities;
int A = rand() - 50;
int B = rand() - 50;
int C = rand() - 50;
for (int i = 0; i < iterations; i++) {
enArr[i].a = A;
enArr[i].b = B;
enArr[i].c = C;
entities->a[i] = A;
entities->b[i] = B;
entities->c[i] = C;
}
auto start = std::chrono::high_resolution_clock::now();
// const auto ret = AoSTest(iterations, enArr);
const auto ret = SoATest(iterations, entities);
auto finish = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> elapsed = finish - start;
std::cout << std::to_string(std::chrono::duration_cast<std::chrono::nanoseconds>(finish - start).count()) + "ns "
<< "ret=" << ret;
}
int AoSTest(int iterations, Entity enArr[]) {
for (int i = 0; i < iterations; i++) {
enArr[i].a = sqrt(enArr[i].a * enArr[i].c);
enArr[i].c = sqrt(enArr[i].c * enArr[i].a);
}
return enArr[iterations - 1].c;
}
int SoATest(int iterations, Entities* entities) {
for (int i = 0; i < iterations; i++) {
entities->a[i] = sqrt(entities->a[i] * entities->c[i]);
entities->c[i] = sqrt(entities->c[i] * entities->a[i]);
}
return entities->c[iterations - 1];
}
SoA is beneficial to load or store your data with SIMD intrinsics, see e.g. https://software.intel.com/sites/landingpage/IntrinsicsGuide/#techs=AVX&cats=Load&expand=3317 for intel AVX.
Now in your specific case, you need to provide more information regarding the compiler options etc., but probably your specific case is not easy for the compiler to vectorize.
I would suggest that you use independent instructions for each entry (here c depends on a) to perform more tests.
I wrote a multithreaded simulated annealing program but its not running. I am not sure if the code is correct or not. The code is able to compile but when i run the code it crashes. Its just a run time error.
#include <stdio.h>
#include <time.h>
#include <iostream>
#include <stdlib.h>
#include <math.h>
#include <string>
#include <vector>
#include <algorithm>
#include <fstream>
#include <ctime>
#include <windows.h>
#include <process.h>
using namespace std;
typedef vector<double> Layer; //defines a vector type
typedef struct {
Layer Solution1;
double temp1;
double coolingrate1;
int MCL1;
int prob1;
}t;
//void SA(Layer Solution, double temp, double coolingrate, int MCL, int prob){
double Rand_NormalDistri(double mean, double stddev) {
//Random Number from Normal Distribution
static double n2 = 0.0;
static int n2_cached = 0;
if (!n2_cached) {
// choose a point x,y in the unit circle uniformly at random
double x, y, r;
do {
// scale two random integers to doubles between -1 and 1
x = 2.0*rand()/RAND_MAX - 1;
y = 2.0*rand()/RAND_MAX - 1;
r = x*x + y*y;
} while (r == 0.0 || r > 1.0);
{
// Apply Box-Muller transform on x, y
double d = sqrt(-2.0*log(r)/r);
double n1 = x*d;
n2 = y*d;
// scale and translate to get desired mean and standard deviation
double result = n1*stddev + mean;
n2_cached = 1;
return result;
}
} else {
n2_cached = 0;
return n2*stddev + mean;
}
}
double FitnessFunc(Layer x, int ProbNum)
{
int i,j,k;
double z;
double fit = 0;
double sumSCH;
if(ProbNum==1){
// Ellipsoidal function
for(j=0;j< x.size();j++)
fit+=((j+1)*(x[j]*x[j]));
}
else if(ProbNum==2){
// Schwefel's function
for(j=0; j< x.size(); j++)
{
sumSCH=0;
for(i=0; i<j; i++)
sumSCH += x[i];
fit += sumSCH * sumSCH;
}
}
else if(ProbNum==3){
// Rosenbrock's function
for(j=0; j< x.size()-1; j++)
fit += 100.0*(x[j]*x[j] - x[j+1])*(x[j]*x[j] - x[j+1]) + (x[j]-1.0)*(x[j]-1.0);
}
return fit;
}
double probl(double energychange, double temp){
double a;
a= (-energychange)/temp;
return double(min(1.0,exp(a)));
}
int random (int min, int max){
int n = max - min + 1;
int remainder = RAND_MAX % n;
int x;
do{
x = rand();
}while (x >= RAND_MAX - remainder);
return min + x % n;
}
//void SA(Layer Solution, double temp, double coolingrate, int MCL, int prob){
void SA(void *param){
t *args = (t*) param;
Layer Solution = args->Solution1;
double temp = args->temp1;
double coolingrate = args->coolingrate1;
int MCL = args->MCL1;
int prob = args->prob1;
double Energy;
double EnergyNew;
double EnergyChange;
Layer SolutionNew(50);
Energy = FitnessFunc(Solution, prob);
while (temp > 0.01){
for ( int i = 0; i < MCL; i++){
for (int j = 0 ; j < SolutionNew.size(); j++){
SolutionNew[j] = Rand_NormalDistri(5, 1);
}
EnergyNew = FitnessFunc(SolutionNew, prob);
EnergyChange = EnergyNew - Energy;
if(EnergyChange <= 0){
Solution = SolutionNew;
Energy = EnergyNew;
}
if(probl(EnergyChange ,temp ) > random(0,1)){
//cout<<SolutionNew[i]<<endl;
Solution = SolutionNew;
Energy = EnergyNew;
cout << temp << "=" << Energy << endl;
}
}
temp = temp * coolingrate;
}
}
int main ()
{
srand ( time(NULL) ); //seed for getting different numbers each time the prog is run
Layer SearchSpace(50); //declare a vector of 20 dimensions
//for(int a = 0;a < 10; a++){
for (int i = 0 ; i < SearchSpace.size(); i++){
SearchSpace[i] = Rand_NormalDistri(5, 1);
}
t *arg1;
arg1 = (t *)malloc(sizeof(t));
arg1->Solution1 = SearchSpace;
arg1->temp1 = 1000;
arg1->coolingrate1 = 0.01;
arg1->MCL1 = 100;
arg1->prob1 = 3;
//cout << "Test " << ""<<endl;
_beginthread( SA, 0, (void*) arg1);
Sleep( 100 );
//SA(SearchSpace, 1000, 0.01, 100, 3);
//}
return 0;
}
Please help.
Thanks
Avinesh
As leftaroundabout pointed out, you're using malloc in C++ code. This is the source of your crash.
Malloc will allocate a block of memory, but since it was really designed for C, it doesn't call any C++ constructors. In this case, the vector<double> is never properly constructed. When
arg1->Solution1 = SearchSpace;
Is called, the member variable "Solution1" has an undefined state and the assignment operator crashes.
Instead of malloc try
arg1 = new t;
This will accomplish roughly the same thing but the "new" keyword also calls any necessary constructors to ensure the vector<double> is properly initialized.
This also brings up another minor issue, that this memory you've newed also needs to be deleted somewhere. In this case, since arg1 is passed to another thread, it should probably be cleaned up like
delete args;
by your "SA" function after its done with the args variable.
While I don't know the actual cause for your crashes I'm not really surprised that you end up in trouble. For instance, those "cached" static variables in Rand_NormalDistri are obviously vulnerable to data races. Why don't you use std::normal_distribution? It's almost always a good idea to use standard library routines when they're available, and even more so when you need to consider multithreading trickiness.
Even worse, you're heavily mixing C and C++. malloc is something you should virtually never use in C++ code – it doesn't know about RAII, which is one of the few intrinsically safe things you can cling onto in C++.
Here's the code that I'm currently using
template <class T>
float Knapsack<T>::knapTable()
{
const int MAXSIZE = 40000;
int temps = nObjects - 1;
float** memoize = new float*[MAXSIZE];
for(int y = 0; y < weight[nObjects]; y++)
memoize[nObjects][y] = 0;
for(int y = weight[nObjects]; y < knapCap; y++)
memoize[nObjects][y] = price[y];
for(int i = temps; i >= 1; i--)
{
for(int y = weight[i]; y < knapCap; y++)
memoize[i][y]= max(memoize[i+1][y], (memoize[i+1][y-weight[i]]+price[i]));
}
return memoize[1][nObjects];
}
For some reason I keep getting the error: knapsack.hpp:68:64: error: invalid types ‘float*[float]’ for array subscript.
That's this line: float** memoize = new float*[MAXSIZE];
For some reason the compiler seems to be recognizing MAXSIZE as a float, it's a const int.
Is there a way I can fix this?
Edited for more code
header file
#ifndef KNAPSACK_H
#define KNAPSACK_H
#include <stdexcept>
#include <assert.h>
#include <iostream>
#include <limits.h>
using namespace std;
template <class T>
class Knapsack
{
private:
float knapPrice;
int knapCap, nObjects;
float weight[40000];
float price[40000];
public:
Knapsack(): knapPrice(0), knapCap(0), nObjects(0) {}
~Knapsack() {knapPrice = 0; knapCap = 0;}
float knapFull (int position, int currentCap);
float knapTable ();
float greedyKnap (int currentCap);
float max(float noAdd,float addOb);
void printPrice();
//valueized and valued are modified versions of mergeSort and merge
//designed to sort two arrays by a fraction of the two.
void valueize(int ini, int last);
void valued(int ini, int middle, int last);
void fillWandP();
void setNObjects(int n);
void setKnapCap(int boom);
};
#include "knapsack.hpp"
#endif
Main function //Though I don't think this would affect it
#include "sortClass.h"
#include "knapsack.h"
#include
#include
#include
#include
using namespace std;
//mergeSort main;
int main()
{
Knapsack<float> a;
float sacked = 0;
int nO = 18;
int cap = 700;
a.setNObjects(nO);
a.setKnapCap(cap);
a.fillWandP();
for(int b = 0; b <3800000; b++)//for getting good times
sacked = b;
int startAll = clock()*1000000;
sacked = a.knapFull(1, cap);
int knapped = clock()*1000000;
int boom = a.knapTable();
int tabled = clock()*1000000;
a.valueize(1, cap);
int andDone = a.greedyKnap(cap);
int greedified = clock()*1000000;
cout<<startAll<<endl;
greedified = greedified - tabled;
tabled = tabled - knapped;
knapped = knapped - startAll;
cout<<"Recursion profit:"<<sacked<<" Time: "<<knapped<<endl;
cout<<"Memoization profit:"<<boom<<" Time: "<<tabled<<endl;
cout<<"Greedy profit: "<<andDone<<" Time: "<<greedified<<endl;
return 0;
}
weight is declared as float weight[40000] in class Knapsack.
You then use an element of weight as an index into memoize in the knaptable() function:
memoize[i][y]= max(memoize[i+1][y], (memoize[i+1][y-weight[i]]+price[i]));
// ^^^^^^^^^
And for the record, that's the line that the error is produced for by g++ 4.6.1; it doesn't point to the line where memoize is declared.
Not necessarily related, but you're not using your arrays/pointers correctly. You create your first level of pointers when you call float** memoize = new float*[MAXSIZE] but you then just have an array of pointers, not a double array. You need to initialize each of memoize[i] as an array as well.
That being said, it doesn't look like you should be allocating memory for your memoize array anyway. Just declare it as
float memoize[SIZE][SIZE];
That way, you won't have to worry about memory cleanup or anything, and it makes a lot more sense.
for(int i = temps; i >= 1; i--)
{
for(int y = weight[i]; y < knapCap; y++)
memoize[i][y]= max(memoize[i+1][y], (memoize[i+1][y-weight[i]]+price[i]));
}
y-weight[i] is a float. This is your problem.
Once you fix this you will discover that you still have an issue, you're allocating an array of pointers but you also need to allocate the second dimension for each of those pointers before you can use that array.
Something along the lines of:
float** memoize = new float*[MAXSIZE];
for(size_t i = 0; i < MAXSIZE; ++i)
{
memoize[i] = new float[MAXSIZE];
}
i think maybe you just need to allocate memory for the second pointer ,something like
float** memoize = new float*[MAXSIZE];
memoize=(float**)malloc(sizeof(float*)*MAXSIZE);
for(int i=0;i<MAXSIZE;i++)
{
memoize[i]=(float*)malloc(sizeof(float)*MAXSIZE);
}