shutdown
17-07-2007, 17:03
Hallo,
ich versuche momentan "nebenher" ein bisschen C zu lernen, da die Sprache doch sehr weit verbreitet ist und es eigentlich nur von Vorteil sein kann, sich damit als interessierter Linuxuser ein bisschen auszukennen.
Man könnte das Ganze auch einfach Hobby nennen.
Beim Herumspielen mit pthreads bzw der Umstellung eines Programms zur Berechnung von Primzahlen auf pthreads ist mir nun etwas sehr seltsames aufgefallen.
Hier erstmal mein Programm:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#define MAXNUM 200000
#define MINNUM 1
#define THREAD_NUM 8
int primcnt[THREAD_NUM+1];
int primcount=0;
void *thread_main( void *ptr ) {
int i,*tid,tmax,tmin,trange,tmodulo;
tid = (int *) ptr;
/*
* Calculate the range of numbers each thread has to process;
* calculate the starting and ending number for each thread.
*/
trange=((MAXNUM-MINNUM)+1)/THREAD_NUM;
tmin=(trange*(*tid))+MINNUM;
tmax=tmin+trange-1;
/* If the division for range calculation returns a rest, let the last thread do the extra job */
if (*tid == THREAD_NUM-1) {
tmax=tmax+(((MAXNUM-MINNUM)+1)%THREAD_NUM);
trange=trange+(((MAXNUM-MINNUM)+1)%THREAD_NUM);
}
/* Give some feedback so we can see everything is all right so far */
printf("This is thread %d. Range is %d, starting at %d, ending at %d.\n",*tid,trange,tmin,tmax);
/* Check if a number within the threads range is a prime number */
for (i=tmin;i<=tmax;i++) {
if (isprim(i) == 0) {
/*
* If we've found a prime number, raise the counter.
* Interesting part: We've got 2 counters, a in-thread counter and a global one.
* This will be quite interesting later...
*/
primcnt[*tid]++;
primcount++;
}
}
return 0;
}
int isprim(int number) {
/*
* Function to see if a given number is a prime number.
* This method seems to be pretty fast and accurate.
*/
int divisor=2;
while (divisor*divisor <= number) {
if (number % divisor == 0) {
return 1; /* Not a prime number */
}
divisor++;
}
return 0; /* Prime number found :) */
}
main() {
int i,*threadid[THREAD_NUM],tid[THREAD_NUM],iret[THREAD_NUM];
pthread_t thread[THREAD_NUM];
/* Prepare some IDs to pass to the threads */
for (i=0;i<THREAD_NUM;i++) {
tid[i]=i;
threadid[i]=&tid[i];
}
/* Run independent threads */
for (i=0;i<THREAD_NUM;i++) {
iret[i] = pthread_create( &thread[i], NULL, thread_main, (void*) threadid[i]);
}
/* Wait for threads to finish */
for (i=0;i<THREAD_NUM;i++) {
pthread_join( thread[i], NULL);
}
/* Let's see how many prime numbers each thread has found. Further we can see if the number of hits will change here */
for (i=0;i<THREAD_NUM;i++) {
printf("Thread %d returned %d prime numbers.\n",i,primcnt[i]);
primcnt[THREAD_NUM]=primcnt[THREAD_NUM]+primcnt[i];
}
/* Let's see how many prime numbers we've counted.
* AND let's see, if the sum of all in-thread counters is equal to our global counter.
* I can tell you it won't be, so print the offset as well.
* The offset seems to be *always* >0, so our global counter must be missing some counts.
* Dunno why this happens, seems quite impossible to me.
*/
printf("\n\n%d prime numbers found\n",primcnt[THREAD_NUM]);
printf("Alternative number is %d. Offset is %d\n",primcount,primcnt[THREAD_NUM]-primcount);
return 0;
}
Nun zum eigentlichen Phänomen: Ich starte mehrere threads, die je einen gewissen Zahlenraum nach Primzahlen absuchen. Jeder der Threads hat einen eigenen Zähler für Funde, zudem habe ich einen globalen Zähler.
Die Zähler innerhalb der Threads habe ich erst zur Fehlersuche hinzugefügt, da der globale Zähler bei jedem Programmaufrauf einen etwas anderen Wert zurückliefert.
Hier mal 2 Beispielausgaben des Programms, die Merkwürdigkeit ist einfach offensichtlichst:
This is thread 0. Range is 25000, starting at 1, ending at 25000.
This is thread 1. Range is 25000, starting at 25001, ending at 50000.
This is thread 2. Range is 25000, starting at 50001, ending at 75000.
This is thread 5. Range is 25000, starting at 125001, ending at 150000.
This is thread 3. Range is 25000, starting at 75001, ending at 100000.
This is thread 6. Range is 25000, starting at 150001, ending at 175000.
This is thread 4. Range is 25000, starting at 100001, ending at 125000.
This is thread 7. Range is 25000, starting at 175001, ending at 200000.
Thread 0 returned 2763 prime numbers.
Thread 1 returned 2371 prime numbers.
Thread 2 returned 2260 prime numbers.
Thread 3 returned 2199 prime numbers.
Thread 4 returned 2142 prime numbers.
Thread 5 returned 2114 prime numbers.
Thread 6 returned 2068 prime numbers.
Thread 7 returned 2068 prime numbers.
17985 prime numbers found
Alternative number is 17928. Offset is 57
This is thread 0. Range is 25000, starting at 1, ending at 25000.
This is thread 1. Range is 25000, starting at 25001, ending at 50000.
This is thread 2. Range is 25000, starting at 50001, ending at 75000.
This is thread 3. Range is 25000, starting at 75001, ending at 100000.
This is thread 4. Range is 25000, starting at 100001, ending at 125000.
This is thread 5. Range is 25000, starting at 125001, ending at 150000.
This is thread 6. Range is 25000, starting at 150001, ending at 175000.
This is thread 7. Range is 25000, starting at 175001, ending at 200000.
Thread 0 returned 2763 prime numbers.
Thread 1 returned 2371 prime numbers.
Thread 2 returned 2260 prime numbers.
Thread 3 returned 2199 prime numbers.
Thread 4 returned 2142 prime numbers.
Thread 5 returned 2114 prime numbers.
Thread 6 returned 2068 prime numbers.
Thread 7 returned 2068 prime numbers.
17985 prime numbers found
Alternative number is 17952. Offset is 33
Was ich hier offset getauft habe ändert sich bei jedem Aufruf des Programms, und nun meine eigentliche Frage: Warum?
Welchen wahrscheinlich blöden Anfängerfehler mache ich, dass das passiert? Ich suche den Fehler nun seit 2 Tagen und finde ihn nicht...
Verzeiht mir meine Ahnungslosigkeit, aber es macht mich einfach irre!
Ach ja: Übersetzt habe ich mein Werk mit "gcc -o threads ./threads.c -pthread"
Peter
PS: Falls jemand einen Vorschlag hat, den Quellcode fürs nächste Programm übersichtlicher zu machen oder "Stilfehler" bemängeln will, macht es bitte! Das ist das erste C-Programm von mir, das ich jemand anderen als mich lesen lasse ;)
ich versuche momentan "nebenher" ein bisschen C zu lernen, da die Sprache doch sehr weit verbreitet ist und es eigentlich nur von Vorteil sein kann, sich damit als interessierter Linuxuser ein bisschen auszukennen.
Man könnte das Ganze auch einfach Hobby nennen.
Beim Herumspielen mit pthreads bzw der Umstellung eines Programms zur Berechnung von Primzahlen auf pthreads ist mir nun etwas sehr seltsames aufgefallen.
Hier erstmal mein Programm:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#define MAXNUM 200000
#define MINNUM 1
#define THREAD_NUM 8
int primcnt[THREAD_NUM+1];
int primcount=0;
void *thread_main( void *ptr ) {
int i,*tid,tmax,tmin,trange,tmodulo;
tid = (int *) ptr;
/*
* Calculate the range of numbers each thread has to process;
* calculate the starting and ending number for each thread.
*/
trange=((MAXNUM-MINNUM)+1)/THREAD_NUM;
tmin=(trange*(*tid))+MINNUM;
tmax=tmin+trange-1;
/* If the division for range calculation returns a rest, let the last thread do the extra job */
if (*tid == THREAD_NUM-1) {
tmax=tmax+(((MAXNUM-MINNUM)+1)%THREAD_NUM);
trange=trange+(((MAXNUM-MINNUM)+1)%THREAD_NUM);
}
/* Give some feedback so we can see everything is all right so far */
printf("This is thread %d. Range is %d, starting at %d, ending at %d.\n",*tid,trange,tmin,tmax);
/* Check if a number within the threads range is a prime number */
for (i=tmin;i<=tmax;i++) {
if (isprim(i) == 0) {
/*
* If we've found a prime number, raise the counter.
* Interesting part: We've got 2 counters, a in-thread counter and a global one.
* This will be quite interesting later...
*/
primcnt[*tid]++;
primcount++;
}
}
return 0;
}
int isprim(int number) {
/*
* Function to see if a given number is a prime number.
* This method seems to be pretty fast and accurate.
*/
int divisor=2;
while (divisor*divisor <= number) {
if (number % divisor == 0) {
return 1; /* Not a prime number */
}
divisor++;
}
return 0; /* Prime number found :) */
}
main() {
int i,*threadid[THREAD_NUM],tid[THREAD_NUM],iret[THREAD_NUM];
pthread_t thread[THREAD_NUM];
/* Prepare some IDs to pass to the threads */
for (i=0;i<THREAD_NUM;i++) {
tid[i]=i;
threadid[i]=&tid[i];
}
/* Run independent threads */
for (i=0;i<THREAD_NUM;i++) {
iret[i] = pthread_create( &thread[i], NULL, thread_main, (void*) threadid[i]);
}
/* Wait for threads to finish */
for (i=0;i<THREAD_NUM;i++) {
pthread_join( thread[i], NULL);
}
/* Let's see how many prime numbers each thread has found. Further we can see if the number of hits will change here */
for (i=0;i<THREAD_NUM;i++) {
printf("Thread %d returned %d prime numbers.\n",i,primcnt[i]);
primcnt[THREAD_NUM]=primcnt[THREAD_NUM]+primcnt[i];
}
/* Let's see how many prime numbers we've counted.
* AND let's see, if the sum of all in-thread counters is equal to our global counter.
* I can tell you it won't be, so print the offset as well.
* The offset seems to be *always* >0, so our global counter must be missing some counts.
* Dunno why this happens, seems quite impossible to me.
*/
printf("\n\n%d prime numbers found\n",primcnt[THREAD_NUM]);
printf("Alternative number is %d. Offset is %d\n",primcount,primcnt[THREAD_NUM]-primcount);
return 0;
}
Nun zum eigentlichen Phänomen: Ich starte mehrere threads, die je einen gewissen Zahlenraum nach Primzahlen absuchen. Jeder der Threads hat einen eigenen Zähler für Funde, zudem habe ich einen globalen Zähler.
Die Zähler innerhalb der Threads habe ich erst zur Fehlersuche hinzugefügt, da der globale Zähler bei jedem Programmaufrauf einen etwas anderen Wert zurückliefert.
Hier mal 2 Beispielausgaben des Programms, die Merkwürdigkeit ist einfach offensichtlichst:
This is thread 0. Range is 25000, starting at 1, ending at 25000.
This is thread 1. Range is 25000, starting at 25001, ending at 50000.
This is thread 2. Range is 25000, starting at 50001, ending at 75000.
This is thread 5. Range is 25000, starting at 125001, ending at 150000.
This is thread 3. Range is 25000, starting at 75001, ending at 100000.
This is thread 6. Range is 25000, starting at 150001, ending at 175000.
This is thread 4. Range is 25000, starting at 100001, ending at 125000.
This is thread 7. Range is 25000, starting at 175001, ending at 200000.
Thread 0 returned 2763 prime numbers.
Thread 1 returned 2371 prime numbers.
Thread 2 returned 2260 prime numbers.
Thread 3 returned 2199 prime numbers.
Thread 4 returned 2142 prime numbers.
Thread 5 returned 2114 prime numbers.
Thread 6 returned 2068 prime numbers.
Thread 7 returned 2068 prime numbers.
17985 prime numbers found
Alternative number is 17928. Offset is 57
This is thread 0. Range is 25000, starting at 1, ending at 25000.
This is thread 1. Range is 25000, starting at 25001, ending at 50000.
This is thread 2. Range is 25000, starting at 50001, ending at 75000.
This is thread 3. Range is 25000, starting at 75001, ending at 100000.
This is thread 4. Range is 25000, starting at 100001, ending at 125000.
This is thread 5. Range is 25000, starting at 125001, ending at 150000.
This is thread 6. Range is 25000, starting at 150001, ending at 175000.
This is thread 7. Range is 25000, starting at 175001, ending at 200000.
Thread 0 returned 2763 prime numbers.
Thread 1 returned 2371 prime numbers.
Thread 2 returned 2260 prime numbers.
Thread 3 returned 2199 prime numbers.
Thread 4 returned 2142 prime numbers.
Thread 5 returned 2114 prime numbers.
Thread 6 returned 2068 prime numbers.
Thread 7 returned 2068 prime numbers.
17985 prime numbers found
Alternative number is 17952. Offset is 33
Was ich hier offset getauft habe ändert sich bei jedem Aufruf des Programms, und nun meine eigentliche Frage: Warum?
Welchen wahrscheinlich blöden Anfängerfehler mache ich, dass das passiert? Ich suche den Fehler nun seit 2 Tagen und finde ihn nicht...
Verzeiht mir meine Ahnungslosigkeit, aber es macht mich einfach irre!
Ach ja: Übersetzt habe ich mein Werk mit "gcc -o threads ./threads.c -pthread"
Peter
PS: Falls jemand einen Vorschlag hat, den Quellcode fürs nächste Programm übersichtlicher zu machen oder "Stilfehler" bemängeln will, macht es bitte! Das ist das erste C-Programm von mir, das ich jemand anderen als mich lesen lasse ;)