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