次は一番重たい部分を計算するCのプログラム
correlation.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <math.h>
#define DEBUG (0)
typedef struct __data {
struct __data *next;
struct __data *prev;
char *element_name;
int correlation;
double sum;
double mean;
double sqsum;
double subpart;
long numdata;
double *datalist;
} data;
data* read_each( char *listfilename );
data* read_datafile( char *linebuffer );
void add_data_array( data **datalistpp, data *new_datap );
void calculate( data *data_list );
void remove_trailing_newline( char *linebuffer );
void help( char *programname );
void *o_malloc( size_t size, const char *msg, const char *func, const long line );
int
main( int argc, char *argv[] )
{
data *data_list;
if ( argc != 2 ) {
help( argv[0] );
}
data_list = read_each( argv[1] );
if ( data_list == NULL ) {
fprintf( stderr, "no data given.\n" );
exit( 1 );
}
calculate( data_list );
}
data*
read_each( char *listfilename )
{
FILE *fp;
char *linebuffer;
int linebufsize;
data *read_datap = NULL;
linebufsize = getpagesize();
linebuffer = o_malloc( linebufsize, "unable to prepare linebuffer\n", __func__, __LINE__ );
fp = fopen( listfilename, "r" );
if ( fp == NULL ) {
fprintf( stderr, "unable to open file %s\n", listfilename );
exit( 1 );
}
while( fgets( linebuffer, linebufsize, fp ) != NULL ) {
data *new_datap;
long len;
if ( linebuffer[0] == '#' ) {
continue;
}
remove_trailing_newline( linebuffer );
new_datap = read_datafile( linebuffer );
if ( new_datap->correlation != 0 ) {
add_data_array( &read_datap, new_datap );
} else {
/* take it away from the memory.*/
free( new_datap->datalist );
free( new_datap->element_name );
free( new_datap );
}
}
fclose( fp );
return read_datap;
}
data*
read_datafile( char *datafilename )
{
data *newdata;
FILE *fp;
char *linebuffer;
size_t linebufsize;
char *tagname;
newdata = o_malloc( sizeof( data ), "unable to allocate memory for newdata\n", __func__, __LINE__ );
linebufsize = getpagesize();
linebuffer = o_malloc( linebufsize, "unable to prepare linebuffer for read_datafile()\n", __func__, __LINE__ );
fp = fopen( datafilename, "r" );
if ( fp == NULL ) {
fprintf( stderr, "unable to open file %s as data file target\n", datafilename );
exit( 1 );
}
/* read tagname */
{
char *p;
long dataoffset;
if( fgets( linebuffer, linebufsize, fp ) == NULL ) {
fprintf( stderr, "unable to read tagname at file %s\n", datafilename );
exit( 1 );
}
remove_trailing_newline( linebuffer );
p = strstr( linebuffer, "tagname\t" );
if ( p != linebuffer ) {
fprintf( stderr, "unable to find \"tagname\t\" indicator in %s\n", datafilename );
exit( 1 );
}
dataoffset = strlen( "tagname\t" );
newdata->element_name = strdup( linebuffer + dataoffset );
if ( newdata->element_name == NULL ) {
fprintf( stderr, "unable to copy tagname %s in %s\n",
linebuffer + dataoffset, datafilename );
exit( 1 );
}
#if DEBUG
fprintf( stderr, "tagname was %s\n", newdata->element_name );
#endif/*DEBUG*/
}
/* read Correlation */
{
char *p;
long dataoffset;
if( fgets( linebuffer, linebufsize, fp ) == NULL ) {
fprintf( stderr, "unable to read Correlation at file %s\n", datafilename );
exit( 1 );
}
p = strstr( linebuffer, "Correlation\t" );
if ( p != linebuffer ) {
fprintf( stderr, "unable to find \"Correlation\t\" indicator in %s\n", datafilename );
exit( 1 );
}
dataoffset = strlen( "Correlation\t" );
if ( strncasecmp( linebuffer + dataoffset, "OK", 2 ) != 0 ) {
newdata->correlation = 0;
} else {
newdata->correlation = 1;
}
#if DEBUG
fprintf( stderr, "correlation was %d\n", newdata->correlation );
#endif/*DEBUG*/
}
/* read sum */
{
char *p;
long dataoffset;
int res;
if( fgets( linebuffer, linebufsize, fp ) == NULL ) {
fprintf( stderr, "unable to read sum at file %s\n", datafilename );
exit( 1 );
}
p = strstr( linebuffer, "sum\t" );
if ( p != linebuffer ) {
fprintf( stderr, "unable to find \"sum\t\" indicator in %s\n", datafilename );
exit( 1 );
}
dataoffset = strlen( "sum\t" );
newdata->sum = 0.0;
res = sscanf( linebuffer + dataoffset, "%lf", &( newdata->sum ));
if ( res != 1 ) {
perror( "sscanf failed when reading sum.\n" );
exit( 1 );
}
#if DEBUG
fprintf( stderr, "sum was %lf\n", newdata->sum );
#endif/*DEBUG*/
}
/* read mean */
{
char *p;
long dataoffset;
int res;
if( fgets( linebuffer, linebufsize, fp ) == NULL ) {
fprintf( stderr, "unable to read mean at file %s\n", datafilename );
exit( 1 );
}
p = strstr( linebuffer, "mean\t" );
if ( p != linebuffer ) {
fprintf( stderr, "unable to find \"mean\t\" indicator in %s\n", datafilename );
exit( 1 );
}
dataoffset = strlen( "mean\t" );
newdata->mean = 0.0;
res = sscanf( linebuffer + dataoffset, "%lf", &( newdata->mean ));
if ( res != 1 ) {
perror( "sscanf failed when reading mean.\n" );
exit( 1 );
}
#if DEBUG
fprintf( stderr, "mean was %lf\n", newdata->mean );
#endif/*DEBUG*/
}
/* read sqsum */
{
char *p;
long dataoffset;
int res;
if( fgets( linebuffer, linebufsize, fp ) == NULL ) {
fprintf( stderr, "unable to read sqsum at file %s\n", datafilename );
exit( 1 );
}
p = strstr( linebuffer, "sqsum\t" );
if ( p != linebuffer ) {
fprintf( stderr, "unable to find \"sqsum\t\" indicator in %s\n", datafilename );
exit( 1 );
}
dataoffset = strlen( "sqsum\t" );
newdata->sqsum = 0.0;
res = sscanf( linebuffer + dataoffset, "%lf", &( newdata->sqsum ));
if ( res != 1 ) {
perror( "sscanf failed when reading sqsum.\n" );
exit( 1 );
}
#if DEBUG
fprintf( stderr, "sqsum was %lf\n", newdata->sqsum );
#endif/*DEBUG*/
}
/* read subpart */
{
char *p;
long dataoffset;
int res;
if( fgets( linebuffer, linebufsize, fp ) == NULL ) {
fprintf( stderr, "unable to read subpart at file %s\n", datafilename );
exit( 1 );
}
p = strstr( linebuffer, "subpart\t" );
if ( p != linebuffer ) {
fprintf( stderr, "unable to find \"subpart\t\" indicator in %s\n", datafilename );
exit( 1 );
}
dataoffset = strlen( "subpart\t" );
newdata->subpart = 0.0;
res = sscanf( linebuffer + dataoffset, "%lf", &( newdata->subpart ));
if ( res != 1 ) {
perror( "sscanf failed when reading subpart.\n" );
exit( 1 );
}
#if DEBUG
fprintf( stderr, "subpart was %lf\n", newdata->subpart );
#endif/*DEBUG*/
}
/* read datas */
{
char *p;
long dataoffset;
int res;
if( fgets( linebuffer, linebufsize, fp ) == NULL ) {
fprintf( stderr, "unable to read datas at file %s\n", datafilename );
exit( 1 );
}
p = strstr( linebuffer, "datas\t" );
if ( p != linebuffer ) {
fprintf( stderr, "unable to find \"datas\t\" indicator in %s\n", datafilename );
exit( 1 );
}
dataoffset = strlen( "datas\t" );
newdata->numdata = 0;
res = sscanf( linebuffer + dataoffset, "%ld", &( newdata->numdata ));
if ( res != 1 ) {
perror( "sscanf failed when reading datas.\n" );
exit( 1 );
}
#if DEBUG
fprintf( stderr, "datas was %d\n", newdata->numdata );
#endif/*DEBUG*/
}
/* read data list */
{
long i;
int res;
newdata->datalist
= malloc( sizeof( double ) * newdata->numdata );
if ( newdata->datalist == NULL ) {
fprintf( stderr, "not enough memory for datalist: %s\n", datafilename );
exit( 1 );
}
if( fgets( linebuffer, linebufsize, fp ) == NULL ) {
fprintf( stderr, "unable to read empty line at file %s\n", datafilename );
exit( 1 );
}
for ( i = 0; i < newdata->numdata; i++ ) {
if( fgets( linebuffer, linebufsize, fp ) == NULL ) {
fprintf( stderr, "unable to read datas at file %s\n", datafilename );
exit( 1 );
}
res = sscanf( linebuffer, "%lf", &( newdata->datalist[i] ));
/* if we failed in reading data, that means that element should be 0.0 */
if ( res != 1 ) {
newdata->datalist[i] = 0.0;
}
#if DEBUG
fprintf( stderr, "datas was %lf\n", newdata->datalist[i] );
#endif/*DEBUG*/
}
}
fclose( fp );
return newdata;
}
void
add_data_array( data **datalistpp, data *new_datap )
{
if (( *datalistpp ) == NULL ) {
new_datap->next = NULL;
new_datap->prev = NULL;
(*datalistpp) = new_datap;
} else {
new_datap->next = (*datalistpp);
new_datap->prev = NULL;
(*datalistpp)->prev = new_datap;
(*datalistpp) = new_datap;
}
}
void calculate( data *data_list )
{
data *one, *two;
double cor, cor2;
int i;
/* First, change all the data in datalist[] to be subtraction of each's mean */
for ( one = data_list; one != NULL; one = one->next ) {
for ( i = 0; i < one->numdata; i++ ) {
one->datalist[i] -= one->mean;
}
}
for ( one = data_list; one != NULL; one = one->next ) {
for ( two = one->next; two != NULL; two = two->next ) {
cor = 0.0;
for ( i = 0; i < one->numdata; i++ ) {
cor += one->datalist[i] * two->datalist[i];
}
cor /= ( one->subpart * two->subpart );
cor2 = cor * cor;
fprintf( stdout,
"%s\t%s\t%lf\t%lf\n",
one->element_name,
two->element_name,
cor, cor2 );
}
}
}
void remove_trailing_newline( char *linebuffer )
{
size_t len;
while( 1 ) {
len = strlen( linebuffer );
if ( linebuffer[len-1] == '\n' ) {
linebuffer[len-1] = '\0';
} else {
break;
}
}
}
void
help( char *programname )
{
fprintf( stderr,
"%s <filename containing target>\n",
programname );
exit( 127 );
}
void*
o_malloc( size_t size, const char *msg, const char *func, const long line )
{
void *p;
p = malloc( size );
if ( p == NULL ) {
fprintf( stderr, "%s:%ld: %s", func, line, msg );
exit( 1 );
}
}
みての通り、ファイルを読んでくるところの処理が延々。実際の計算部分は calculate() の部分だけ。しかもこれすら標準出力への出力コードが含まれているのに、このサイズ。あぁ、Cの最大の弱点はファイルIOの記述/メモリ管理の記述が面倒くさいことだなぁ。
ちなみに。「C++の例外処理で書けばいいじゃない」という提案をいただいた。が、その手段は使えないし、趣味として使わない。例外処理は素晴らしい機能だ。ただし、あれは「コード的には正しいが、動作環境との兼ね合いで不幸に至った場合」に、どこで不幸を食らったかに依存せず「不幸を食らった事実だけ」を書けばよいから素晴らしいのだ。
しかし、上記のコード、多分端々に見えているだろうが、「おかしくなるのは私のコーディングが間違っているから」というケースに対応するためのものだ。実際に発生した現象としては:
- tagname を引っ張ってきたのはいいが、行末の "\n" を削り忘れていた。
- Correlation のOK を case insensitive にチェックしているが、実は最初のコードでは "OK" に対して入力は "OK\n" で合致しない攻撃をくらいまくっていた。
- Correlation が NG の場合、計算対象からはずすためにリンクリストに登録しないのだが、そのメモリを「放置」していた(メモリリークだ)。64bit環境では特に問題は無かった(ちょっと swap IO するだけ)のだが、32bit環境に持ってきたらパンクした。
- Correlation 自身はOKであっても、データが全フィールドに渡って存在するとは限らない。存在しないエントリは 0.0 として扱うことになっていたのだが、sscanf() の戻り値をチェックするときにそのことを完全に失念していて、おかしな部分で give up して exit() していた。
まぁ、そういうケースに対応するための「デバッガコード」であって、異常系ではなかったりする。例外処理だと、「どこでおかしいのか」も判らないし、特に 1 のケースなどは assert() にもひっかけようがない。
先にも書いたが、1データ組み合わせ当たり 6.74usec で計算できた。はーやーいー。9msec とは比べ物にならない。やっぱりコンパイラ言語は本気を出すとすごいわ。