C++Guns – RoboBlog blogging the bot

29.05.2015

2.1GB limit Qt Container (Bug of the day 6)

Filed under: Allgemein — Tags: , — Thomas @ 13:05

Diesmal hab ich mir selbst in den Fuß geschossen. Zum zweiten mal in den selben.

Alle Qt Container können nur maximal 2.1GB allocieren. Das ist eine Design Entscheidung von Qt. Dummerweise wird keine Fehlermeldung erstellt falls man mehr Speicher braucht. Ich kann schon verstehen, dass es irgendwie falsch ist, wenn eine GUI Anwendung auf einmal Tonnen von GB brauch. Aber einfach ein Absturz zulassen ist schlampig.

Warum 2.1GB? Das entspricht der Anzahl der Bytes die ein signed 32bit integer darstellen kann. Genauer 2^31 = 2,147,483,648. Das entspeicht etwas 500 millionen integer Werte. 250 millionen double Werte. Und 83 millionen x,y,z Koordinaten. Das hört sich viel an, aber wenn wir mal ehrlich sind, das ist garnichts. Gerade in der Big Data Zeit rechnet man nicht mehr in GB sondern ehr in PB.

Also, muss man in seiner GUI Anwendung so 10, 20 Zahlen speichern, dann sind die Qt Container alle gut.
Möchte man mehr wissenschaftlich arbeiten, dann nur die std Container. Alles schön trennen und streng zu sich selbst sein, dann stürzen die Programme auch nicht ab :)

20.05.2015

-1 (Bug of the day 5)

Filed under: Allgemein — Tags: , — Thomas @ 19:05

vector vecB;
vecB.resize(vecA.size()-1);

for(size_t i = 0; i < vecA.size(); ++i) {  
  ...
  vecB[i] = value
  ...
}

Na, wer findet ihn?

Bei so etwas findet ich QT's QVector wieder besser als den Standard c++ vector. Qt macht zumindest bei Debug Einstellung eine Bereichsüberprüfung.

Ausser es wird die at() Funktion statt den operator() benutzt. Dann machen beide eine Bereichsüberprüfung. Aber auch hier zeigen sich kleine Unterschiede welche die Philosophie von Qt und cpp Standardlib zeigen.
Qt bietet nur eine cost Version der Funktion at() an. Der Grund liegt wahrscheinlich im intuitiven Verständnis des Codes.
Ein normaler Vector zugriff wie man das seit Jahrzehnten macht geht so:

vec[i] = value;

Das sieht sieht "komisch" aus:

vec.at(i) = value;

Qt für normale Programmierer mit wenig Schmerzen. c++ std für Profis mit viel Schmerzen :D

16.05.2015

integer overflow debugger trap

Filed under: Allgemein — Tags: , — Thomas @ 08:05

Benutzt man 16bit Integer statt 32bit um Speicher zu sparen und seine cache misses zu optimieren, läuft man Gefahr den Zahlenbereich von -32768 bis +32767 zu verlassen. So wie es für Floatingpoint Zahlen Überprüfungen auf over/underflow etc gibt, die ein Signal werfen bzw den Compiler anspringen lassen, so gibt es das auch für Integer. Ist wohl nur nicht so bekannt.

Für den GNU Compiler gibt es die Option -ftrapv [1]

This option generates traps for signed overflow on addition, subtraction, multiplication operations.

Leider wird nicht erwähnt, dass das Programm nun furchtbar langsamer läuft. Weiterhin scheinen sich noch im Geheimen Fehler zu verbergen die ich nicht ganz verstehe. [2]

When -ftrapv is in effect, libcalls are "necessary" so that the results of an operation can be propagated without making the call to the libgcc functions dead.

Im Prinzip funktioniert das Überprüfen auf Overflows aber ohne große Einbüßen wie dieser Artikel zeigt [3]

How much overhead should we expect from enabling integer overflow checks? Using a compiler flag or built-in intrinsics, we should be able to do the check with a conditional branch that branches based on the overflow flag that add and sub set. Assuming that branch is always correctly predicted (which should be the case for most code), the costs of the branch are the cost of executing that correctly predicted not-taken branch, the pollution the branch causes in the branch history table, and the cost of decoding the branch.
...
result in 3% penalty.
...
John Regehr, who’s done serious analysis on integer overflow checks estimates that the penalty should be about 5%, which is in the same ballpark as our napkin sketch estimate.

Der Artikel "Catching Integer Overflows in C" [4] gibt eine schöne Übersicht über die Techniken der Overflow detections und jener FAQ Eintrag "comp.lang.c FAQ list · Question 20.6b" [5] gibt eine Beispiel Implementierung. Mit dieser Einschränkung:

