Showing posts with label development. Show all posts
Showing posts with label development. Show all posts

Monday, November 20, 2017

Avoid Static Initializers

First of all, let's try to define what a static initializer is:
A static initializer is a small piece of code that the system dynamic linker runs when it loads a shared library or executable in memory. This code is executed just after the binary is reallocated and before the linker returns from dlopen(), LoadLibrary() or main().

C++ compilers generate static initializers for the following cases:
- non-POD that requires to be initialized by calling a constructor.
   e.g.: static std::string hello("hello");
- POD that initialization cannot take place at compiling time.
   e.g.:
     extern const int my_external_constant;
     const int my_local_constant { my_external_constant + 2 };

In these cases the compiler will generate code in a special hidden function and place it in the object file and initialize these variables for you. Then, when the static linker assembles all these binary object files it also creates a list a list of addresses of the static initializers, that will be read later by the dynamic linker at runtime.

Why should we avoid them

Static C++ variable initializer make loading libraries slower, and sometimes really slower (because of page faults):
- They make loading libraries slower because the must run before dlopen() or LoadLibrary() returns.
- They are small pieces of code, but they use to be randomly scattered through the final binary, which means that loading them forces the paging of a whole machine page from disk. On a cold startup this is terrible.
- They also affects global variables that are randomly spread over the library's .data section, resulting in more PSS (Portion Set Size) usage (i.e. private system physical page that cannot be shared, and requiere swap).
- They can make library unloading slower, because running the destructors of all global variables that were initialized in them is required before dlclose() returns, or the program exits.
- Finally, they prevent globant constant variables from being stored int the .text section of the final library (the one that can be shared read-only between all processes, without swapping). Instead these variables are put into mutable storage (e.g. the .data section) and will consume private non-shared memory.

Removing static initializers in big projects has been result in saving time in one order of magnitud.

Putting the things in code (just a little bit)

class Foo {
   public:
     Foo(const int value) : value{ value } {}
     int get() const { return my_value; }
     int add(const Foo& other) { my_value += other.get(); return my_value; };
     static Foo fromInt(const int value) { return Foo(value); }
   private:
     int my_value;
};

Now some global variables:

Foo foo_val_1(10);
Foo foo_val_2 = Foo::fromInit(2);

As these variables' types has a constructor, the compiler has to generate a static initializer for this source file. It may be a function to initialize foo_val_1 and foo_val_2 before anything else.

Without optimizations, this might look like:

void __global_initializer_for_foo.cpp_debug() {
   foo_val_1.Foo::Foo(10); // Call Foo constructor on foo_val_1
   Foo temp(2); // Create temp object with value 2
   foo_val_2.Foo::Foo(temp); // Call auto-generated Foo default copy-constructor
}

With optimizations something like:

void __global_initializer_for_foo.cpp_release() {
   foo_val_1::value_ = 10; // Inlined constructor
   foo_val_2::value_ = 2; // Inlined copy construction with temp obj.
}

In this specific case, the function only contains trivial assignments, and could be completely removed for the compiler, putting directly the initialization values directly into the objects file's .data section.

But unfortunately, this is very often not the case. Even for trivial uses, the compiler will, often, generate the static initializer function.

Static locals

One way to avoid static initializers is to place the variable into accessor functions, make them local statics. The C++ specification mandates that these objects are only constructed when the control flow reaches these declarations:

const Value& ​get_constant() {
   ​static const ​value magical_constant(complex_computation());
   ​return​ magical_constant;
}

No static initializer is generated here, but this function will contain special extra code to ensure that the magical_constant object is only constructed on the first call to get_constant().

But there are two problems with this technique:
- C++ local static construction is not guaranteed to be thread-safe on all systems. Some compilers, like Visual Studio implement it in a way that make it useless for use in multi-thread environment.
- Under the hood, it turns any constant into a mutable variable. Which potentially makes your code larger, slower and more memory consumer.

Something important to mention is that some compilers, like GCC, have the option to disable thread-safe local static initialization code, which makes your code more cross-platform.

Finding static initializers

Identifying code that generates static initializers from those that do not, might be hard and error-prone. It is better to look at the generated binary and look for them. There are some ways to do that:

- Object files will contain a special named function corresponding to the initialization code. GCC generates __GLOBAL__I_you_source_file. e.g. __GLOBAL__I_foo.cpp. Note the dot, which is not possible for ordinary C++ functions.
- Shared libraries contain static initializers entries. This varies for each platforms. For x86 and x64, look at the .ctors section. For ARM look for the .init_array
section. These will contain a list to function entry points in the binary.

Summary

Prefer POD global variable over non-POD. e.g:
   ​char​ name[128];
instead of:
   ​std::string​ name;

Sunday, February 22, 2015

Bingrep - Binary Auditor

Bingrep - Binary Auditor Bingrep allows you to find a binary expression into a file, or files, by searching recursively in a directory tree. You do not depend on file extensions anymore. You can find any file for its type, Bingrep will find it looking for binary patterns into the file.
You could use Bingrep for binary audit purposes as well.

How to use it

You just have to provide the path where to look for the expression, and the text file containing the expression to be find:

using

The text file with the expression to be find must look like:
61 6E 64 6F
Using one space to separate each hexa expression.

Here an output sample:

sample

As you can see, bingrep returns the offset where the expression was found.

Download

Linux:
Bingrep: bingrep-0.2.07.tar.gz (Normally this is what you want)
Bingrep library: libbingrep-0.2.05.tar.gz

Instructions: Build libbingrep and copy the resulting dynamic library to the lib directory in the bingrep folder. Add this lib folder to the LD_LIBRARY_PATH environment variable.

Sunday, January 18, 2015

C++11 benchmarks

The last week I been running some benchs to compare the new C++ standard, C++11, against its predecessor. The feature I was testings was the new move semantics and, as I expected, the new move semantic is way ahead in performance over its predecessor.

The tests were written for Linux, since it is very common in any distro to find the tools that the program depends to run, and, most important: I am using GCC to compile both version, C++11 and the old standard. I just turn on/off the new standard flag (-std=c++11), and I compile each version.
Using the same compiler to build each version, I avoid any difference I could face for using different compiler version.


The environment

The machine where the tests were ran:
Phenom II x4 955
SSD Corsair GT 160 GB
4 GB of RAM memory
Arch Linux Kernel 3.17.6-1-ARCH #1 SMP PREEMPT
gcc (GCC) 4.9.2 20141224 (prerelease)
GNU bash, version 4.3.33(1)-release (x86_64-unknown-linux-gnu)

Our program has:
An dummy object, called Dummy. It has implemented all the move semantics for the new standard, and, to put the things interesting, it has a double pointer that points to an array of 67108864 double, all of them genereted dynamically in it constructor.
Some foo functions. One of those take a Dummy object by value (lvalue), and other take a Dummy as a rvalue.


The features we are testing

The idea for this section is to clarify how the things had been working before the new standard, and how thing could be now, of course, if we do the things well :)

We are going to see three features:
- Perfect forwarding.
- Move constructors.
- Move assignment.


Perfect forwarding:

Let's say we have a function that takes an object Dummy by value, and other one that takes a Dummy object as a rvalue:








Before the new standard, we just call foo passing our object:



Here, a temporary object is created, calling the Dummy's copy constructor, and going on a deep copy to duplicate its array member, which is very expensive.

In C++11, we can use std::move(d1) to pass to foo:



The move call returns a rvalue, this way, we perform a Perfect forwarding, because no temporary object is created, instead, the rvalue version of foo is called, calling the move constructor of Dummy, going on a shallow copy, which does not duplicate the member array, instead, takes the same memory address of its source.

Based on this, for our tests, we can conclude that the rival of Perfect forwarding is going to be the Copy constructor. Because before perfect forwarding, we had no option more than create the temporary object.


Move constructor
Let's say we want to create a new instance of our Dummy object, but we want to base this one on an already existing Dummy object.
Dummy interface


Before the new standard, when we want to create an object based on another object, our best option is deep copying the old one to the new one. As I said before, it is very expensive, and unnecessary if we are not going to use our old object any more.



The new standard allow us to create an object stealing the members from the object we are basing the new one, by calling the Dummy's move constructor.



Just, after this, be careful when you refer to d1. It is better if you don't.

Based on this, for our tests, we can conclude that the rival of Move constructor is going to be the Copy constructor. Because before move constructos, we had no option more than do a deep copy of an object.


Move assignment
Let's we have already created d1 and d2, and we want to copy d2 to d1. Or we just want to assign a default Dummy object to d1.








As we can see on the Dummy interface image, Dummy implements the move semantics overloading the operator=.

Before the new standard, when we want to assign an already existing object to another existing one, we just assign d2 to d1, which goes on a deep copy. And the same happens when we want to assign an object created on the fly:




In C++11 we can overload the operator= for an rvalue, this allow us move assignments.




Since the Dummy() call returns a rvalue, the move assignment operator= is called.
And as Dummy() is a rvalue, when don't have to have the same caution that we have to have when we call the move constructor in the previous test example (which is a lvalue), when don't have the address of Dummy() to make any mistakes ;)

Based on this, for our tests, we can conclude that the rival of Move assignment is going to be the Copy assignment. Because before move assignment, we had no option more than do a copy assignment.


The benchs

Well, finally we have the results of the benchs. We run the benchs using the command time, which shows the wall clock time, the user time, and the sys time.
The wall clock time is the the human perception time. The user time is the time that the program runs on user space of the operative system. The sys time is the time that the program runs on kernel space (system calls).
Each test was ran 20 times, and I took the average.

First Test: Perfect Forwarding vs Temporaries







 






Unbelieve!, no? It is beacuse Perfect forwarding does not duplicate memory.


Second test: Move Constructor vs Copy Constructor










Same monster difference. Same motive.


Third test: Move Assignment vs Copy Assignment










And finally we can see that the same happens with the assignments.



Summary

Overload the operator= to achieve the move semantics.
Use move semantics whenever the situation allow you. They are way faster than duplicating blocks of memory.


You can download the benchs and run on you computer: c9xvsc++11