How to set CPU Affinities on linux box:

April 19, 2012


#include <cstdio>
#include <errno.h>
#include <sched.h>

#include <unistd.h> //processor affinity.
#include <sys/syscall.h> // thread ID.
#include <sys/resource.h> // get/set priority.

using namespace std;

int ShowAffinity () {
     cpu_set_t *mask;
     size_t size;

     // you can use sysconf or __CPU_SETSIZE system macro.
     int ncpu = sysconf(_SC_NPROCESSORS_ONLN);

     mask = CPU_ALLOC(ncpu);
     size = CPU_ALLOC_SIZE(ncpu);
     CPU_ZERO_S(size, mask);
     errno = 0;
     if ( sched_getaffinity(0, sizeof(mask), mask) == -1 ) {
     CPU_FREE(mask);
     switch ( errno ) {
     case EPERM: printf ( “Error: Insufficient permission.\n” ); break;
     case EFAULT: printf ( “Error: Invalid memory address.\n” ); break;
     case ESRCH: printf ( “Error: Invalid process ID\n” ); break;
     case EINVAL: printf ( “Error: Invalid arguments\n” ); break;
     }
     return -1;
     }

     printf ( “CPU Affinity: \n” );
     for ( int i=0; i < ncpu; i ++ ) {
         if ( CPU_ISSET_S( i, size, mask ) ) {
            printf(“\tCPU %d is set\n”, (i+1));
         }
     }

     CPU_FREE(mask);
    return 0;
}

int SetAffinity ( int cpuId ) {
    int ncpu = sysconf(_SC_NPROCESSORS_ONLN);
    cpu_set_t *mask = CPU_ALLOC(ncpu);
    size_t size = CPU_ALLOC_SIZE(ncpu);
    CPU_ZERO_S(size, mask);
    CPU_SET_S(cpuId, size, mask);
    if ( sched_setaffinity(0, size, mask) == -1 ) {
       CPU_FREE(mask);
       switch ( errno ) {
       case EPERM: printf ( “Error: Insufficient permission.\n” ); break;
       case EFAULT: printf ( “Error: Invalid memory address.\n” ); break;
       case ESRCH: printf ( “Error: Invalid process ID\n” ); break;
       case EINVAL: printf ( “Error: Invalid arguments\n” ); break;
       }
       return -1;
    }

    CPU_FREE(mask);
    return 0;
}

int ResetAffinity () {
    int ncpu = sysconf(_SC_NPROCESSORS_ONLN);
    cpu_set_t *mask = CPU_ALLOC(ncpu);
    size_t size = CPU_ALLOC_SIZE(ncpu);

    CPU_ZERO_S(size, mask);
    for ( int i=0; i < ncpu; i++ ) {
        CPU_SET_S(i, size, mask);
    }

    if ( sched_setaffinity(0, size, mask) == -1 ) {
        CPU_FREE(mask);
        switch ( errno ) {
        case EPERM: printf ( “Error: Insufficient permission.\n” ); break;
        case EFAULT: printf ( “Error: Invalid memory address.\n” ); break;
        case ESRCH: printf ( “Error: Invalid process ID\n” ); break;
        case EINVAL: printf ( “Error: Invalid arguments\n” ); break;
        default: printf ( “Error: Unexpected Error code %d\n”, errno ); break;
        }
        return -1;
    }

    CPU_FREE(mask);
    return 0;
}

 

// To set CPU affinity of a any thread, call  SetAffinity() from thread function.

// e.g. you can set affinity for main thread/process as follows:

int main(void) {

    //get number of processors on the box.
    int ncpu = sysconf(_SC_NPROCESSORS_ONLN);
    ShowAffinity ();
    printf ( “\nRestricting to only middle one : %d \n”, ncpu/2 );
    SetAffinity ( (ncpu-1) / 2 );
    ShowAffinity ();

    return 0;
}

Advertisements

“mktime” slow? use custom function.

December 1, 2010

Recently I was working on stream of timestamps in ASCII (C string) format. To do different calculations we mostly need to convert timestamp strings to epoch ticks. Standard C library provides “mktime” function which takes struct tm to be filled and passed as an argument to mktime function. Function works fine if you are not using it very frequently. But if you need to convert millions of conversion at runtime then this function becomes a bottle neck on the performance of your application. I didn’t get any chance to go through the source code/assembly of mktime so I am not sure why this function is so expensive. But I just tried to implement my custom function to do the same conversion what “mktime” does. Results were unbelieveable. I ran one million calls of each function and here are the results:

 mktime: 2950000 microseconds
 time_to_epoch: 60000 microseconds