(Note: these functions all share one bug: they may fail if invoked on the largest negative integer, INT_MI

#include < stdio.h >
#include < limits.h >

int chkadd(int a, int b) {
	if(b < 0)
		return chksub(a, -b);
	if(INT_MAX - b < a) {
		fputs("int overflow\n", stderr);
		return INT_MAX;
	}
	return a + b;
}

int chksub(int a, int b) {
	if(b < 0)
		return chkadd(a, -b);
	if(INT_MIN + b > a) {
		fputs("int underflow\n", stderr);
		return INT_MIN;
	}
	return a - b;
}

int chkmul(int a, int b) {
	int sign = 1;
	if(a == 0 || b == 0) return 0;
	if(a < 0) { a = -a; sign = -sign; }
	if(b < 0) { b = -b; sign = -sign; }
	if(INT_MAX / b < a) {
		fputs("int overflow\n", stderr);
		return (sign > 0) ? INT_MAX : INT_MIN;
	}
	return sign * a * b;
}

Nun möchte man nicht für das komplette Programm die Überprüfung einschalten, sondern z.B. nur für 16bit Integer. Vielleicht implementiere ich einen eigenen Type mit überladenen Funktionen dafür. Irgendwann..

[1] https://gcc.gnu.org/onlinedocs/gcc/Code-Gen-Options.html
[2] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=35412
[3] http://danluu.com/integer-overflow/
[4] http://www.fefe.de/intof.html
[5] http://c-faq.com/misc/intovf.html

24.04.2015

return void func call (Pseudo Bug of the day 3)

Filed under: Allgemein — Tags: — Thomas @ 08:04

Heute gefunden. Im Prinzip geht der pseudobug so:

void calc(int a, int b) {
  std::cout << "func " << a + b << "\n";
}

void add(int a, int b) {
  return calc(a, b);
}

int main() {
  add(2, 3);
  return 0;
}

Na, wer sieht das Problem? Wird calc aufgerufen?

(Ja, aber explicit void zurückgeben is braindamage ;) )

21.04.2015

Passing random generators around (functor)

Filed under: Allgemein — Tags: — Thomas @ 02:04

Update: Version with template instead of std::function C++ Guns: Passing function objects around (Update Example random generators)

Consider the following problem: One has a parallelly callable function which needs a random generator for every thread. One wants to call the function several times and every time we want another random sequence. How can we implement this?

Between every function call the random generator must keep its states. So the caller of the function must own the generator objects and pass them to the function. The caller is also in responsible the initialize the random generators. One can feed them with random seeds. But i think there exist a better solutions [1]

We create an array of n random generators, initialize them with a random seed and pass them per pointer to the function.

Inside this function we need a distribution. A simple rand() % maxrand is not good. It uses only the lower bits which may not be random. With c++11 we can bind the random generator and number distribution together. This allows us the create a nice dice() object which represent our rand(). Of course, this object can be simply pass around.

Output:

Thread 0 sum 5021
Thread 0 sum 4966
Thread 3 sum 4996
Thread 3 sum 5014
Thread 1 sum 5007
Thread 2 sum 4970
Thread 1 sum 5092
Thread 2 sum 5002
Info sizeof std::function 16

Code:


// can be passed by value
int function2(std::function dice) {
  int sum = 0;
  for(int i=0; i < 1000; i++) {
    sum += dice();
  }
  return sum;
}

// pass by reference. it has state
int function(std::minstd_rand& generator) {
  // we need random numbers from 0 to 10
  std::uniform_int_distribution distribution(0,10);

  // we bind the generator and distribution together to a new functor dice()
  // std::bind copy its arguments by value. To change the given generator state,
  // my must pass it by reference. This can be easly done with std:ref.
  // Yeah C++ can be strange.
  std::function dice = std::bind ( distribution, std::ref(generator) );

  return function2(dice);
}

int main() {
  omp_set_num_threads(4);

  // create 4 random generatores with random seed
  std::minstd_rand seedGen;
  std::minstd_rand generators[omp_get_num_threads()];
  for(int i=0; i < omp_get_num_threads(); i++) {
    generators[i].seed(seedGen());
  }

#pragma omp parallel for
  for(int i=0; i < 8; i++) {
    const int id = omp_get_thread_num();
    // pass one generator to our function
    int sum = function(generators[id]);

    std::cout << "Thread " << id << " sum " << sum << "\n";
  }

  std::cout << "Info sizeof std::function " << sizeof(std::function) << "\n";

  return 0;
}

