fft_param.cc

Go to the documentation of this file.
00001 #ifndef _fft_param_file
00002 #define _fft_param_file
00003 
00009 #include "utils.H"
00010 #include <sstream>
00011 
00012 
00013 #if HAVE_LINUX_SYSINFO
00014 
00015 // only guaranteed to work under Linux, since we read kernel-specific data
00016 #include <sys/sysinfo.h>
00017 
00018 unsigned long int allowed_memory_usage_KB(void)
00019 {
00020   // returns the allowed memory that fft may consume during operation.
00021   // only guaranteed to work under Linux, since we read kernel-specific data.
00022 
00023   // problem: between different calls of this function there may still exist
00024   // allocated space (up to several hundred megabytes!) for invariant
00025   // polynomials. For this reason we must add the amount of memory allocated by
00026   // the current process. This info can be obtained via "/proc/<pid>/mstat".
00027 
00028   const pid_t mypid = getpid();
00029   std::ostringstream ostr;
00030   ostr << "/proc/" << mypid << "/statm"; // filename to retrieve info: "/proc/<pid>/mstat"
00031   std::ifstream proc_file(ostr.str().c_str());
00032 
00033   unsigned long long int owned_ram = 0;
00034   proc_file >> owned_ram; // read in the memory pages currently allocated by this process
00035   owned_ram*=getpagesize(); // multiplied with the pagesize to get the actual value in bytes
00036 
00037   struct sysinfo info;
00038   if (sysinfo(&info)!=0)
00039    {
00040      cerr << "call sysinfo failed; unable to get memory info" << endl;
00041      exit(1);
00042    }
00043 
00044   owned_ram/=info.mem_unit; // actual value in "memory units"
00045 
00046   static unsigned long long int initially_owned_ram = 0;
00047   if (initially_owned_ram==0) initially_owned_ram = owned_ram;
00048 
00049 #if defined(VERBOSE_INFO)
00050   cout << "memory status info:" << endl; 
00051   cout << "memory unit: " << info.mem_unit << endl;
00052   cout << "total ram  : " << info.totalram << endl;
00053   cout << "free ram   : " << info.freeram << endl;
00054   cout << "free swap  : " << info.freeswap << endl;
00055   cout << "totalhigh  : " << info.totalhigh << endl;
00056   cout << "freehigh   : " << info.freehigh << endl;
00057   cout << "buffer ram : " << info.bufferram << endl;
00058   cout << "shared ram : " << info.sharedram << endl;
00059   cout << "owned ram  : " << owned_ram << endl;
00060 #endif
00061 
00062   unsigned long int ram = info.freeram;
00063   if (ram<info.totalram/2)
00064     ram=info.totalram/2; // sorry, but we cannot detect cached mem that will
00065                          // eventually be freed if needed. But anyone who
00066                          // starts this program expects some usability.
00067                          // We have to be a little greedy...
00068   if (info.freeswap>ram-ram/4) ram+=ram/2; // a little bit of swapping should be allowed ;-)
00069 
00070   ram=ram+owned_ram-initially_owned_ram; // treat ram allocated by ourselves as free ram
00071 
00072   unsigned long int m = static_cast<unsigned long int>(static_cast<double>(ram) /1024.0 * static_cast<double>(info.mem_unit));
00073   return min(m,FFT_MAX_MEM_USAGE);
00074 }
00075 #else
00076 
00077 #warning "linux free memory detection disabled!"
00078 unsigned long int allowed_memory_usage_KB(void)
00079 {
00080   return FFT_MAX_MEM_USAGE;
00081 }
00082 #endif
00083 
00084 
00086 void get_fft_parameter(unsigned int &pg_returnvalue, unsigned int &pms_returnvalue,
00087                        const double phase2, const mpz_t N)
00088 {
00089    // estimated memory usage depends on
00090    //  - given number N:
00091    //    for polynomial calculations we need to reduce results mod N;
00092    //    intermediate results are about N^2;
00093    //    -> memory usage per coefficient is approximately ld(N^2)=2*ld(N) bits
00094    //  - size of polynomial:
00095    //    each polynomial stores pg coefficients
00096    //    -> 2*ld(N)*pg bits
00097    //  - temporary memory for polynomial calculations:
00098    //    since we are operating with more than one polynomial
00099    //    and since most data structures produce some memory overhead
00100    //    we estimate the temporary space by using a fixed multiplier,
00101    //    -> 2*ld(N)*pg*fixed_multiplier bits
00102    //  - storage requested for recursive calls
00103    //     if intermediate results need to be stored in temporary memory
00104    //     during recursion, the storage requirements are typically
00105    //     proportional to the size of initial given data.
00106    //     However, if intermediate results are generated and
00107    //     need to be stored for later use (cf. fast multipoint polynomial
00108    //     evaluation schemes), the amount of storage needed can exceed
00109    //     a constant factor. We assume a logarithmic growth of the multiplier:
00110    //       recursion_depth * size_of_initial_data
00111    //
00112    // memory usage in Kilobytes (1KB = 1024 bytes= 8*1024 bits = 2^13 bits)
00113    //  -> 2*ld(N)*pg*fixed_multiplier *2^(-13) KB
00114    //   = (fixed_multiplier*ld(N)/4096)*pg KB
00115 
00116    const double fixed_multiplier = 2.3;
00117    const double mem_multiplier = fixed_multiplier/4096*mpz_sizeinbase(N,2); 
00118 
00119 
00120    // precomputing most common used differences:
00121    // pre_mul_size ist die Intervallgröße mit der pro Polynom gesucht wird
00122 
00123      // Je mehr verschiedene, kleine Primfaktoren enthalten sind, desto
00124      // weniger teilerfremde Zahlen gibt es im Intervall -> desto geringer
00125      // sind also die Speicherkosten.
00126      // Bei unserer fft-Version gilt in etwa, dass phi*pre_mul_size ungefähr phase2
00127      // entsprechen sollte; d.h. das Polynom hat die Größenordnung phi und
00128      // greift ein Intervall der Größe pre_mul_size ab. Und #phi Funktionswerte lassen
00129      // sich dann schnell über multipoint_eval ermitteln.
00130      // Nun ist die Polynomauswertung allerdings besonders effizient, wenn die
00131      // Polynome und die Anzahl der Punkte Zweierpotenzen sind.
00132      // Meiner Ansicht nach dürfte es daher vorteilhaft sein, pre_mul_size so zu
00133      // wählen, dass es maximal wird für ein phi, welches nahe unterhalb der
00134      // gewünschten Zweierpotenz liegt.
00135      // Da es nun nicht allzu viele praktikable Zweierpotenzen gibt, mit denen
00136      // wir arbeiten können (bei kleinen Zweierpotenzen ist die improved standard
00137      // continuation überlegen, bei zu großen Zweierpotenzen ist der Speicherbedarf
00138      // zu hoch), können wir geeignete Werte über eine Tabelle vorgeben.
00139      // (Für die Berechnung der Tabellenwerte siehe "fft_param.txt")
00140      /* Vorschläge wären 
00141         Polynomgrad    phi(pre_mul_size)     pre_mul_size                              abgedeckter Suchraum
00142         2^8 =     256,        240,        1050=(2)*(3)*(5)^2*(7),                                   134.400
00143         2^9 =     512,        480,        2310=(2)*(3)*(5)*(7)*(11),                                591.360
00144         2^10=    1024,        960,        4620=(2)^2*(3)*(5)*(7)*(11),                            2.365.440
00145         2^11=    2048,       1920,        9240=(2)^3*(3)*(5)*(7)*(11),                            9.461.760
00146         2^12=    4096,       4032,       19110=(2)*(3)*(5)*(7)^2*(13),                           39.137.280
00147         2^13=    8192,       7680,       39270=(2)*(3)*(5)*(7)*(11)*(17),                       160.849.920
00148         2^14=   16384,      16128,       79170=(2)*(3)*(5)*(7)*(13)*(29),                       648.560.640
00149         2^15=   32768,      31680,      159390=(2)*(3)^2*(5)*(7)*(11)*(23),                   2.611.445.760
00150         2^16=   65536,      63360,      330330=(2)*(3)*(5)*(7)*(11)^2*(13),                  10.824.253.440
00151         2^17=  131072,     126720,      690690=(2)*(3)*(5)*(7)*(11)*(13)*(23),               45.265.059.840
00152         2^18=  262144,     253440,     1381380=(2)^2*(3)*(5)*(7)*(11)*(13)*(23),            181.060.239.360
00153         2^19=  524288,     518400,     2852850=(2)*(3)*(5)^2*(7)*(11)*(13)*(19),            747.857.510.400
00154         2^20= 1048576,    1036800,     5705700=(2)^2*(3)*(5)^2*(7)*(11)*(13)*(19),        2.991.430.041.600
00155         2^21= 2097152,    2027520,    11741730=(2)*(3)*(5)*(7)*(11)*(13)*(17)*(23),      12.312.096.276.480
00156         2^22= 4194304,    4055040,    23483460=(2)^2*(3)*(5)*(7)*(11)*(13)*(17)*(23),    49.248.385.105.920
00157         2^23= 8388608,    8294400,    48498450=(2)*(3)*(5)^2*(7)*(11)*(13)*(17)*(19),   203.417.242.828.800
00158         2^24=16777216,   16588800,    96996900=(2)^2*(3)*(5)^2*(7)*(11)*(13)*(17)*(19), 813.668.971.315.200
00159      */
00160 
00161    const unsigned int pg_start = 256;
00162    static const unsigned int pms[]
00163     = { 1050, 2310, 4620, 9240, 19110, 39270, 79170, 159390, 330330, 690690,
00164         1381380, 2852850, 5705700, 11741730, 23483460, 48498450, 96996900,0 };
00165 
00166 
00167    // since we do pairing, our values vary a little bit...
00168    pg_returnvalue=pg_start; pms_returnvalue=2*pms[0]; // initial values (default)
00169    unsigned int estimated_mem_usage = 0;
00170    const unsigned int allowed_mem_usage = allowed_memory_usage_KB();
00171    for (unsigned int pg=pg_start,i=0; pms[i]!=0; pg*=2, ++i)
00172     {
00173       const unsigned int mem_usage = static_cast<unsigned int>(mem_multiplier*(8+3+i)*pg);
00174       const double grenze = static_cast<double>((pg/4)*3)*static_cast<double>(pms[i]);
00175 
00176 #if defined(VERBOSE)
00177       cout << "pg=" << pg
00178            << " -> pms: " << pms[i]
00179            << ", estimated memory usage: " << mem_usage << "KB  (" << mem_usage/1024 << "MB)"
00180            << endl;
00181 #endif
00182 
00183       if ( phase2 > grenze && allowed_mem_usage>=mem_usage )
00184        {
00185          estimated_mem_usage=mem_usage;
00186          pg_returnvalue = pg; pms_returnvalue = 2*pms[i];
00187        }
00188     }
00189 
00190 #if defined (VERBOSE_NOTICE)
00191    cout << "using pg=" << pg_returnvalue
00192         << " -> pms: " << pms_returnvalue << endl
00193         << "estimated memory usage: " << estimated_mem_usage << "KB (" << estimated_mem_usage/1024 << "MB)"
00194         << endl;
00195    cout << "allowed memory usage : " << allowed_mem_usage << "KB (" << allowed_mem_usage/1024 << "MB)"
00196         << endl;
00197 #endif
00198 
00199    /* Am Rande:
00200       Bei 1 GB main memory und pg=262144 geht dem Rechner bereits der
00201       Speicher aus. (Der Speicherbedarf hängt natürlich auch von der Größe
00202       der zu faktorisierenden Zahl ab.) Wenn der Rechner nur im Hintergrund
00203       faktorisieren soll und Memory geswapt wird, ist das kontraproduktiv.
00204       -> Die Werte sind also individuell zu tunen.
00205          Die Funktion allowed_memory_usage_KB(void) kann z.B. entsprechend
00206          verfeinert werden.
00207       -> Die Speicherbedarfsabschätzung ist sehr grob; sie kann aber ohne eine
00208          genaue Analyse der unterliegenden Datenstrukturen und verwendeten
00209          Algorithmen nicht sehr viel besser sein.
00210          Wenn speicherbedarfsrelevante Änderungen an den Algorithmen vorgenommen
00211          werden, kann eine Validierung und ggf. Änderung der Abschätzungsroutine
00212          erforderlich werden.
00213    */
00214 
00215 }
00216 
00217 #endif

Generated on Wed Nov 7 23:29:25 2007 for Qsieve by  doxygen 1.5.4