Here is source code:

time_t time_to_epoch ( const struct tm *ltm, int utcdiff ) {
   const int mon_days [] =
      {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
   long tyears, tdays, leaps, utc_hrs;
   int i;

   tyears = ltm->tm_year – 70 ; // tm->tm_year is from 1900.
   leaps = (tyears + 2) / 4; // no of next two lines until year 2100.
   //i = (ltm->tm_year – 100) / 100;
   //leaps -= ( (i/4)*3 + i%4 );
   tdays = 0;
   for (i=0; i < ltm->tm_mon; i++) tdays += mon_days[i];

   tdays += ltm->tm_mday-1; // days of month passed.
   tdays = tdays + (tyears * 365) + leaps;

   utc_hrs = ltm->tm_hour + utcdiff; // for your time zone.
   return (tdays * 86400) + (utc_hrs * 3600) + (ltm->tm_min * 60) + ltm->tm_sec;
}

void str_to_tm ( char *mdate, char *mtime, struct tm* mtm ) {
   char *pstr;
   long year, month, day, hour, min, sec;

   year = strtol( mdate, &pstr, 10 );
   month = strtol( ++pstr, &pstr, 10 );
   day = strtol( ++pstr, &pstr, 10 );

   hour = strtol( mtime, &pstr, 10 ); while( !isdigit(*pstr) ) ++pstr;
   min = strtol( pstr, &pstr, 10 ); while( !isdigit(*pstr) ) ++pstr;
   sec = strtol( pstr, &pstr, 10 );

   mtm->tm_sec = sec;
   mtm->tm_min = min;
   mtm->tm_hour = hour;
   mtm->tm_mday = day;
   mtm->tm_mon = month – 1;
   mtm->tm_year = year – 1900;
}

int main ( int argc, char *argv[] ) {
   char mydate[] = “2010-11-30”;
   char mytime[] = “11:30:45″;

   struct tm mtm;
   time_t ticks, mytcks;

   str_to_tm ( mydate, mytime, &mtm );

   mytcks = time_to_epoch ( &mtm, 5 );

   ticks = mktime ( &mtm );

   printf ( ” std func time : %s”, asctime( localtime( &ticks ) ) );
   printf ( ” our func time : %s”, asctime( localtime( &mytcks ) ) );
   printf ( ” stdlib func ticks : %ld \n”, ticks );
   printf ( ” our func ticks : %ld \n”, mytcks );
}

Output:

std func time : Tue Nov 30 11:30:45 2010
our func time : Tue Nov 30 11:30:45 2010
std func ticks : 1291134645
our func ticks : 1291134645

Problem with ascii to floating point functions using gcc

November 26, 2010

Found this issue while playing around with inline assembly code of floating point to string encoding and decoding functions on Linux. If you are using gcc (haven’t checked any other C compiler yet) and try to convert c-strings to floating points you might get some weired results in some cases. Here is trimmed down version of my code:

#include <stdio.h>
int main () {
char *pch;
double d1 = atof( “00.000001” );
double d2 = strtod( “00.000001”, pch = NULL );
printf ( ” d1 : %f \n d2 : %f \n”, d1, d2 );
}

Output:
d1 : -1598689907.000000
d2 : -1598689907.000000

If you turn on warnings (-Wall) you might get something like:

warning: implicit declaration of function ‘atof’

which gives clear hint about the problem. The problem is related to the return type of these conversion functions. C compiler doesn’t know the return type of these symbols unless we include stdlib.h. So it assumes the default return type which is integer and then convert that integer to double later on so loses actual floating point value.

Boost Threads and Condition Variables

August 24, 2010

#include <boost/thread/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/condition.hpp>
#include <iostream>

const int BUF_SIZE = 10;
const int ITERS = 100;

boost::mutex io_mutex;

class buffer
{
public:
  typedef boost::mutex::scoped_lock scoped_lock;

  buffer(): p(0), c(0), full(0)
  {
  }

  void put(int m)
  {
    scoped_lock lock(mutex);
    if (full == BUF_SIZE)
    {
      {
        boost::mutex::scoped_lock lock(io_mutex);
        std::cout << "Buffer is full. Waiting..." << std::endl;
      }
      while (full == BUF_SIZE)
        cond.wait(lock);
    }
    buf[p] = m;
    p = (p+1) % BUF_SIZE;
    ++full;
    cond.notify_one();
  }

  int get()
  {
    scoped_lock lk(mutex);
    if (full == 0)
    {
      {
        boost::mutex::scoped_lock lock(io_mutex);
        std::cout << "Buffer is empty. Waiting..." << std::endl;
      }
      while (full == 0)
        cond.wait(lk);
    }
    int i = buf[c];
    c = (c+1) % BUF_SIZE;
    --full;
    cond.notify_one();
    return i;
  }

private:
  boost::mutex mutex;
  boost::condition cond;
  unsigned int p, c, full;
  int buf[BUF_SIZE];
};

buffer buf;

void writer()
{
  for (int n = 0; n < ITERS; ++n)
  {
    {
      boost::mutex::scoped_lock lock(io_mutex);
      std::cout << "sending: " << n << std::endl;
    }
    buf.put(n);
  }
}

void reader()
{
  for (int x = 0; x < ITERS; ++x)
  {
    int n = buf.get();
    {
      boost::mutex::scoped_lock lock(io_mutex);
      std::cout << "received: " << n << std::endl;
    }
  }
}

int main(int argc, char* argv[])
{
  boost::thread thrd1(&reader);
  boost::thread thrd2(&writer);
  thrd1.join();
  thrd2.join();
  return 0;
}

Number of elements in enum (C++)

July 26, 2010

There is no dynamic way to find number of elements in enum, but the trick to do so might be putting an additional constant at the end of enum which will represent the size of enum:

enum Elements { FIRST, SECOND, THIRD, NUM_ELEMENT };

Note: To make if work, you can’t assign any value to any of the elements…:(

Perl: Parse pipe (“|”) separated data

June 27, 2010

Using Split:

$DATAFILE = "xyz.dat";
open(DATAFILE) or die("Could not open data file.");
foreach $line (<DATAFILE>) {
    chomp($line);              # remove the newline from $line.
    @fields = split("[|]", $line);  # note: split('|', $line) doesn't work
    print "$field[0]\n";
    print "$field[1]\n";  # and so on.
}

Hard Drive Clusters

April 14, 2010

The smallest unit of space on the hard disk that any software program can access is a sector, which usually consists of 512 bytes. It is possible to have an allocation system for a disk whereupon each file is assigned as many individual sectors as is required. An an, a 1 Megabyte would require approximately 2,048 individual sectors to store its data, and the HPFS utilizes this type of allocation system.

The FAT file system, as is the case with most file systems, does not utilize individual sectors, and there are several performance reasons for this. By using individual sectors, the process of managing disks becomes overly cumbersome since files are being broken into 512-byte pieces.

In order for FAT to manage files with some form of efficiency is to group sectors into larger blocks referred to as clusters, or allocation units. Cluster size, however, is not a predetermined size, but rather is determined by the size of the disk volume itself, with small volumes (disk sizes) resulting in smaller clusters, and larger volumes (disk sizes) using larger cluster sizes. For the most part, a cluster ranges in size from 4 sectors or 2,048 bytes to 64 sectors or 32,768 bytes. You should be aware that you may, on some occasions, find 128-sector clusters in use at 65,536 bytes per cluster

Cluster size is determined at the time a disk volume is partitioned. Cluster size is an important consideration when setting up a hard disk so as to ensure that you maximizing the efficiency of the disk. Larger cluster sizes result in more wasted space because files are less likely to fill up an integer number of clusters e.g. if a volume uses clusters that contain 8,192 bytes, an 8,000 byte file will use one cluster, or 8,192 bytes on the disk. On the other hand, a 9,000 byte file will use two clusters, or 16,384 bytes on the disk.

References:

http://www.dewassoc.com/kbase/hard_drives/clusters.htm

http://www.ntfs.com/hard-disk-basics.htm

Process Layout in a memory

March 24, 2010

Typically a process has 5 different areas of memory allocated

  1. Text segment – Code
  2. Data segment – Initialized data
  3. bss segment – Uninitialized data
  4. Heap
  5. Stack

The bss and data segments are allocated when the program is first loaded into memory, and their size does not change while it runs.

The heap and stack segments occupy the virtual memory above the text and data segments. The contents of the heap by contrast are volatile and change throughout the run, as dynamic memory operations are performed.

Important Tools:

  • size
  • nm
  • objdump
  • ldd (checks linked libraries with a binary)

Further Reading:

Anatomy of a Program in Memory

COMPILER, ASSEMBLER, LINKER AND LOADER: A BRIEF STORY