[1] Tina’s Random Number Generator Library
Tina’s Random Number Generator Library

Calling multiple random generator in parallel c++11

Filed under: Allgemein — Tags: — Thomas @ 00:04

Standard random functions are not thread safe. Calling them in parallel may result in undefined behavior or the threads blocking each other. This is a little test to create the same random sequences in serial as in parallel too.

Setting the same seed on a random generator as a start point should produce the same random sequence. First we create one generator and print the first three numbers. Restart it with different seed numbers.
Then we create three random generators with the same seeds and call them parallel. They should create the same sequences even if they are called interleaved.

Program output:

seriell
seed 1
48271
182605794
1291394886

seed 2
96542
365211588
435306125

seed 3
144813
547817382
1726701011

parallel
Thread 0 seed 1: 48271
Thread 0 seed 1: 182605794
Thread 0 seed 1: 1291394886
Thread 1 seed 2: 96542
Thread 1 seed 2: 365211588
Thread 1 seed 2: 435306125
Thread 2 seed 3: 144813
Thread 2 seed 3: 547817382
Thread 2 seed 3: 1726701011

Yep, work as expected :D
The program looks not as simple as it might can be. But I think it is okay.

void waitlong() {
  long a = 0;
  for(int i=0; i < 1000000; i++) {
      a += i;
  }
}

int main() {
  // create some random number and store them in expectedResult
  int expectedResult[3][3] = { 0 };
  std::minstd_rand generator;

  std::cout << "seriell\n";

  for(int id=0; id < 3; id++) {
    generator.seed(id+1);
    std::cout << "seed " << id+1 << "\n";
    for(int i=0; i < 3; i++) {
      expectedResult[id][i] = generator();
      std::cout << expectedResult[id][i] << "\n";
    }
  }

  // create three generators with the same seed and call them parallel
  std::cout << "\nparallel\n";

  std::minstd_rand generators[3];
  generators[0].seed(1);
  generators[1].seed(2);
  generators[2].seed(3);

  int result[3][3] = { 0 };
  int count = 0;

  // create 3 threads and interleave them with schedule(static, 1) num_threads(3)
  // We can not call cout in a threaded enviroment. So we must store the random numbers
  // in a array and print them laster. count and result is for this.
#pragma omp parallel for firstprivate(count) shared(result) schedule(static, 1) num_threads(3)
  for(int i=0; i < 3*3; i++) {
    int id = omp_get_thread_num();
    int rand = generators[id]();

    // not error free even with omp critical
//     std::cout << "Thread " << id << " seed " << id+1 << ": " << rand << "\n";

    result[id][count++] = rand;

    // wait a litte bit to give the next thread a chance to start
    waitlong();
  }

  // now print and compre the results
  for(int id=0; id < 3; id++) {
    for(int i=0; i < 3; i++) {
      std::cout << "Thread " << id << " seed " << id+1 << ": " << result[id][i] << "\n";
      if(result[id][i] != expectedResult[id][i]) {
        std::cout << "but expected " << expectedResult[id][i] << "\n";
      }
    }
  }

  std::cout << "\nValues per seed must be equal\n";
  return 0;
}

09.04.2015

Polymorphie virtual abstract pure deconstrutor WTF?

Filed under: Allgemein — Tags: — Thomas @ 08:04

Okay ich fasse mal zusammen. Ich sehe 3 Fälle, vom einfachsten zum schwersten.

1) Ganz normal Klassen ableiten.

class Base {
public:
  ~Base() {
    cout << "~TestClass\n";
  }
};

class Derived : public TestClass {
public:
  ~Derived() {
    cout << "~Derived\n";
  }
};

int main() {
  {
  cout << "a\n";
  Derived a;
  }
  cout << "b\n";
  Derived *b = new Derived();
  delete b;
}
Ausgabe:
a                                                                                                                
~Derived                                                                                                         
~Base
b
~Derived
~Base

Der Destructor wird immer aufgerufen.
2) Mit Polymorphie (Pointer ist vom Type Base)

class Base {
public:
  virtual ~Base() {
    cout << "~TestClass\n";
  }
};

class Derived : public TestClass {
public:
  virtual ~Derived() {
    cout << "~Derived\n";
  }
};

int main() {
  {
  cout << "a\n";
  Derived a;
  }
  cout << "b\n";
  Base *b = new Derived();
  delete b;
}
Ausgabe:
a                                                                                                                
~Derived                                                                                                         
~Base
b
~Derived
~Base

