I received a task, of taking a known text file (Linux dictionary), use threads to count the different letters in it, and present the results in an array.
The code doesn't have to be pretty, elegant, clean or short. The only demand is to count the letters correctly and do it as fast as possible. The file will be timed upon execution by using the time command, so every millisecond counts.
I know using a single thread may prove to be more efficient, but multithreading is a requirement here...
What I did so far:
- memory mapped the file so I can work with it from memory.
- used 4 threads so each of the 4 cores can do part of the job.
- avoided race conditions in design, so locks won't have to be used and spend time.
- tried to avoid calculations and unnecessary function calls.
- tried to get higher priority for my process.
- used register on variables whenever possible.
I am compiling the file with:
gcc count.c -o count -lpthread -Ofast
and running it as sudo su, hoping it will be higher priority.
I will be very happy if you could review my code and tell me where you think I can improve the speed (no matter how negligible you think it is).
Also, if you think something I did is pointless, please feel free to throw it in my face as well.
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <pthread.h>
#include <sys/time.h>
#include <sys/resource.h>
/* declare counter arrays for the threads - global is quicker */
int counter[256];
int counter2[256];
int counter3[256];
int counter4[256];
/* declare a global pointer for the mapped file */
unsigned char* mapped_file;
/* declare variables for thread ids */
pthread_t pthread_id_1;
pthread_t pthread_id_2;
pthread_t pthread_id_3;
pthread_t pthread_id_4;
/* there are 4 functions that are declared independently, for each thread.
it can be smarter, but we are looking for quick, not smart */
void* thread_func1(void* NotInUse)
{
/* using register to quicken processing.
234712 is a quarter of the size of the file */
register int i1 = 234712;
register int offset = 0;
while (--i1)
{
++counter[*(mapped_file +offset)];
offset += 4;
}
return NULL;
}
void* thread_func2(void* NotInUse)
{
register int i2 = 234712;
register int offset = 1;
while (--i2)
{
++counter2[*(mapped_file +offset)];
offset += 4;
}
return NULL;
}
void* thread_func3(void* NotInUse)
{
register int i3 = 234712;
register int offset = 2;
while (--i3)
{
++counter3[*(mapped_file +offset)];
offset += 4;
}
return NULL;
}
void* thread_func4(void* NotInUse)
{
register int i4 = 234713;
register int offset = 3;
while (--i4)
{
++counter4[*(mapped_file + offset)];
offset += 4;
}
return NULL;
}
/* main */
int main (int argc, char** arv, char** envp)
{
/* trying to give my process a higher priority */
setpriority(PRIO_PROCESS, 0, -20);
/* open the text file */
int fd = open("/usr/share/dict/american-english", O_RDONLY);
/* map the text file. 938849 is its size */
mapped_file = mmap((caddr_t)0, 938849, PROT_READ, MAP_SHARED, fd, 0);
/* launching the treads */
pthread_create(&pthread_id_1 , NULL , &thread_func1 , NULL );
pthread_create(&pthread_id_2 , NULL , &thread_func2 , NULL );
pthread_create(&pthread_id_3 , NULL , &thread_func3 , NULL );
pthread_create(&pthread_id_4 , NULL , &thread_func4 , NULL );
/* joining the threads */
pthread_join( pthread_id_1 , NULL );
pthread_join( pthread_id_2 , NULL );
pthread_join( pthread_id_3 , NULL );
pthread_join( pthread_id_4 , NULL );
/* merging the individual counters */
int i = 0;
for ( i = 0 ; i < 256 ; ++ i)
{
counter[i] += counter2[i] + counter3[i] + counter4[i];
}
/* printing the final array
(only to check it works, will be removed on the final run)*/
i = 0;
while (i < 256)
{
if (counter[i]) printf("%d. %d\n", i, counter[i]);
++i;
}
printf("Done\n");
return 0;
}