10 simple ways to improve the efficiency of C + + programs _c language

Source: Internet
Author: User

In this paper, the program is described as an example of the procedure efficiency of 10 simple methods, share for everyone for reference. The specific analysis is as follows:

For every programmer, the operational efficiency of a program is a problem that deserves attention and effort. But the optimization of program performance is also a complex knowledge, requires a lot of information, but not every programmer has such knowledge, and discusses how to optimize the program to improve the efficiency of the program is also very few books. But this does not mean that we can ignore the operating efficiency of the program, the following introduction I accumulated some simple and practical ways to improve the efficiency of the program, I hope to help.

First, to minimize the value of transmission, more than using references to pass parameters.
As for the reasons, I believe you are also very clear, if the parameters are such as int language custom types may be the impact of performance is not very large, but if the parameter is a class object, then its efficiency problem is self-evident. For example, a function that determines whether two strings are equal is declared as follows:

BOOL Compare (string s1, string s2)
bool Compare (string *s1, string *s2)
bool Compare (string &s1, String &s 2
bool Compare (const string &S1, const string &s2)

If you use the first function (value delivery), when parameter passing and function returns, the constructor and destructor of string need to be called two times (that is, a total of four functions are called), while the other three functions (pointer passing and reference passing) do not need to call these four functions. Because neither the pointer nor the reference creates a new object. If the overhead of constructing an object and destructor is huge, it will have a certain effect on efficiency.

However, in many people's eyes, the pointer is a nightmare, the use of pointers means that the error, then use the reference bar! It is as convenient and intuitive as the use of ordinary values, with the efficiency and ability of pointer transmission. Because a reference is an alias for a variable, it is equivalent to manipulating the actual object, so when you are sure that your function does or does not need the value of the variable parameter, you boldly add a const to the front of the declaration, as in the last function declaration.

Adding a const also has the advantage of referencing constants, which cannot be referenced without the const modifier.

Ii. efficiency issues derived from ++i and i++

Looking at the 1th above, you might think that's not just calling four more functions, you might be dismissive. So let's take a look at the following example, which should surprise you.

As for the integer variables of the former Plus and after the difference I believe that everyone is very clear. But here I want to talk to you about is the C + + class operator overload, in order to be consistent with the use of shaping variables, in C + + overloaded operator + + is generally the front plus and after the overload. You may say that you do not overload the + + operator in your code, but do you dare say that you have not used the + + operator overload of the class? Iterator class you've always used it! Perhaps to now you do not quite understand what I am saying, then take a look at the following example, is I write a list of internal iterators.

_singlelist::iterator& _singlelist::iterator::operator++ ()//Front add
{
  pnote = pnote->pnext;
  return *this;
}
_singlelist::iterator _singlelist::iterator::operator++ (int)//after adding
{
  iterator tmp (*this);
  Pnote = pnote->pnext;
  return tmp;
}

As you can see from the implementation of the post-add, the object uses itself to create a temporary object (itself in a copy of the function call), then changes its state and returns the temporary object, while the preceding implementation changes its internal state directly and returns its own reference.

From the 1th discussion, we can know that the copy constructor is invoked when the implementation is added. The destructor is also called when the function returns, and the constructor and destructor are not invoked because the preceding implementation changes the internal state of the object directly and returns its own reference to the beginning without creating a new object.

What's worse, iterators are usually used to traverse containers, most of which are used in loops, and imagine that your list has 100 elements that you can iterate through in the following two ways:

for (_singlelist::iterator it = List.begin (), it!= list.end (); ++it)
{
  //do something
} for 

(_ Singlelist::iterator it = List.begin (); It!= list.end (); it++)
{
  //do something
} 

If your habit is bad, write a second form, then unfortunately, do the same thing, because of a former plus and one after the difference, you need to call more than 200 functions, its effect on efficiency can not be ignored.

Iii. Loop-initiated discussion 1 (defined within the loop or outside of the loop)

Take a look at the following two pieces of code:

Code 1:

Classtest ct; for
(int i = 0; i < ++i)
{
  ct = A;
  Do something
}

Code 2:

for (int i = 0; i < ++i)
{
  classtest CT = A;
  Do something
}

Which code do you think is more efficient to run? Code 1 The scientist is code 2? In fact, in this case, which piece of code is more efficient is uncertain, or by this class classtest to decide, analysis is as follows:

For code 1: You need to call the Classtest constructor 1 times, the Assignment action function (operator=) 100 times, for code 2: You need to use (copy) The constructor 100 times, and the destructor 100 times.

If the cost of calling an assignment action function is less than the total cost of calling the constructor and destructor, the first is highly efficient, or the second is efficient.

Iv. cyclic initiation of discussion 2 (avoid too large loops)

Now, take a look at the next two pieces of code,
Code 1:

for (int i = 0; i < n; ++i)
{
  fun1 ();
  Fun2 ();
}

Code 2:

for (int i = 0; i < n; ++i)
{
  fun1 ();
}
for (int i = 0; i < n; ++i)
{
  fun2 ();
}

Note: There is no association between the FUN1 () and fun2 (), that is, the result of the two pieces of code is the same.

At the level of the code, it seems that the code 1 more efficient, because after all, the code 1 less than n times of the operation and judgment, after all, since the addition of operation and judgment also takes time. But is this really the case?

It depends on the size (or complexity) of the two functions, fun1 and fun2, if there are few code statements for these functions, code 1 is a bit more efficient, but if there are many fun1 and FUN2 statements, then code 2 is much more efficient than code 1. Maybe you don't understand why this is about the hardware of the computer.

Because the CPU can only be read from within the data, and the CPU's operation speed is far greater than memory, so in order to improve the speed of the program to effectively use the CPU's ability, between the memory and the CPU has a call cache memory, it's speed close to the CPU. And the data in the cache is loaded from memory, this process requires access to memory, slower.

Here first said that the cache design principle, is the time locality and the spatial locality. Time locality means that if a storage unit is accessed, it is possible that the unit will be accessed again quickly because the program has a loop. Spatial locality means that if a storage unit is accessed, the cell adjacent to the unit may also be accessed quickly, because most of the instructions in the program are sequentially stored, sequentially executed, and the data is generally clustered together in the form of vectors, arrays, trees, and tables.

See here you may have understood why. That's right, that's it! If Fun1 and Fun2 code is large, for example, are larger than the cache capacity, then in code 1, you can not make full use of cache (by the time of local and spatial local known), because each cycle, you have to put the contents of the cache kicked out, To reload code directives and data from another function in memory. And code 2 is a better use of the cache, the use of two circular statements, each of the data used by the loop has been loaded into the cache, each cycle can read and write data from the cache, access to less memory, faster, In theory, you just have to completely kick out the FUN1 data 1 times.

V. Local variable vs static variable

Many people think that local variables will not allocate storage units in memory until they are used. Static variables in the beginning of the program in memory, so the use of static variables should be more efficient than local variables, in fact, this is a misunderstanding, the use of local variables more efficient than the use of static variables.

This is because a local variable exists on the stack, and the allocation of its space is just a modification of the contents of the ESP register (even if a set of local variables is defined once). The biggest advantage of local variables in the stack is that functions can reuse memory, and when a function is called, exits the program stack, the memory space is recycled, and when the new function is invoked, the local variable can reuse the same address. When a piece of data is repeatedly read and written, its data will remain in the CPU's first-level cache (cache), access speed is very fast. Static variables, however, do not exist on the stack.

It can be said that static variables are inefficient.

Vi. avoidance of multiple inheritance

In C + +, multiple inheritance is supported, that is, a subclass can have more than one parent class. The book will tell us about the complexity of multiple inheritance and the difficulty of using it, and warn us not to use multiple inheritance lightly. In fact, multiple inheritance does not only make programs and code more complex, but also affect the efficiency of the program's operation.

This is because each object in C + + has a this pointer to the object itself, while the use of a class in C + + for a member variable is computed by the address of this, and in the case of multiple inheritance, the variable is more complex, which reduces the efficiency of the program. In order to solve the two semantics, multiple inheritance using virtual base classes has a more serious effect on efficiency because of the more complex inheritance relationships and the more complex relationships of the parent classes to which the member variables belong.

Vii. use of dynamic_cast as little as possible

The role of dynamic_cast is to type conversions of pointers or references, and dynamic_cast conversions require a certain relationship between the target type and the source object: the inheritance relationship. The implementation of pointer conversion from subclass to base class, in fact, is very inefficient, the performance of the program is also relatively large, can not be used in large quantities, and the more complex the inheritance relationship, the deeper the hierarchy, the greater the cost of conversion time. Use should be minimized in the program.

Viii. Reducing the use of division operations

Whether it is an integer or a floating-point operation, division is a slow-moving instruction, and it is more complicated to achieve division in the computer. So to reduce the number of division operations, here are some simple ways to improve efficiency:
1, through the mathematical method, the division into multiplication operations, such as if (A > b/c), if a, B, c are positive numbers, can be written as if (A*c > B)
2, let the compiler have room for optimization, such as the operation you want to do is an int type of N/8 words, written (unsigned) N/8 is conducive to compiler optimization. For the compiler to have room for optimization, the divisor must be constant, and this can be used to modify a variable to achieve the goal.

To declare a small-granularity function as an inline function (inline)

As we all know, call function is to protect the scene, for local variables allocated memory, after the function to restore the cost of the scene, and the inline function is to write its code directly to the call function, so do not need these costs, but will make the program's source code length becomes larger.

So if a small-grained function, such as the Max function below, can increase the efficiency of the program because it does not need to invoke the overhead of ordinary functions.

int Max (int a, int b)
{return
  a>b?a:b;
}

The use of more direct initialization

corresponding to the direct initialization is the replication initialization, what is direct initialization? What is replication initialization? To give a simple example,

Classtest Ct1;
Classtest Ct2 (CT1);  Direct initialization of
classtest ct3 = ct1;  Copy initialization

So what's the difference between direct initialization and replication initialization? Direct initialization is the construction of another object directly with one object, if you use Ct1 to construct CT2, replication initialization is to construct an object first, and then copy another object value to the object, such as constructing an object ct3, and then copying the value of the member variable in CT1 to Ct3, from here, It can be seen that direct initialization is more efficient, and the use of direct initialization is still an advantage, that is, for objects that cannot replicate, a stream object cannot be initialized with an assignment, and can only be initialized directly. Maybe I am not very clear, then quote the following classic bar!

Here is Primer's exact words:

" when used with class-type objects, the initialization forms differ from the direct form: direct initialization directly invokes the constructor that matches the arguments, and replication initialization always calls the copy constructor. Replication initialization first creates a temporary object using the specified constructor. Then copy the temporary object to the object being created with the copy constructor , and there's a paragraph that says, " typically direct initialization and replication initialization differ only on low-level optimizations, but for types that do not support replication, or when using a explicit constructor, they are fundamentally different:
Ifstream file1 ("filename")://ok:direct initialization
Ifstream file2 = "filename";//error:copy constructor is private
"
Note: If you have questions about direct initialization and replication initialization, you can refer to an earlier article:
The difference between direct initialization of C + + and replication initialization is deeply parsed, with detailed explanations of direct initialization and replication initialization.

Add:

Here just a little bit of advice, although said so much, but still want to say is: to avoid unnecessary optimization, to avoid immature optimization, immature optimization is the wrong source, because the compiler will do for you a lot you do not know optimization .

I hope this article will help to improve the efficiency of C + + programming.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.