Der Destructor wird immer aufgerufen.

3) Mit Polymorphie und class Derived als abstracte Klasse


class Base {
public:
  virtual ~Base() = 0;
};

Base::~Base() {
    cout << "~Base\n";
}

class Derived : public Base {
public:
  virtual ~Derived() {
    cout << "~Derived\n";
  }
};

int main() {
  {
  cout << "a\n";
  Derived a;
  }
  cout << "b\n";
  Base *b = new Derived();
  delete b;
}
Ausgabe:
a                                                                                                                
~Derived                                                                                                         
~Base
b
~Derived
~Base

Der Destructor wird immer aufgerufen.
Der Destructor von Base ist zwar abstract, es gibt aber eine default Implementation, die im Zweifel garnichts macht. Es muss immer eine Implementation der Spezialfunktionen, wie dem Destructor, geben. Wird keine explicit angegeben, erstellt automatisch der Compiler eine. (Ausser man declariert ihn mit "=0" als abstract).

Wozu das ganze? Abstrace Klassen können nicht instanziiert werden, sie dienen wirklich nur als ein abstractes Interface. Sollte man es dennoch versuchen, bekommt man folgende Compilerfehler:

error: cannot declare variable ‘c’ to be of abstract type ‘Base’
 Base c;
 note:   because the following virtual functions are pure within ‘Base’:
 virtual ~Base() = 0;

PS: Wird das virtual keyword mal vergessen, wird der Base Destructor im ungünstigen Fall nicht aufgerufen ;)

Hf

11.03.2015

Wikipedia's Kellerautomat

Filed under: Allgemein — Tags: , — Thomas @ 23:03

Wikipedia's Kellerautomat Beispiel in C ist total falsch (Stand 11.03.2015).
Nicht nur, dass es im feinsten C programmiert ist, was kein Mensch versteht. Es wird nicht mal ein Kellerautomat implementiert. Die Konfiguration eines Kellerautomats hängt von seinem Zustand, dem zu lesenden Zeichen und dem Zeichen auf dem Stack (Keller) ab.
In dem Beispiel wird aber nur das zu lesende Zeichen und der Stack benutzt. Es gibt kein expliziten Zustand des Automaten. Vielmehr wird der Zustand in dem Kellerspeicher gespeichert. Was nicht Sinn der Sache ist. Das Beispiel verwirrt also mehr, als dass es nutzt.

Ich habe den Beispielcode nach C++ Qt übersetzt und so auch den Fehler gefunden.


#include < QCoreApplication >
#include < QTextStream >
#include < QString >
#include < QDebug >
#include < QStack > 
#include < QMap >


int main(int argc, char *argv[]) {
    QCoreApplication a(argc, argv);
    
    QStack stack;
    QTextStream ss(stdin);
    QByteArray s;

    /* Eingabestring */
    qDebug() << "Bitte Ausdruck eingeben:";

    ss >> s;
    // Wir benutzen in der  Zustandsuebergangsfunktion das Null Byte als Indikator fuer das Ende vom String.
    // Fuege es manuell in den String ein.
    s += '\0';

    /* Start-state auf Stack pushen */
    stack.push(0);

    /*
     Ein einfacher Kellerautomat ("pushdown automaton")

     Symbol   | (       | )     | \0
     ---------+---------+--------+-----------
     State 0  | PUSH 1  | ERROR  | ACCEPT
     State 1  | PUSH 1  | POP    | ERROR
    */

    enum class Actions {PUSH, POP, ERROR, ACCEPT};
    QMap< QPair< int, char>, Actions> states;
    states.insert(qMakePair(0, '('),  Actions::PUSH);
    states.insert(qMakePair(0, ')'),  Actions::ERROR);
    states.insert(qMakePair(0, '\0'), Actions::ACCEPT);
    states.insert(qMakePair(1, '('),  Actions::PUSH);
    states.insert(qMakePair(1, ')'),  Actions::POP);
    states.insert(qMakePair(1, '\0'), Actions::ERROR);


    /* Ausführungsschleife */
    bool finish = false;
    for(int i=0; i < s.size() && finish == false ; i++) {
        /* Aktion auf Basis des Eingabesymbols ermitteln */
        auto key = qMakePair(stack.top(), s.at(i));
        Actions action = states.value(key, Actions::ERROR);

        /* Ausführen der Aktionen */
        switch(action) {
        case Actions::ERROR: {
            qDebug() << "Unerwartetes Zeichen \"" << s.at(i) << "\"an Position" << i+1;
            finish = true;
            break;
        }
        case Actions::ACCEPT: {
            qDebug() << "Ausdruck akzeptiert!";
            finish = true;
            break;
        }
        case Actions::POP: {
            stack.pop();
            break;
        }
        case Actions::PUSH: {
            stack.push(1);
            break;
        }
        default:
            qDebug() << "internal error";
        }
    }

    return 0;
}

26.02.2015

rosettacode - Simple moving variance

Filed under: Allgemein — Tags: , — Thomas @ 11:02

As mentioned in my earlier post original C++ rosettacode sucks.
Here is my example for a simple moving variance and standard deviation. One has to add only a few lines of code:


in add()
...
    sum2 = sum2 - oldValue*oldValue + value*value;
...

double var() const {
    return sum2/size - avg()*avg();
}

double std() const {
    return std::sqrt(var());
}
Data1 Period 3
Added 1 avg:        1 var:        0 std: 0
Added 2 avg:      1.5 var:     0.25 std: 0.5
Added 3 avg:        2 var: 0.666667 std: 0.816497
Added 4 avg:        3 var: 0.666667 std: 0.816497
Added 5 avg:        4 var: 0.666667 std: 0.816497
Added 5 avg:  4.66667 var: 0.222222 std: 0.471405
Added 4 avg:  4.66667 var: 0.222222 std: 0.471405
Added 3 avg:        4 var: 0.666667 std: 0.816497
Added 2 avg:        3 var: 0.666667 std: 0.816497
Added 1 avg:        2 var: 0.666667 std: 0.816497

Data2 Period 8
Added 2 avg:        2 var:        0 std: 0
Added 4 avg:        3 var:        1 std: 1
Added 4 avg:  3.33333 var: 0.888889 std: 0.942809
Added 4 avg:      3.5 var:     0.75 std: 0.866025
Added 5 avg:      3.8 var:     0.96 std: 0.979796
Added 5 avg:        4 var:        1 std: 1
Added 7 avg:  4.42857 var:  1.95918 std: 1.39971
Added 9 avg:        5 var:        4 std: 2

ToDo: add Skewness and Kurtosis

23.02.2015

c++ CYK implementation

Filed under: Allgemein — Tags: — Thomas @ 14:02

Die Wikipedia Artikel zum Cocke-Younger-Kasami-Algorithmu kann man vergessen. Man kapiert nichts. Hab auf youtube ein 15min Video [1] von Benny Neugebauer dazu gefunden. Wunderbar, alles gleich verstanden :)
Hab ihn mal schnell in C++ implementiert. Hier die Hauptschleife:


    /*
    Für j = 2 ... n
      Für i = 1 ... n - j + 1
        Für k = 1 ... j - 1
          Setze V_{i,j} := V_{i,j} + { l | \exists B,C in N ((l -> BC) in P and B in V_{i,k} and C in V_{i + k, j - k}) \}

    Falls S \in V_{1,n}, stoppe und gib "w wird von G erzeugt" aus
    Stoppe und gib "w wird nicht von G erzeugt" aus
    */
    for(size_t row=1; row < n; row++) {
//        cout << "row " << row << "\n";
        for(size_t col=0; col < n-row; col++) {
//            cout << "col " << col << "\n";
            for(size_t k=0; k < row; k++) {
                const auto &e1 = V.at(k, col);
                const auto &e2 = V.at(row-k-1, col+k+1);

                for(size_t i=0; i < e1.size(); i++) {
                    for(size_t j=0; j < e2.size(); j++) {
//                        cout << "Check " << e1.at(i) << " " << e2.at(j) << "\n";
                        const Production::String toTest({e1.at(i), e2.at(j)});
                        for(const Production& p : P) {
                            if(p.right == toTest) {
                                // uniq append
                                if(contains(V.at(row, col), p.left) == false) {
                                    V.at(row, col).push_back(p.left);
                                }
                            }
                        }
                    }
                }
            }
        }
    }

Für jeden Matrixeintrag in der oberen linken Dreiecksmatrix:
Für jeden Eintrag oberhalt in der Spalte und Diagonal nach oben:
Schaue nach ob die Konkatenation der beiden Einträge in der rechten Seite der Produktionsregeln vorkommen:
Ja) Linke Seite der Produktion in die Matrix eintragen.

Steht am Ende in der letzten Zeile, erste Spalte das Startsymbol, dann kann das Wort erkannt werden.

CYK.zip

[1] https://www.youtube.com/watch?v=JugIgxUyA4Q

« Newer PostsOlder Posts »

Powered by WordPress